Compare commits

..

320 Commits

Author SHA1 Message Date
Unknwon
c9e0b3b987 prepare for 0.5.9 2014-12-13 20:32:30 -05:00
Unknwon
28766479a7 update gopmfile 2014-12-13 20:26:04 -05:00
Unknwon
9ac940d31d update locales 2014-12-13 20:06:50 -05:00
Unknwon
b553ea45ee modes/repo: incorrect SSH clone URL for #742 2014-12-13 16:46:00 -05:00
Unknwon
ac4a10456e api: able to create repo and fix #726
- POST /user/repos
- POST /org/:org/repos
2014-12-12 20:30:32 -05:00
Unknwon
2f3a7e53cb fix #735 2014-12-12 01:29:36 -05:00
Unknwon
42c7bb7529 mirror code clean 2014-12-11 18:55:09 -05:00
无闻
35140f1cc7 Merge pull request #731 from cryptix/dev
increase minimum version for HTTPS to TLS 1.0 (POODLE, fixes #730)
2014-12-11 18:32:49 -05:00
Henry
4f4392b83e increase minimum version vor HTTPS to TLS 1.0 (POODLE, fixes #730) 2014-12-11 11:14:41 +01:00
codeskyblue
db6c0ebf76 fix git clone error when repo has upper case name 2014-12-11 15:57:32 +08:00
Unknwon
cf7ebfbdc8 mirror fix on release JS 2014-12-10 16:41:49 -05:00
Unknwon
bc8721fb6c Finish new UI for release page 2014-12-10 16:37:54 -05:00
Unknwon
01ba771783 fix #703 2014-12-10 07:00:11 -05:00
Unknwon
9ee80e3e54 fix compile error 2014-12-10 05:10:26 -05:00
无闻
b2c6a6920f Merge pull request #727 from Mageti/master
Correction for #723
2014-12-10 05:03:50 -05:00
Mageti
e321469884 remove unused code in BasicAuthDecode 2014-12-10 11:01:17 +01:00
Mageti
b7ebbb4064 Correction for #723
Correction for #723
Bug was : decode failed if the password contains ```:```
2014-12-10 10:51:51 +01:00
Unknwon
9a1fe801e5 fix #711 2014-12-09 02:18:25 -05:00
Unknwon
6f71632e3e new language 2014-12-07 19:27:49 -05:00
无闻
2844674587 Merge pull request #720 from Alukardd/dev
Allow send mail without authentication if SMTP server allow this
2014-12-07 16:05:55 -05:00
fuxiaohei
0daef29053 add label-create ui elements 2014-12-07 21:29:37 +08:00
Alukardd
21081836ba Allow send mail without authentication if SMTP server allow this 2014-12-07 16:07:48 +03:00
无闻
a2f6e1803b Merge pull request #718 from jbcrail/fix-comment-spelling
Fix spelling errors in comments.
2014-12-06 21:01:00 -05:00
Joseph Crail
39c068400e Fix spelling errors in comments. 2014-12-06 20:22:48 -05:00
Unknwon
47e7175b80 upload locales 2014-12-05 18:27:59 -05:00
Unknwon
0b785ad967 work on #672 2014-12-05 18:08:09 -05:00
Unknwon
069486d169 fix #165 2014-12-05 17:54:57 -05:00
无闻
298ebc58c1 Merge pull request #716 from ErebusBat/master
Fix Gravatar images in web view (like commit listing)
2014-12-05 16:20:34 -05:00
无闻
3fd41d138c Merge pull request #712 from Dennis-Smurf/enhancement-issuelink-in-commit
Added issue link rendering in commit messages
2014-12-05 16:15:43 -05:00
Andrew Burns
35b02997f8 Fix Gravatar images in web view (like commit listing)
Related to #700

In the original bug report it was referencing only the sytem avatar images for setup users (like in the header); however the problem also persists with things like commit history.

This commit fixes the `tool.AvatarLink` function so that it also uses the already existing `avatar.HashEmail` function.

I also refactored the `tool.AvatarLink` method some to make the control flow more apparent and adhere better to DRY (there were multiple calls to the `EncodeMd5` function that the `HashEmail` function call replaced, now there is only one.)
2014-12-05 11:02:59 -07:00
Andrew Burns
adc1ac689e HashEmail function should also remove spaces
According to the [Gravatar API](https://en.gravatar.com/site/implement/hash/) whitespace should also be removed from the email, it was not doing this previously.
2014-12-05 10:58:49 -07:00
dennis-smurf
528c075ad6 Added issue link rendering in commit messages 2014-12-05 11:02:52 +01:00
Unknwon
e577f2fff3 fix #706 and other mirror improve 2014-12-04 23:07:51 -05:00
无闻
daf96dfae1 Merge pull request #709 from TonyTsangHK/dev
Display multi-line commit message on commit diff page.
2014-12-04 22:36:54 -05:00
Tony Tsang
d4a1d9f82a Display multi-line commit message on commit diff page. 2014-12-05 10:53:37 +08:00
无闻
f7f4ea1dcf Merge pull request #708 from ErebusBat/master
Use the avatar.HashEmail function instead of hashing email directly.
2014-12-04 18:33:10 -05:00
fuxiaohei
0af3a5b603 add issue list filter ui 2014-12-04 23:22:16 +08:00
Andrew Burns
00cf3e4dab Use the avatar.HashEmail function instead of hashing email directly. Fixes #700 2014-12-03 16:19:35 -07:00
无闻
cb6be94358 Merge pull request #696 from deiwin/update/user-api-to-include-full-name
add full name to found users' list on the UI
2014-12-02 10:27:24 -05:00
Deiwin Sarjas
54ef1cee1d add full name to found users' list 2014-12-02 16:55:52 +02:00
无闻
b9999427a8 Merge pull request #694 from deiwin/update/user-api-to-include-full-name
update user api search results to include the full name
2014-12-02 07:50:05 -05:00
无闻
ddeb076890 Merge pull request #695 from CBiX/dev
fixed code view line height for firefox
2014-12-02 07:47:13 -05:00
Florian Hülsmann
d06d58e461 fixed code view line height for firefox 2014-12-02 11:31:57 +01:00
Deiwin Sarjas
48bb0fadc2 update user api search results to include the full name
Is fully backwards compatible
Fixes #677
2014-12-02 11:44:03 +02:00
无闻
cd627ba16e Merge pull request #690 from Dennis-Smurf/feature-sha1-markdown-pattern
Addded sha1 pattern in markdown for current repository
2014-12-01 11:35:18 -05:00
dennis-smurf
d0a80e432d Addded sha1 pattern in markdown for current repository 2014-12-01 09:30:35 +01:00
Unknwon
9558999698 mirror fix 2014-11-30 18:35:05 -05:00
Unknwon
5338808600 fix #676 2014-11-30 18:29:16 -05:00
Unknwon
82da024a4d fix #687 2014-11-30 10:55:26 -05:00
Unknwon
5c866fc737 Merge branch 'dev' of github.com:gogits/gogs into dev 2014-11-30 02:26:33 -05:00
Unknwon
d75013a0e8 fix #580 2014-11-30 02:26:29 -05:00
无闻
1591a37ad5 Merge pull request #686 from willglynn/cert_stub_exit_code
Cert command stub should return a non-zero exit code
2014-11-30 01:38:35 -05:00
Will Glynn
3e528f34af Cert command stub should return a non-zero exit code 2014-11-29 21:52:59 -06:00
Unknwon
b6437b5a4c safe check 2014-11-28 21:54:49 -05:00
Unknwon
db4951bc61 mirror fix on oauth2 2014-11-28 21:38:35 -05:00
Unknwon
d6132aaa88 fix oauth2 2014-11-28 21:20:13 -05:00
无闻
9adc8e9d3f Merge pull request #683 from jcracknell/admin-repos-cols
Fix swapped issue/star columns in /admin/repos
2014-11-28 15:28:08 -05:00
无闻
be54b4bf26 Merge pull request #682 from jcracknell/user-links
Fix user links
2014-11-28 15:22:56 -05:00
James Cracknell
64d90a761b Fix swapped issue/star columns in /admin/repos 2014-11-28 12:40:02 -07:00
James Cracknell
4d123d0a93 Fix user links 2014-11-28 11:53:00 -07:00
Unknwon
5dd8308686 Merge branch 'dev' of github.com:gogits/gogs into dev 2014-11-27 21:36:20 -05:00
Unknwon
9c2120eb34 fix #675 2014-11-27 21:36:12 -05:00
无闻
4f8d888e66 Merge pull request #674 from jcracknell/pfx-export
Added comments documenting how to export SSL keys from .pfx
2014-11-27 16:56:55 -05:00
James Cracknell
ca09a0b516 Added comments documenting how to export SSL keys from .pfx 2014-11-27 14:42:06 -07:00
Unknwon
a43164877c fix #664 2014-11-25 23:05:43 -05:00
Unknwon
515641d033 fix #659 2014-11-25 22:48:04 -05:00
Unknwon
54d25c13d7 Fix #543 2014-11-24 18:47:59 -05:00
fuxiaohei
23d53561d1 update milestone page design 2014-11-24 23:13:42 +08:00
Unknwon
0cce61de3a Merge branch 'dev' of github.com:gogits/gogs into dev 2014-11-24 09:54:22 -05:00
Unknwon
5b96e3fcc7 fix #660 2014-11-24 09:54:08 -05:00
fuxiaohei
79dae254cf add milestone page design 2014-11-24 22:33:04 +08:00
无闻
5b32cdd960 Merge pull request #657 from chai2010/dev
Fix #656
2014-11-23 22:15:25 -05:00
chai2010
f9ad8d6903 Fix #656 2014-11-24 10:58:39 +08:00
Unknwon
1b66600bd0 Fix #652 2014-11-23 02:33:47 -05:00
Unknwon
dc53270da9 Fix 653 2014-11-22 10:22:53 -05:00
Unknwon
8ea7ba3afa fix #644 2014-11-21 13:10:00 -05:00
Unknwon
ef275ebf62 more on change avatar 2014-11-21 12:51:36 -05:00
Unknwon
22ab4fa1b0 fix #139 2014-11-21 11:08:24 -05:00
Unknwon
55dfe2c978 custom avatar upload 2014-11-21 10:58:08 -05:00
Unknwon
3c3f7c2a56 Fix #643 2014-11-20 18:03:42 -05:00
Unknwon
0c92da7e9c fix invalid user links in admin panels 2014-11-19 21:19:37 -05:00
无闻
8fac41768c Merge pull request #639 from pkgr/pkgr
Make postinstall work for CentOS/RHEL packages as well.
2014-11-19 17:27:53 -05:00
Cyril Rohr
c418b83678 Make postinstall work for CentOS/RHEL packages as well. 2014-11-19 21:43:12 +00:00
无闻
b53f6357fc Merge pull request #638 from pkgr/pkgr
Fix packages generated on packager.io
2014-11-19 15:51:00 -05:00
Cyril Rohr
11ca3dedfb Fix .pkgr.yml 2014-11-19 19:18:44 +00:00
Cyril Rohr
f77680520f Link custom app.ini to /etc/gogs/ 2014-11-19 19:18:44 +00:00
Unknwon
2d8c414f8c Fix #635 2014-11-18 19:05:33 -05:00
Unknwon
ce8d4cc80b #634 2014-11-18 15:13:08 -05:00
Unknwon
37d8d3afe9 more APIs on #12 2014-11-18 11:07:16 -05:00
Unknwon
db0026c507 test drone 2014-11-17 17:47:13 -05:00
Unknwon
51f6148851 test drone 2014-11-17 17:36:51 -05:00
Unknwon
c0549a169a test drone 2014-11-17 17:19:17 -05:00
Unknwon
1e47e2df85 quick fix 2014-11-17 16:29:23 -05:00
Unknwon
5d9ef2bb12 lock gopmfile 2014-11-17 16:25:39 -05:00
无闻
d21d7171b0 Merge pull request #587 from eryx/dev
Fix #266
2014-11-17 16:17:52 -05:00
Unknwon
32dcaefafa fox #620 2014-11-17 14:53:41 -05:00
fuxiaohei
d1a60e3643 add pull-request and labels page design 2014-11-17 23:07:34 +08:00
Unknwon
a0f9197b45 GetFile api 2014-11-16 21:32:26 -05:00
Unknwon
340a4595dd support duoshuo mirror of gravatar 2014-11-16 20:27:04 -05:00
无闻
4c770b87c5 Merge pull request #628 from KylePDavis/use_url_prefix_for_fcgi
Use AppSubUrl as prefix for routes in FCGI mode
2014-11-16 06:59:01 -05:00
Kyle P Davis
47f37e55e9 use AppSubUrl as prefix for routes in FCGI mode 2014-11-16 00:06:09 -05:00
Unknwon
52d66ba6c8 #12, use go-gogs-client 2014-11-14 17:11:30 -05:00
无闻
437dd5272f Merge pull request #626 from skibum55/dev
Add function argument missing
2014-11-14 15:12:30 -05:00
Sean Keery
6bdb8ec4b9 Add function argument missing
Not enough arguments in call to c.AddFunc when I copied and pasted
2014-11-14 10:58:37 -07:00
Unknwon
9dc3c93a6a #12, add/edit hook 2014-11-13 12:57:00 -05:00
Unknwon
9e22840483 fix #622 2014-11-13 05:27:01 -05:00
Unknwon
8eb5120fbd #12, API: list user repos, list repo hooks 2014-11-13 02:32:18 -05:00
Unknwon
8c9338a537 add personal access token panel #12 2014-11-12 06:48:50 -05:00
Unknwon
21b9d5fa1f Fix #618 2014-11-11 16:56:57 -05:00
Unknwon
4f360d8f08 Fix #617 and update Germeny locale 2014-11-11 15:30:11 -05:00
Unknwon
dfa4b38081 work on #616 2014-11-10 06:26:07 -05:00
Unknwon
e0de6cb5ad work on #616 and update locales 2014-11-10 05:30:07 -05:00
Unknwon
ff8578082e update locale files 2014-11-07 17:07:36 -05:00
Unknwon
35977cd34c continue work on #255 2014-11-07 16:44:25 -05:00
Unknwon
abc57b6e43 work on #609 2014-11-07 14:46:13 -05:00
无闻
a01b4baca2 Merge pull request #612 from justinclift/enable_el6_builds
Enable package building on EL6
2014-11-06 23:13:27 -05:00
Justin Clift
fc5985f425 Enable package building on EL6 2014-11-07 03:44:34 +00:00
Unknwon
23eec25274 Fix #605, fix #255, fix #101 2014-11-06 22:06:41 -05:00
Unknwon
4e7eb5be9d Work on #5 fork and fix #608 2014-11-05 23:30:04 -05:00
Unknwon
b375192352 fix tpl error 2014-11-04 12:07:43 -05:00
Unknwon
0c5ba4573a fix session API broken and SQL pretection 2014-11-04 11:37:15 -05:00
fuxiaohei
69a98236bd Merge remote-tracking branch 'origin/dev' into dev 2014-11-04 21:58:47 +08:00
fuxiaohei
b866dc92d7 finish issue list ui draft 2014-11-04 21:58:28 +08:00
Unknwon
78f4f59380 ui fix 2014-11-04 07:59:36 -05:00
Unknwon
743b55b9b0 fix gobuild 2014-11-04 07:56:07 -05:00
无闻
0d5d555521 Merge pull request #604 from omeid/dev
Fix x padding for #repo-content for width < 1170px
2014-11-04 07:55:40 -05:00
omeid
80c6cc6321 Fix x padding for #repo-content for width < 1170px
This pull request fixes the issue when resizing the window smaller than 1170 px, right now there is no padding/margin on sides and it looks broken.
2014-11-04 14:14:23 +11:00
无闻
6588ce52fe Merge pull request #602 from andyleap/fcgi
Add basic FCGI support
2014-11-03 21:04:00 -05:00
Andy Leap
5094e9501c Add basic FCGI support 2014-11-03 20:49:56 -05:00
Unknwon
e9875edcad Update gopmfile 2014-11-02 10:57:29 -05:00
无闻
7e2ea7639e Merge pull request #596 from isotas38/dev
Fix #595
2014-11-02 10:57:04 -05:00
无闻
5b290013a9 Merge pull request #597 from luto/fix-mysql-engine
force the use of InnoDB as db engine for all tables
2014-11-02 10:52:36 -05:00
isotas38
2c28ed8c05 Fix #595 2014-11-02 14:18:37 +09:00
Unknwon
283c81316c locale fix 2014-11-01 21:43:53 -04:00
无闻
beb8a77fc5 Merge pull request #593 from mcheng89/dev
check for smtp AUTH extension
2014-10-31 22:18:15 -04:00
mcheng89
b9881d1e7b check for smtp AUTH extension 2014-10-31 21:52:03 -04:00
无闻
87be4623cf Merge pull request #591 from semlanik/semlanik/589
Fix Issue 589
2014-10-30 22:31:47 -04:00
semlanik
377530ec21 Fix Issue 589 2014-10-30 22:13:52 +03:00
luto
bff1e157d5 force the use of InnoDB as db engine for all tables, fixes #59 2014-10-28 16:40:09 +01:00
Eryx
e84e0ab904 Fix #266 2014-10-28 10:36:47 +08:00
Unknwon
c7f56d7483 Fix #584 2014-10-27 11:18:00 -04:00
Unknwon
83283bca4c Safe work 2014-10-25 07:50:19 -04:00
Unknwon
f1d8746264 Fix fork repo and macaron API broken 2014-10-24 18:43:17 -04:00
无闻
baae94b9cf Merge pull request #581 from compressed/slack_upd
update slack hook to use new format
2014-10-24 17:54:40 -04:00
Christopher Brickley
3794111460 update slack hook to use new format 2014-10-24 08:56:12 -04:00
无闻
d7ea49b9f5 Merge pull request #579 from TonyTsangHK/dev
Fix misuse of issue index for issue id.
2014-10-22 08:44:33 -04:00
Tony Tsang
d87a9cb362 Avoid setting missing label/milestone/assignee ids
Label, milestone, assignee ids are not includes in post request, possible
js or form building bug.
2014-10-22 14:52:49 +08:00
Tony Tsang
93b9a2acc0 Fix misuse of issue index for issue id.
- UpdateAssignee
- UpdateIssueMilestone
2014-10-21 16:27:01 +08:00
无闻
d4a608f64c Merge pull request #577 from eryx/master
Fix #575
2014-10-20 04:44:06 -04:00
Eryx
8209bf74f8 Fix #575 2014-10-20 11:44:11 +08:00
Unknwon
d88ebd9a4b Fix #554 2014-10-19 02:22:38 -04:00
Unknwon
ec8ec58b17 Update gopmfile 2014-10-19 02:13:39 -04:00
Unknwon
a342d58d7e Able to fork repo to individuals 2014-10-19 01:35:24 -04:00
无闻
d7d167ac63 Merge pull request #560 from evolvedlight/master
First cut of fork repo
2014-10-18 23:59:36 -04:00
Unknwon
146c8efee3 Fix API broken 2014-10-18 23:42:43 -04:00
Unknwon
3abc41ccca Fix API broken 2014-10-18 23:26:55 -04:00
fuxiaohei
9e3a1bc11a add nav bar in issue list 2014-10-18 22:52:34 +08:00
fuxiaohei
cb2da7bf2c some ui details fix 2014-10-18 22:15:05 +08:00
Unknwon
1aa12c7452 Fix #572 2014-10-15 16:28:38 -04:00
Unknwon
fa241efa6d Use binding middleware 2014-10-15 11:19:20 -04:00
Unknwon
ecf3eb4307 Fix #570 2014-10-15 07:55:20 -04:00
Unknwon
9ae92459a5 Fix repo css and rel path img in md 2014-10-14 23:44:34 -04:00
无闻
5b3f1efd9f Merge pull request #567 from TonyTsangHK/dev
Fixes #562 & updated zh-HK locale
2014-10-14 14:25:07 -04:00
Tony Tsang
e0f945959e Replace white spaces with tab for format consistency 2014-10-14 17:31:35 +08:00
Tony Tsang
885833892f Updated locale zh-HK 2014-10-14 17:04:45 +08:00
Tony Tsang
91127d9016 Fixes #562 2014-10-14 16:46:25 +08:00
无闻
1a38f0e0d9 Merge pull request #561 from eryx/master
Fix pagination() to get the right total number of pages
2014-10-13 22:47:21 -04:00
Eryx
fb8beaf19a Fix pagination() to get the right total number of pages 2014-10-14 09:54:45 +08:00
Unknwon
4a8a70dde0 Fix gzip 2014-10-13 18:04:07 -04:00
evolvedlight
8d2a6fc484 Merge remote-tracking branch 'upstream/dev'
Conflicts:
	models/repo.go
2014-10-13 20:30:31 +01:00
evolvedlight
29ac3980ff More changes 2014-10-13 20:23:30 +01:00
无闻
89bd994c83 Merge pull request #558 from jacksonpan/master
add +x autoboot.sh
2014-10-13 04:00:46 -04:00
jacksonpan
3e4db7299d add +x autoboot.sh
fix
2014-10-13 15:52:29 +08:00
无闻
80dd548b39 Merge pull request #556 from jacksonpan/master
update the supervisor script and conf
2014-10-13 03:24:59 -04:00
Unknwon
451f328a4c Fix #457 and fix #557 2014-10-13 03:07:47 -04:00
jacksonpan
9e4c2afad7 add auto start script
how to use:
vim /etc/rc.local, then add
cd /home/git/gogs; scripts/autoboot.sh
(goes path you can replace your path)
2014-10-13 14:32:08 +08:00
jacksonpan
2dd4ab65c7 update the supervisor script and conf
now, we auto add the start.sh -> /usr/bin/gogs_start, then supervisor
needn’t the full path, detail please see the commit.
and how to use:
entry gogs root path, then input
scripts/gogs_supervisord.sh restart (add sudo if need)
2014-10-13 14:23:55 +08:00
Unknwon
b93ae452fe Fix #555 2014-10-12 23:22:16 -04:00
Unknwon
c04aea8d9d Fix #540 2014-10-12 02:39:00 -04:00
Unknwon
0d7b9065c6 Fix #540 2014-10-12 02:06:46 -04:00
Unknwon
452ccff81c Mirror fix on add ssh key 2014-10-11 21:04:42 -04:00
Unknwon
fb839ca0fb More debug info 2014-10-11 20:34:48 -04:00
Unknwon
b7b7863364 Work on ssh key issue 2014-10-11 20:06:35 -04:00
Unknwon
3005c4f6db Fix diff css style, hooks \r char 2014-10-11 18:20:07 -04:00
Unknwon
963354c5d7 Add raw, history file button, and other mirror fixes 2014-10-11 18:02:48 -04:00
无闻
17c1bc7383 Merge pull request #549 from m0sth8/fix-timesince
Add omitted lang
2014-10-11 17:36:33 -04:00
Vyacheslav Bakhmutov
5103f16f78 add omitted lang 2014-10-11 13:43:21 +07:00
lunnyxiao
e848b17fab bug fixed for #540 2014-10-11 13:24:36 +08:00
Unknwon
3600498c8f Add push tag action 2014-10-10 21:58:13 -04:00
Unknwon
b2632dec09 Page: Compare 2 commits 2014-10-10 21:40:51 -04:00
Unknwon
54930c001d Prepare for v0.5.5 2014-10-10 16:22:49 -04:00
Unknwon
41dbb4c148 Hide org member in collar page 2014-10-10 06:15:27 -04:00
Unknwon
85c9f7c5f3 Issue: Show error prompt when add repository to team and it does not exist #533 2014-10-10 05:06:12 -04:00
无闻
c31606daf9 Merge pull request #544 from TonyTsangHK/dev
Add locale zh-HK (Traditional Chinese)
2014-10-10 03:41:39 -04:00
Unknwon
d78abd3561 Mirror fix on admin/orgs paging 2014-10-10 03:28:21 -04:00
Tony Tsang
8c71ab5094 Add locale zh-HK (Traditional Chinese) 2014-10-10 14:17:47 +08:00
evolvedlight
211dc74816 Got the fork count working 2014-10-10 00:21:36 +01:00
Unknwon
79262173a6 Webhook delivery locking & Hide repo for org members if they don't have access 2014-10-09 19:01:22 -04:00
Unknwon
7b03b1df0e Allow custom locale 2014-10-09 18:35:09 -04:00
Unknwon
39931f8e00 Allow mail with self-signed certificates 2014-10-09 18:08:07 -04:00
Unknwon
1aa76bd279 Fix #532, add system notice 2014-10-08 18:29:18 -04:00
无闻
54c9844d66 Merge pull request #539 from Xefir/dev
Fix malformed address
2014-10-08 12:30:59 -04:00
Michel Roux
6705559ce0 Fix malformed address 2014-10-08 18:29:02 +02:00
无闻
8e8dfaf227 Merge pull request #538 from m0sth8/fix-timesince
Add omitted lang
2014-10-08 12:17:07 -04:00
Vyacheslav Bakhmutov
ce7422473a add omitted lang 2014-10-08 12:27:05 +04:00
Unknwon
fc18741cc7 Fix #524 2014-10-07 07:02:53 -04:00
Unknwon
fef09c2de6 Fix badges 2014-10-06 19:35:41 -04:00
Unknwon
1e1f9e7166 Update with macaron 2014-10-06 19:12:52 -04:00
Unknwon
64c68220d2 Fix #264 2014-10-06 17:50:00 -04:00
Unknwon
91e5c24a31 Fix #522 2014-10-06 10:36:32 -04:00
无闻
810ff480b8 Merge pull request #531 from basilfx/dev
Updated Dutch translations
2014-10-05 18:09:34 -04:00
无闻
de65c30770 Merge pull request #530 from chk1/master
Add and translate missing German strings for user page
2014-10-05 18:09:19 -04:00
Bas Stottelaar
bbf5bcdf99 Updated Dutch translations
Fixed some spelling mistakes.

* Number up to 20 are written as words instead of digits.
* Nouns are written without spaces.
* Fixed mixups between informal/formal language.
* Removal of extra spaces in sentences.
2014-10-05 21:03:07 +02:00
unknown
02b53aff0f Add and translate missing German strings for user page 2014-10-05 17:26:32 +02:00
无闻
b694cc88e4 Merge pull request #525 from linquize/tag-sort-reverse
sort tags in descending order by version number
2014-10-05 00:08:24 -04:00
Linquize
67c44b7d27 If git >= 2.0, sort tags in descending order by version number 2014-10-05 11:59:54 +08:00
Unknwon
263d409326 Basic xss prevention 2014-10-04 17:15:22 -04:00
无闻
6a79b76531 Merge pull request #529 from chk1/master
Translate missing entries & add German translation to homepage
2014-10-04 15:56:43 -04:00
unknown
da0fbbadc2 Fix memecache/memcache typo 2014-10-04 21:44:17 +02:00
unknown
1031271224 Add German translation to home page 2014-10-04 21:33:32 +02:00
unknown
8c70bcee99 Translate missing strings and some improvements 2014-10-04 21:32:55 +02:00
无闻
07c8925805 Merge pull request #527 from michaelboke/master
Dutch translations
2014-10-04 15:26:29 -04:00
evolvedlight
c9e5e38fcf Initial cut of git fork 2014-10-04 18:19:14 +01:00
Michael Boke
29a7d1ce61 updated some strange translated message so they will match their context on the website 2014-10-04 10:32:31 +02:00
Michael Boke
500b8a2a0f updated translations with the latest master version 2014-10-04 10:22:03 +02:00
Michael Boke
ba1270df2d Merge remote-tracking branch 'upstream/master'
Conflicts:
	conf/app.ini
2014-10-03 22:51:07 +02:00
Michael Boke
ba0feadc34 Added dutch translations 2014-10-03 22:43:42 +02:00
Unknwon
405ee14711 Fix SMTP auth logic 2014-10-03 13:12:54 -04:00
无闻
1126522a99 Merge pull request #521 from DerDackel/dev
Added some missing strings under the [action] category to german localization
2014-10-01 10:42:46 -04:00
Sebastian Jackel
1601b27ad3 Added some missing strings under the [action] category to german localization 2014-10-01 14:47:34 +02:00
Unknwon
f03b6be8f9 Work on #516 2014-10-01 07:40:48 -04:00
无闻
3ffa17c49a Merge pull request #519 from silverkorn/dev
Debian's "init.d" - Tentative to fix user's environment variables loading
2014-09-30 21:39:12 -04:00
Danny B
98c719c342 Debian's Init.d workaround for loading user's environment variables with start-stop-daemon command.
Implies 2 running child, so the `stop` command usually needs a `KILL` instead of a `TERM` to close properly so I reduced it to 1 second to get quickly with the `KILL` signal.
2014-09-30 21:30:16 -04:00
Unknwon
2a031c1365 Fix #515 2014-09-30 04:39:53 -04:00
无闻
198567eccb Merge pull request #517 from silverkorn/dev
Added "init.d" service script for Debian-based Linux.
2014-09-29 22:00:31 -04:00
Danny B
f7de6d2b86 Added "init.d" service script for Debian-based Linux.
Compatible with "/etc/default/gogs" configuration file for adding or overwriting values.
Based on "/etc/init.d/skeleton" and tested on Raspbian 2014-06-20 (Debian 7.5 [Wheezy] ARM)

[TO FIX] For some reason, it's necessary to remove the value of RUN_USER from the app.ini configuration file because the following error occurs:
2014/09/28 18:12:53 [setting.go:182 NewConfigContext()] [E] Expect user(git) but current user is:
2014-09-29 21:52:45 -04:00
Unknwon
86eac0842b Fix feeds display issue 2014-09-29 18:58:04 -04:00
Unknwon
a046a31d2b UI: Confirmation box 2014-09-29 18:52:28 -04:00
Unknwon
cd084dacf1 Merge branch 'dev' of github.com:gogits/gogs into dev 2014-09-29 05:38:52 -04:00
Unknwon
ac2055e33c Fix #514 2014-09-29 05:38:46 -04:00
无闻
96f4c9045a Merge pull request #513 from lbeltrame/add-centos-init-script
Add CentOS init script
2014-09-29 05:06:32 -04:00
Luca Beltrame
d95e7065d9 s/USER/GOGS_USER/g 2014-09-29 10:43:51 +02:00
Luca Beltrame
5e48c89c5e Add a CentOS init script 2014-09-29 10:41:29 +02:00
Unknwon
3cfa4a581c Bug: 500 when transfer repository to collaborator 2014-09-29 04:04:48 -04:00
无闻
ce6931a046 Merge pull request #512 from fundon/feature_support_dsa_key
support dsa key format
2014-09-29 00:57:49 -04:00
fundon
eb1e6f8e3e support dsa key format 2014-09-29 12:37:34 +08:00
无闻
263fc76b87 Merge pull request #507 from codeskyblue/dev
update .gobuild.yml inorder to support sqlite for amd64 arch
2014-09-28 18:07:00 -04:00
无闻
c1d047d16e Merge pull request #508 from chadoe/homelink
Fix user HomeLink when using a suburl
2014-09-28 18:05:58 -04:00
无闻
38f71af363 Merge pull request #509 from chadoe/avatarlink
Fix AvatarLink when using a suburl
2014-09-28 18:05:35 -04:00
无闻
ecdbeea3f5 Merge pull request #510 from evolvedlight/master
Fix minor typo
2014-09-28 18:05:16 -04:00
evolvedlight
b7b0ee7df9 Fix minor typo 2014-09-28 22:01:05 +01:00
Martin van Beurden
bb05ef907b Fix AvatarLink when using a suburl and gravatar is disabled or avatars are cached 2014-09-28 14:27:13 +02:00
Martin van Beurden
204952439a Fix user HomeLink when using a suburl 2014-09-28 13:55:58 +02:00
codeskyblue
e2e362f2dc update .gobuild.yml remove start.bat 2014-09-28 16:38:59 +08:00
codeskyblue
26a0888bee Merge branch 'master' into dev 2014-09-28 16:38:19 +08:00
codeskyblue
9ce0bd043c update .gobulid.yml 2014-09-28 16:37:58 +08:00
Unknwon
5e747bc877 Clean old dockerfiles 2014-09-28 03:31:50 -04:00
Unknwon
11af8658cf Simple fix 2014-09-28 01:54:25 -04:00
Unknwon
e6e6aaeacb Delete verbose 2014-09-28 01:45:03 -04:00
Unknwon
e3b78c47e2 Merge branch 'master' of github.com:gogits/gogs
Conflicts:
	public/ng/less/gogs/repository.less
	templates/repo/home.tmpl
2014-09-28 01:44:09 -04:00
Unknwon
49193bebd2 UI: Confirmation box 2014-09-28 01:38:25 -04:00
shengxiang
3598c1435e Update .gobuild.yml 2014-09-28 13:02:17 +08:00
shengxiang
ab59165d2f Create start.bat 2014-09-28 12:45:21 +08:00
shengxiang
092b59a297 Update .gobuild.yml 2014-09-28 12:44:37 +08:00
fuxiaohei
7d48f811f1 add issue router for new issue page ui preview 2014-09-27 19:03:07 +08:00
fuxiaohei
e3a27aeb25 template and ui fix 2014-09-27 17:31:44 +08:00
fuxiaohei
dccc50e9d4 template and ui fix 2014-09-27 17:08:57 +08:00
Unknwon
ad2ab6d214 Add modal to change username
Signed-off-by: Unknwon <joe2010xtmf@163.com>
2014-09-26 22:33:30 -04:00
Unknwon
b8368f98ff Add directory level commit message 2014-09-26 08:55:13 -04:00
无闻
3164354255 Merge pull request #503 from fundon/fix_lastcommit_user_email_link
Fix last commit user email link
2014-09-26 08:35:41 -04:00
fundon
54724c33ec no neet for md5 email 2014-09-26 13:46:51 +08:00
lunnyxiao
d1911658e1 remove debug info 2014-09-26 12:17:46 +08:00
无闻
c8b50975bc Merge pull request #501 from fundon/fix_suburl_undefined
bugfix, suburl defaults to empty string when suburl is undefined
2014-09-25 22:51:31 -04:00
Unknwon
10673417dc Mirror fix on transfer repo 2014-09-25 22:42:31 -04:00
Unknwon
ad52b2d791 Mirror fix on transfer repo 2014-09-25 22:36:07 -04:00
fundon
09c3c4e70c bugfix, suburl defaults to empty string when suburl is undefined 2014-09-26 10:29:57 +08:00
Unknwon
977779cdcf Mirror template bug fix 2014-09-25 20:55:14 -04:00
Unknwon
71e4689d11 Page: User profile 2014-09-25 19:33:39 -04:00
Unknwon
f69761563b Fix bug on transfer repo 2014-09-25 16:36:19 -04:00
无闻
57d48fb6a2 Merge pull request #498 from cedricziel/translations
[TASK] Refine german string for authentification source
2014-09-25 15:11:33 -04:00
fuxiaohei
7c30ae7002 is utils improvement 2014-09-25 21:52:58 +08:00
lunnyxiao
089d934547 add action repousername for transfer 2014-09-25 16:43:14 +08:00
Cedric Ziel
b0f8b1147c [TASK] Refine german string for authentification source 2014-09-25 09:16:43 +02:00
Unknwon
25268577a5 Fix download archive issue 2014-09-24 17:43:33 -04:00
lunnyxiao
612fdb98df bug fixed for download 404 from repo's home page 2014-09-24 21:05:09 +08:00
Unknwon
bd55b78775 Page: Commits and fix #249 2014-09-23 23:18:14 -04:00
Unknwon
5bbeeb0f1b Page: Commits and fix #249 2014-09-23 15:30:04 -04:00
Unknwon
93ee0838eb Page: Repository home page 2014-09-23 13:51:10 -04:00
Unknwon
a1109e6fbc Page: Repository home page 2014-09-23 13:47:54 -04:00
Unknwon
ebb05475ed Fix #495 and cannot view repository by tag 2014-09-23 13:06:25 -04:00
fuxiaohei
a11ed51bbb resize star font size 2014-09-24 00:01:48 +08:00
fuxiaohei
e0493259a6 resize star font size 2014-09-23 23:59:44 +08:00
fuxiaohei
b3f0d25ce5 pull request ui review, change sidebar 2014-09-23 23:48:28 +08:00
fuxiaohei
d750d53422 Merge remote-tracking branch 'origin/dev' into dev 2014-09-23 23:36:25 +08:00
fuxiaohei
8d5a4cc9eb pull request ui review, change sidebar 2014-09-23 23:36:09 +08:00
Unknwon
1476a1a729 Merge branch 'dev' of github.com:gogits/gogs into dev 2014-09-23 10:29:52 -04:00
Unknwon
e3eea745f4 UI fix 2014-09-23 10:29:48 -04:00
fuxiaohei
60c65415dd pull request page ui review, upgrade octicon icons 2014-09-23 22:28:03 +08:00
无闻
b92cac7038 Merge pull request #496 from domnikl/patch-1
fixed typos and English phrases in German translation
2014-09-23 09:56:26 -04:00
fuxiaohei
904f799c1a improve ui details 2014-09-23 21:43:45 +08:00
Dominik Liebler
de2a4f8f83 fixed typos and English phrases
fixed Typo "Hilef"
2014-09-23 13:15:52 +02:00
Unknwon
135d3733b3 Feature: Integrate crypto/tls/generate_cert.go command 2014-09-22 17:30:58 -04:00
Unknwon
3f707b3f32 Add basic submodule support 2014-09-22 17:01:19 -04:00
Unknwon
063aacd436 UI: Use tooltip not alert when copy clone URL 2014-09-22 14:47:47 -04:00
Unknwon
196efecaaa Fix #491 2014-09-22 11:04:46 -04:00
无闻
01e69af2c8 Merge pull request #494 from compressed/gt_2_commits
Increase max commits in payload to 5
2014-09-22 10:29:23 -04:00
Christopher Brickley
25c8d01676 increase max commits in payload to 5 2014-09-22 08:25:39 -04:00
lunnyxiao
7df60af60e submodule support and closed #478 2014-09-22 14:23:36 +08:00
lunnyxiao
79ec08141a Merge branch 'dev' of github.com:gogits/gogs into dev 2014-09-22 10:45:20 +08:00
lunnyxiao
150eef93b2 add submodule basic support & buf fixed #478 2014-09-22 10:43:16 +08:00
Unknwon
1273b3d3a9 Support custom robots.txt 2014-09-21 19:39:10 -04:00
Unknwon
b72d7c201a Mirror bug fix 2014-09-21 12:22:50 -04:00
Unknwon
4a01bb8fa4 Mirror bug fix 2014-09-21 12:19:50 -04:00
无闻
d325b23dbb Merge pull request #492 from chadoe/suburlcookie
Set cookiepath to AppSubUrl
2014-09-21 12:11:40 -04:00
Martin van Beurden
976f1486e0 Set cookiepath to AppSubUrl 2014-09-21 14:25:22 +02:00
无闻
cb0ea46d1e Merge pull request #490 from fanningert/patch-1
Update locale_de-DE.ini
2014-09-20 12:23:36 -04:00
fanningert
052ab30409 Update locale_de-DE.ini 2014-09-20 11:01:05 +02:00
Unknwon
7ba9257a7f Add suburl support 2014-09-19 20:11:34 -04:00
无闻
6a7bd097fe Merge pull request #463 from chadoe/urlroot
Allow Gogs to run from a suburl behind a reverse proxy.
2014-09-19 19:02:40 -04:00
Martin van Beurden
0055cbd365 Allow Gogs to run from a suburl behind a reverse proxy. e.g. http://mydomain.com/gogs/
Conflicts:
	modules/setting/setting.go

Conflicts:
	templates/repo/release/list.tmpl
	templates/user/dashboard/dashboard.tmpl

Conflicts:
	routers/repo/setting.go
2014-09-18 20:50:48 +02:00
256 changed files with 13845 additions and 6074 deletions

View File

@@ -1,5 +1,8 @@
[run]
init_cmds = [["./gogs", "web"]]
init_cmds = [
["grep", "-rn", "FIXME", "."],
["./gogs", "web"]
]
watch_all = true
watch_dirs = [
"$WORKDIR/conf/locale",
@@ -11,7 +14,7 @@ watch_dirs = [
watch_exts = [".go", ".ini"]
build_delay = 1500
cmds = [
["go", "install"],
["go", "build"],
["go", "install", "-tags", "sqlite cert"],# redis memcache
["go", "build", "-tags", "sqlite cert"],
["./gogs", "web"]
]

View File

@@ -1,12 +0,0 @@
{
"paths": ["."],
"depth": 2,
"exclude": [],
"include": ["\\.go$", "\\.ini$"],
"command": [
"bash", "-c", "go build && ./gogs web"
],
"env": {
"POWERED_BY": "github.com/shxsun/fswatch"
}
}

14
.gitignore vendored
View File

@@ -8,28 +8,19 @@ data/
*.iml
public/img/avatar/
files/
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.exe~
/gogs
@@ -39,3 +30,8 @@ __pycache__
output*
config.codekit
.brackets.json
docker/fig.yml
docker/docker/Dockerfile
docker/docker/init_gogs.sh
gogs.sublime-project
gogs.sublime-workspace

View File

@@ -10,3 +10,13 @@ filesets:
- README_ZH.md
excludes:
- \.git
depth: 5
settings:
build: |
if test "$GOOS" = "windows" -a "$GOARCH" = "386"
then
go install -v
else
go get -v -tags "sqlite redis memcache cert" github.com/gogits/gogs
go install -v -tags "sqlite redis memcache cert"
fi

View File

@@ -7,23 +7,26 @@ github.com/beego/redigo = commit:856744a0d5
github.com/Unknwon/cae = commit:2e70a1351b
github.com/Unknwon/com = commit:2cbcbc6916
github.com/Unknwon/goconfig = commit:0f8d8dc1c0
github.com/Unknwon/i18n = commit:47baeff8d0
github.com/Unknwon/macaron = commit:f22f45d79a
github.com/Unknwon/i18n = commit:aec5f77857
github.com/Unknwon/macaron = commit:3d307c1232
github.com/codegangsta/cli = commit:7381bc4e62
github.com/go-sql-driver/mysql = commit:8111ee3ec3
github.com/go-xorm/core = commit:750aae0fa5
github.com/go-xorm/xorm = commit:2d8b3135b1
github.com/gogits/gfm = commit:40f747a9c0
github.com/go-xorm/core = commit:3e0fa232ab
github.com/go-xorm/xorm = commit:58d33844ce
github.com/gogits/go-gogs-client = commit:92e76d616a
github.com/gogits/oauth2 = commit:99cbec870a
github.com/lib/pq = commit:b021d0ef20
github.com/macaron-contrib/cache = commit:204d8e5137
github.com/macaron-contrib/captcha = commit:8f3f1ac0e3
github.com/macaron-contrib/csrf = commit:cd84c01723
github.com/macaron-contrib/i18n = commit:489cc194b5
github.com/macaron-contrib/session = commit:80a88a1bba
github.com/macaron-contrib/binding = commit:0e23661e7d
github.com/macaron-contrib/cache = commit:0bb9e6c9ef
github.com/macaron-contrib/captcha = commit:3567dc48b8
github.com/macaron-contrib/csrf = commit:422b79675c
github.com/macaron-contrib/i18n = commit:184bb832f2
github.com/macaron-contrib/oauth2 = commit:8f394c3629
github.com/macaron-contrib/session = commit:f00d48fd4f
github.com/macaron-contrib/toolbox = commit:57127bcc89
github.com/mattn/go-sqlite3 = commit:a80c27ba33
github.com/nfnt/resize = commit:581d15cb53
github.com/russross/blackfriday = commit:05b8cefd6a
github.com/saintfish/chardet = commit:3af4cd4741
[res]

View File

@@ -2,9 +2,7 @@ targets:
ubuntu-14.04:
ubuntu-12.04:
debian-7:
build_dependencies:
- mercurial
- bzr
centos-6:
dependencies:
- git
before:

View File

@@ -2,5 +2,4 @@ language: go
go:
- 1.2
- 1.3
- tip
- 1.3

40
LICENSE
View File

@@ -1,27 +1,19 @@
Copyright (c) 2014
All rights reserved.
Copyright (c) 2014 All Gogs Contributors
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,11 +1,11 @@
Gogs - Go Git Service [![wercker status](https://app.wercker.com/status/ad0bdb0bc450ac6f09bc56b9640a50aa/s/ "wercker status")](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [![Build Status](https://drone.io/github.com/gogits/gogs/status.png)](https://drone.io/github.com/gogits/gogs/latest)
Gogs - Go Git Service [![wercker status](https://app.wercker.com/status/ad0bdb0bc450ac6f09bc56b9640a50aa/s/ "wercker status")](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs)
=====================
Gogs(Go Git Service) is a painless self-hosted Git Service written in Go.
![Demo](https://gowalker.org/public/gogs_demo.gif)
##### Current version: 0.5.2 Beta
##### Current version: 0.5.8 Beta
### NOTICES
@@ -26,25 +26,29 @@ The goal of this project is to make the easiest, fastest and most painless way t
- See [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team.
- Try it before anything? Do it [online](https://try.gogs.io/Unknown/gogs) or go down to **Installation -> Install from binary** section!
- Having troubles? Get help from [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.md).
- Want to help on localization? Check out [Crowdin](https://crowdin.com/project/gogs)!
## Features
- Activity timeline
- SSH/HTTP(S) protocol support
- SMTP/LDAP/reverse proxy authentication support
- Reverse proxy suburl support
- Register/delete/rename account
- Create/manage/delete organization with team management
- Create/migrate/mirror/delete/watch/rename/transfer public/private repository
- Create/fork/migrate/mirror/delete/watch/rename/transfer public/private repository
- Repository viewer/release/issue tracker
- Repository and Organization level webhooks
- Repository Git hooks
- Add/remove repository collaborators
- Gravatar and cache support
- Mail service(register, issue)
- Administration panel
- Slack webhook integration
- Drone CI integration
- Supports MySQL, PostgreSQL and SQLite3
- Social account login(GitHub, Google, QQ, Weibo)
- Multi-language support(English, Chinese, Germany, French etc.)
- Multi-language support([7 languages](https://crowdin.com/project/gogs))
## System Requirements
@@ -57,7 +61,7 @@ Make sure you install [Prerequirements](http://gogs.io/docs/installation/) first
There are 5 ways to install Gogs:
- [Install from binary](http://gogs.io/docs/installation/install_from_binary.md): **STRONGLY RECOMMENDED**
- [Install from binary](http://gogs.io/docs/installation/install_from_binary.md)
- [Install from source](http://gogs.io/docs/installation/install_from_source.md)
- [Install from packages](http://gogs.io/docs/installation/install_from_packages.md)
- [Ship with Docker](https://github.com/gogits/gogs/tree/master/docker)
@@ -65,16 +69,18 @@ There are 5 ways to install Gogs:
## Acknowledgments
- Router and middleware mechanism of [martini](http://martini.codegangsta.io/).
- Router and middleware mechanism of [Macaron](https://github.com/Unknwon/macaron).
- Mail Service, modules design is inspired by [WeTalk](https://github.com/beego/wetalk).
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
- Usage and modification from [beego](http://beego.me) modules.
- Thanks [lavachen](http://www.lavachen.cn/) and [Rocker](http://weibo.com/rocker1989) for designing Logo.
- Thanks [gobuild.io](http://gobuild.io) for providing binary compile and download service.
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
## Contributors
The [core team](http://gogs.io/team) of this project. See [contributors page](https://github.com/gogits/gogs/graphs/contributors) for full list of contributors.
- The [core team](http://gogs.io/team) of this project.
- See [contributors page](https://github.com/gogits/gogs/graphs/contributors) for full list of contributors.
- See [TRANSLATORS](conf/locale/TRANSLATORS) for full list of translators.
## License

View File

@@ -1,11 +1,11 @@
Gogs - Go Git Service [![wercker status](https://app.wercker.com/status/ad0bdb0bc450ac6f09bc56b9640a50aa/s/ "wercker status")](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [![Build Status](https://drone.io/github.com/gogits/gogs/status.png)](https://drone.io/github.com/gogits/gogs/latest)
Gogs - Go Git Service [![wercker status](https://app.wercker.com/status/ad0bdb0bc450ac6f09bc56b9640a50aa/s/ "wercker status")](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs)
=====================
Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。
![Demo](https://gowalker.org/public/gogs_demo.gif)
##### 当前版本0.5.2 Beta
##### 当前版本0.5.8 Beta
## 开发目的
@@ -17,25 +17,29 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
- 您可以到 [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。
- 想要先睹为快?通过 [在线体验](https://try.gogs.io/Unknown/gogs) 或查看 **安装部署 -> 二进制安装** 小节。
- 使用过程中遇到问题?尝试从 [故障排查](http://gogs.io/docs/intro/troubleshooting.md) 页面获取帮助。
- 希望帮助多国语言界面的翻译吗?请立即访问 [Crowdin](https://crowdin.com/project/gogs)
## 功能特性
- 活动时间线
- 支持 SSH/HTTP(S) 协议
- 支持 SMTP/LDAP/反向代理 用户认证
- 注册/删除/重命名 用户
- 创建/管理/删除 组织以及团队管理功能
- 创建/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库
- 仓库 浏览/发布/工单管理
- 仓库和组织级别 Web 钩子
- 添加/删除 仓库协作者
- Gravatar 以及缓存支持
- 邮件服务注册、Issue
- 支持反向代理子路径
- 支持 注册/删除/重命名 用户
- 支持 创建/管理/删除 组织以及团队管理功能
- 支持 创建/派生/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库
- 支持仓库 浏览/发布/工单管理
- 支持仓库和组织级别 Web 钩子
- 支持仓库 Git 钩子
- 支持 添加/删除 仓库协作者
- 支持 Gravatar 以及本地缓存
- 支持邮件服务注册、Issue
- 管理员面板
- Slack Web 钩子集成
- Drone CI 持续部署集成
- 支持 MySQL、PostgreSQL 以及 SQLite3 数据库
- 社交帐号登录GitHub、Google、QQ、微博
- 多语言支持(英文、简体中文、德语、法语等等
- 多语言支持([7 种语言]([more](https://crowdin.com/project/gogs))
## 系统要求
@@ -48,7 +52,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
然后,您可以通过以下 5 种方式来安装 Gogs
- [二进制安装](http://gogs.io/docs/installation/install_from_binary.md): **强烈推荐**
- [二进制安装](http://gogs.io/docs/installation/install_from_binary.md)
- [源码安装](http://gogs.io/docs/installation/install_from_source.md)
- [包管理安装](http://gogs.io/docs/installation/install_from_packages.md)
- [采用 Docker 部署](https://github.com/gogits/gogs/tree/master/docker)
@@ -56,16 +60,18 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
## 特别鸣谢
- 基于 [Macaron](https://github.com/Unknwon/macaron) 的路由与中间件机制。
- 基于 [WeTalk](https://github.com/beego/wetalk) 修改的邮件服务和模块设计。
- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
- [beego](http://beego.me) 模块的使用与修改。
- [martini](http://martini.codegangsta.io/) 的路由与中间件机制。
- 感谢 [gobuild.io](http://gobuild.io) 提供二进制编译与下载服务。
- 感谢 [lavachen](http://www.lavachen.cn/) 和 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。
- 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。
## 贡献成员
本项目的 [开发团队](http://gogs.io/team)。您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。
- 本项目的 [开发团队](http://gogs.io/team)。
- 您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。
- 您可以通过查看 [TRANSLATORS](conf/locale/TRANSLATORS) 文件获取完整的翻译人员列表。
## 授权许可

160
cmd/cert.go Normal file
View File

@@ -0,0 +1,160 @@
// +build cert
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"log"
"math/big"
"net"
"os"
"strings"
"time"
"github.com/codegangsta/cli"
)
var CmdCert = cli.Command{
Name: "cert",
Usage: "Generate self-signed certificate",
Description: `Generate a self-signed X.509 certificate for a TLS server.
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
Action: runCert,
Flags: []cli.Flag{
cli.StringFlag{"host", "", "Comma-separated hostnames and IPs to generate a certificate for", ""},
cli.StringFlag{"ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521", ""},
cli.IntFlag{"rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set", ""},
cli.StringFlag{"start-date", "", "Creation date formatted as Jan 1 15:04:05 2011", ""},
cli.DurationFlag{"duration", 365 * 24 * time.Hour, "Duration that certificate is valid for", ""},
cli.BoolFlag{"ca", "whether this cert should be its own Certificate Authority", ""},
},
}
func publicKey(priv interface{}) interface{} {
switch k := priv.(type) {
case *rsa.PrivateKey:
return &k.PublicKey
case *ecdsa.PrivateKey:
return &k.PublicKey
default:
return nil
}
}
func pemBlockForKey(priv interface{}) *pem.Block {
switch k := priv.(type) {
case *rsa.PrivateKey:
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
case *ecdsa.PrivateKey:
b, err := x509.MarshalECPrivateKey(k)
if err != nil {
log.Fatal("unable to marshal ECDSA private key: %v", err)
}
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
default:
return nil
}
}
func runCert(ctx *cli.Context) {
if len(ctx.String("host")) == 0 {
log.Fatal("Missing required --host parameter")
}
var priv interface{}
var err error
switch ctx.String("ecdsa-curve") {
case "":
priv, err = rsa.GenerateKey(rand.Reader, ctx.Int("rsa-bits"))
case "P224":
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
case "P256":
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
case "P384":
priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
case "P521":
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
default:
log.Fatalf("Unrecognized elliptic curve: %q", ctx.String("ecdsa-curve"))
}
if err != nil {
log.Fatalf("Failed to generate private key: %s", err)
}
var notBefore time.Time
if len(ctx.String("start-date")) == 0 {
notBefore = time.Now()
} else {
notBefore, err = time.Parse("Jan 2 15:04:05 2006", ctx.String("start-date"))
if err != nil {
log.Fatalf("Failed to parse creation date: %s", err)
}
}
notAfter := notBefore.Add(ctx.Duration("duration"))
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
log.Fatalf("Failed to generate serial number: %s", err)
}
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
Organization: []string{"Acme Co"},
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
hosts := strings.Split(ctx.String("host"), ",")
for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
template.DNSNames = append(template.DNSNames, h)
}
}
if ctx.Bool("ca") {
template.IsCA = true
template.KeyUsage |= x509.KeyUsageCertSign
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
if err != nil {
log.Fatalf("Failed to create certificate: %s", err)
}
certOut, err := os.Create("cert.pem")
if err != nil {
log.Fatalf("Failed to open cert.pem for writing: %s", err)
}
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
certOut.Close()
log.Println("Written cert.pem")
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatal("failed to open key.pem for writing: %v", err)
}
pem.Encode(keyOut, pemBlockForKey(priv))
keyOut.Close()
log.Println("Written key.pem")
}

36
cmd/cert_stub.go Normal file
View File

@@ -0,0 +1,36 @@
// +build !cert
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"fmt"
"os"
"time"
"github.com/codegangsta/cli"
)
var CmdCert = cli.Command{
Name: "cert",
Usage: "Generate self-signed certificate",
Description: `Generate a self-signed X.509 certificate for a TLS server.
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
Action: runCert,
Flags: []cli.Flag{
cli.StringFlag{"host", "", "Comma-separated hostnames and IPs to generate a certificate for", ""},
cli.StringFlag{"ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521", ""},
cli.IntFlag{"rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set", ""},
cli.StringFlag{"start-date", "", "Creation date formatted as Jan 1 15:04:05 2011", ""},
cli.DurationFlag{"duration", 365 * 24 * time.Hour, "Duration that certificate is valid for", ""},
cli.BoolFlag{"ca", "whether this cert should be its own Certificate Authority", ""},
},
}
func runCert(ctx *cli.Context) {
fmt.Println("Command cert not available, please use build tags 'cert' to rebuild.")
os.Exit(1)
}

View File

@@ -58,8 +58,9 @@ func runDump(ctx *cli.Context) {
workDir, _ := setting.WorkDir()
z.AddFile("gogs-repo.zip", path.Join(workDir, "gogs-repo.zip"))
z.AddFile("gogs-db.sql", path.Join(workDir, "gogs-db.sql"))
z.AddFile("custom/conf/app.ini", path.Join(workDir, "custom/conf/app.ini"))
z.AddDir("custom", path.Join(workDir, "custom"))
z.AddDir("log", path.Join(workDir, "log"))
// FIXME: SSH key file.
if err = z.Close(); err != nil {
os.Remove(fileName)
log.Fatalf("Fail to save %s: %v", fileName, err)

View File

@@ -14,6 +14,7 @@ import (
"runtime"
"strings"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
"github.com/gogits/gogs/models"
@@ -164,10 +165,12 @@ func runFixLocation(ctx *cli.Context) {
// Fix in authorized_keys file.
sshPath := path.Join(models.SshPath, "authorized_keys")
fmt.Printf("Fixing pathes in file: %s\n", sshPath)
if err := rewriteAuthorizedKeys(sshPath, oldPath, execPath); err != nil {
fmt.Println(err)
os.Exit(1)
if com.IsFile(sshPath) {
fmt.Printf("Fixing pathes in file: %s\n", sshPath)
if err := rewriteAuthorizedKeys(sshPath, oldPath, execPath); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
// Fix position in gogs-repositories.

View File

@@ -96,7 +96,7 @@ func runServ(k *cli.Context) {
println("Gogs: you are not the owner of SSH key")
log.GitLogger.Fatal(2, "Invalid owner of SSH key: %d", keyId)
}
println("Gogs: internal error:", err)
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to get user by key ID(%d): %v", keyId, err)
}
@@ -125,7 +125,7 @@ func runServ(k *cli.Context) {
println("Gogs: given repository owner are not registered")
log.GitLogger.Fatal(2, "Unregistered owner: %s", repoUserName)
}
println("Gogs: internal error:", err)
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to get repository owner(%s): %v", repoUserName, err)
}
@@ -134,7 +134,7 @@ func runServ(k *cli.Context) {
case isWrite:
has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.WRITABLE)
if err != nil {
println("Gogs: internal error:", err)
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to check write access:", err)
} else if !has {
println("You have no right to write this repository")
@@ -147,7 +147,7 @@ func runServ(k *cli.Context) {
println("Gogs: given repository does not exist")
log.GitLogger.Fatal(2, "Repository does not exist: %s/%s", repoUser.Name, repoName)
}
println("Gogs: internal error:", err)
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to get repository: %v", err)
}
@@ -157,21 +157,27 @@ func runServ(k *cli.Context) {
has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE)
if err != nil {
println("Gogs: internal error:", err)
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to check read access:", err)
} else if !has {
println("You have no right to access this repository")
log.GitLogger.Fatal(2, "User %s has no right to read repository %s", user.Name, repoPath)
}
default:
println("Unknown command")
println("Unknown command: " + cmd)
return
}
uuid := uuid.NewV4().String()
os.Setenv("uuid", uuid)
gitcmd := exec.Command(verb, repoPath)
var gitcmd *exec.Cmd
verbs := strings.Split(verb, " ")
if len(verbs) == 2 {
gitcmd = exec.Command(verbs[0], verbs[1], repoPath)
} else {
gitcmd = exec.Command(verb, repoPath)
}
gitcmd.Dir = setting.RepoRootPath
gitcmd.Stdout = os.Stdout
gitcmd.Stdin = os.Stdin

View File

@@ -5,30 +5,37 @@
package cmd
import (
"crypto/tls"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"net/http/fcgi"
"os"
"path"
"strings"
"github.com/Unknwon/macaron"
"github.com/codegangsta/cli"
"github.com/macaron-contrib/binding"
"github.com/macaron-contrib/cache"
"github.com/macaron-contrib/captcha"
"github.com/macaron-contrib/csrf"
"github.com/macaron-contrib/i18n"
"github.com/macaron-contrib/oauth2"
"github.com/macaron-contrib/session"
"github.com/macaron-contrib/toolbox"
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/auth/apiv1"
"github.com/gogits/gogs/modules/avatar"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/middleware"
"github.com/gogits/gogs/modules/middleware/binding"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/routers"
"github.com/gogits/gogs/routers/admin"
@@ -50,6 +57,7 @@ and it takes care of all the other things for you`,
// checkVersion checks if binary matches the version of templates files.
func checkVersion() {
// Templates.
data, err := ioutil.ReadFile(path.Join(setting.StaticRootPath, "templates/.VERSION"))
if err != nil {
log.Fatal(4, "Fail to read 'templates/.VERSION': %v", err)
@@ -57,6 +65,20 @@ func checkVersion() {
if string(data) != setting.AppVer {
log.Fatal(4, "Binary and template file version does not match, did you forget to recompile?")
}
// Check dependency version.
macaronVer := git.MustParseVersion(strings.Join(strings.Split(macaron.Version(), ".")[:3], "."))
if macaronVer.LessThan(git.MustParseVersion("0.4.7")) {
log.Fatal(4, "Package macaron version is too old, did you forget to update?(github.com/Unknwon/macaron)")
}
i18nVer := git.MustParseVersion(i18n.Version())
if i18nVer.LessThan(git.MustParseVersion("0.0.2")) {
log.Fatal(4, "Package i18n version is too old, did you forget to update?(github.com/macaron-contrib/i18n)")
}
sessionVer := git.MustParseVersion(session.Version())
if sessionVer.LessThan(git.MustParseVersion("0.0.5")) {
log.Fatal(4, "Package session version is too old, did you forget to update?(github.com/macaron-contrib/session)")
}
}
// newMacaron initializes Macaron instance.
@@ -64,39 +86,55 @@ func newMacaron() *macaron.Macaron {
m := macaron.New()
m.Use(macaron.Logger())
m.Use(macaron.Recovery())
if setting.EnableGzip {
m.Use(macaron.Gziper())
}
if setting.Protocol == setting.FCGI {
m.SetURLPrefix(setting.AppSubUrl)
}
m.Use(macaron.Static(
path.Join(setting.StaticRootPath, "public"),
macaron.StaticOptions{
SkipLogging: !setting.DisableRouterLog,
},
))
// if setting.EnableGzip {
// m.Use(macaron.Gzip())
// }
m.Use(macaron.Static(
setting.AvatarUploadPath,
macaron.StaticOptions{
Prefix: "avatars",
SkipLogging: !setting.DisableRouterLog,
},
))
m.Use(macaron.Renderer(macaron.RenderOptions{
Directory: path.Join(setting.StaticRootPath, "templates"),
Funcs: []template.FuncMap{base.TemplateFuncs},
IndentJSON: macaron.Env != macaron.PROD,
}))
m.Use(i18n.I18n(i18n.Options{
Langs: setting.Langs,
Names: setting.Names,
Redirect: true,
SubURL: setting.AppSubUrl,
Directory: path.Join(setting.ConfRootPath, "locale"),
CustomDirectory: path.Join(setting.CustomPath, "conf/locale"),
Langs: setting.Langs,
Names: setting.Names,
Redirect: true,
}))
m.Use(cache.Cacher(cache.Options{
Adapter: setting.CacheAdapter,
Interval: setting.CacheInternal,
Conn: setting.CacheConn,
}))
m.Use(captcha.Captchaer())
m.Use(captcha.Captchaer(captcha.Options{
SubURL: setting.AppSubUrl,
}))
m.Use(session.Sessioner(session.Options{
Provider: setting.SessionProvider,
Config: *setting.SessionConfig,
}))
m.Use(csrf.Generate(csrf.Options{
Secret: setting.SecretKey,
SetCookie: true,
Header: "X-Csrf-Token",
Secret: setting.SecretKey,
SetCookie: true,
Header: "X-Csrf-Token",
CookiePath: setting.AppSubUrl,
}))
m.Use(toolbox.Toolboxer(m, toolbox.Options{
HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{
@@ -106,6 +144,13 @@ func newMacaron() *macaron.Macaron {
},
},
}))
// OAuth 2.
if setting.OauthService != nil {
for _, info := range setting.OauthService.OauthInfos {
m.Use(oauth2.NewOAuth2Provider(info.Options, info.AuthUrl, info.TokenUrl))
}
}
m.Use(middleware.Contexter())
return m
}
@@ -121,73 +166,92 @@ func runWeb(*cli.Context) {
ignSignInAndCsrf := middleware.Toggle(&middleware.ToggleOptions{DisableCsrf: true})
reqSignOut := middleware.Toggle(&middleware.ToggleOptions{SignOutRequire: true})
bind := binding.Bind
bindIgnErr := binding.BindIgnErr
// Routers.
m.Get("/", ignSignIn, routers.Home)
m.Get("/explore", ignSignIn, routers.Explore)
// FIXME: when i'm binding form here???
m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install)
m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost)
m.Group("", func(r *macaron.Router) {
r.Get("/pulls", user.Pulls)
r.Get("/issues", user.Issues)
m.Group("", func() {
m.Get("/pulls", user.Pulls)
m.Get("/issues", user.Issues)
}, reqSignIn)
// API routers.
m.Group("/api", func(_ *macaron.Router) {
m.Group("/v1", func(r *macaron.Router) {
// API.
// FIXME: custom form error response.
m.Group("/api", func() {
m.Group("/v1", func() {
// Miscellaneous.
r.Post("/markdown", bindIgnErr(apiv1.MarkdownForm{}), v1.Markdown)
r.Post("/markdown/raw", v1.MarkdownRaw)
m.Post("/markdown", bindIgnErr(apiv1.MarkdownForm{}), v1.Markdown)
m.Post("/markdown/raw", v1.MarkdownRaw)
// Users.
m.Group("/users", func(r *macaron.Router) {
r.Get("/search", v1.SearchUsers)
m.Group("/users", func() {
m.Get("/search", v1.SearchUsers)
m.Group("/:username", func() {
m.Get("", v1.GetUserInfo)
m.Group("/tokens", func() {
m.Combo("").Get(v1.ListAccessTokens).Post(bind(v1.CreateAccessTokenForm{}), v1.CreateAccessToken)
}, middleware.ApiReqBasicAuth())
})
})
// Repositories.
m.Group("/repos", func(r *macaron.Router) {
r.Get("/search", v1.SearchRepos)
r.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), v1.Migrate)
m.Combo("/user/repos", middleware.ApiReqToken()).Get(v1.ListMyRepos).Post(bind(api.CreateRepoOption{}), v1.CreateRepo)
m.Post("/org/:org/repos", middleware.ApiReqToken(), bind(api.CreateRepoOption{}), v1.CreateOrgRepo)
m.Group("/repos", func() {
m.Get("/search", v1.SearchRepos)
m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), v1.MigrateRepo)
m.Group("/:username/:reponame", func() {
m.Combo("/hooks").Get(v1.ListRepoHooks).Post(bind(api.CreateHookOption{}), v1.CreateRepoHook)
m.Patch("/hooks/:id:int", bind(api.EditHookOption{}), v1.EditRepoHook)
m.Get("/raw/*", middleware.RepoRef(), v1.GetRepoRawFile)
}, middleware.ApiRepoAssignment(), middleware.ApiReqToken())
})
r.Any("/*", func(ctx *middleware.Context) {
ctx.JSON(404, &base.ApiJsonErr{"Not Found", v1.DOC_URL})
m.Any("/*", func(ctx *middleware.Context) {
ctx.JSON(404, &base.ApiJsonErr{"Not Found", base.DOC_URL})
})
})
})
// User routers.
m.Group("/user", func(r *macaron.Router) {
r.Get("/login", user.SignIn)
r.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)
r.Get("/login/:name", user.SocialSignIn)
r.Get("/sign_up", user.SignUp)
r.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
r.Get("/reset_password", user.ResetPasswd)
r.Post("/reset_password", user.ResetPasswdPost)
// User.
m.Group("/user", func() {
m.Get("/login", user.SignIn)
m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)
m.Get("/info/:name", user.SocialSignIn)
m.Get("/sign_up", user.SignUp)
m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
m.Get("/reset_password", user.ResetPasswd)
m.Post("/reset_password", user.ResetPasswdPost)
}, reqSignOut)
m.Group("/user/settings", func(r *macaron.Router) {
r.Get("", user.Settings)
r.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
r.Get("/password", user.SettingsPassword)
r.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost)
r.Get("/ssh", user.SettingsSSHKeys)
r.Post("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost)
r.Get("/social", user.SettingsSocial)
r.Route("/delete", "GET,POST", user.SettingsDelete)
m.Group("/user/settings", func() {
m.Get("", user.Settings)
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), user.SettingsAvatar)
m.Get("/password", user.SettingsPassword)
m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost)
m.Get("/ssh", user.SettingsSSHKeys)
m.Post("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost)
m.Get("/social", user.SettingsSocial)
m.Combo("/applications").Get(user.SettingsApplications).Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost)
m.Route("/delete", "GET,POST", user.SettingsDelete)
}, reqSignIn)
m.Group("/user", func(r *macaron.Router) {
m.Group("/user", func() {
// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds)
r.Any("/activate", user.Activate)
r.Get("/email2user", user.Email2User)
r.Get("/forget_password", user.ForgotPasswd)
r.Post("/forget_password", user.ForgotPasswdPost)
r.Get("/logout", user.SignOut)
m.Any("/activate", user.Activate)
m.Get("/email2user", user.Email2User)
m.Get("/forget_password", user.ForgotPasswd)
m.Post("/forget_password", user.ForgotPasswdPost)
m.Get("/logout", user.SignOut)
})
m.Get("/user/:username", ignSignIn, user.Profile) // TODO: Legacy
// Gravatar service.
avt := avatar.CacheServer("public/img/avatar/", "public/img/avatar_default.jpg")
os.MkdirAll("public/img/avatar/", os.ModePerm)
@@ -195,35 +259,40 @@ func runWeb(*cli.Context) {
adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true})
m.Group("/admin", func(r *macaron.Router) {
m.Group("/admin", func() {
m.Get("", adminReq, admin.Dashboard)
r.Get("/config", admin.Config)
r.Get("/monitor", admin.Monitor)
m.Get("/config", admin.Config)
m.Get("/monitor", admin.Monitor)
m.Group("/users", func(r *macaron.Router) {
r.Get("", admin.Users)
r.Get("/new", admin.NewUser)
r.Post("/new", bindIgnErr(auth.RegisterForm{}), admin.NewUserPost)
r.Get("/:userid", admin.EditUser)
r.Post("/:userid", bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost)
r.Post("/:userid/delete", admin.DeleteUser)
m.Group("/users", func() {
m.Get("", admin.Users)
m.Get("/new", admin.NewUser)
m.Post("/new", bindIgnErr(auth.RegisterForm{}), admin.NewUserPost)
m.Get("/:userid", admin.EditUser)
m.Post("/:userid", bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost)
m.Post("/:userid/delete", admin.DeleteUser)
})
m.Group("/orgs", func(r *macaron.Router) {
r.Get("", admin.Organizations)
m.Group("/orgs", func() {
m.Get("", admin.Organizations)
})
m.Group("/repos", func(r *macaron.Router) {
r.Get("", admin.Repositories)
m.Group("/repos", func() {
m.Get("", admin.Repositories)
})
m.Group("/auths", func(r *macaron.Router) {
r.Get("", admin.Authentications)
r.Get("/new", admin.NewAuthSource)
r.Post("/new", bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost)
r.Get("/:authid", admin.EditAuthSource)
r.Post("/:authid", bindIgnErr(auth.AuthenticationForm{}), admin.EditAuthSourcePost)
r.Post("/:authid/delete", admin.DeleteAuthSource)
m.Group("/auths", func() {
m.Get("", admin.Authentications)
m.Get("/new", admin.NewAuthSource)
m.Post("/new", bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost)
m.Get("/:authid", admin.EditAuthSource)
m.Post("/:authid", bindIgnErr(auth.AuthenticationForm{}), admin.EditAuthSourcePost)
m.Post("/:authid/delete", admin.DeleteAuthSource)
})
m.Group("/notices", func() {
m.Get("", admin.Notices)
m.Get("/:id:int/delete", admin.DeleteNotice)
})
}, adminReq)
@@ -235,129 +304,147 @@ func runWeb(*cli.Context) {
reqTrueOwner := middleware.RequireTrueOwner()
// Organization routers.
m.Group("/org", func(r *macaron.Router) {
r.Get("/create", org.Create)
r.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost)
// Organization.
m.Group("/org", func() {
m.Get("/create", org.Create)
m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost)
m.Group("/:org", func(r *macaron.Router) {
r.Get("/dashboard", user.Dashboard)
r.Get("/members", org.Members)
r.Get("/members/action/:action", org.MembersAction)
m.Group("/:org", func() {
m.Get("/dashboard", user.Dashboard)
m.Get("/members", org.Members)
m.Get("/members/action/:action", org.MembersAction)
r.Get("/teams", org.Teams)
r.Get("/teams/:team", org.TeamMembers)
r.Get("/teams/:team/repositories", org.TeamRepositories)
r.Get("/teams/:team/action/:action", org.TeamsAction)
r.Get("/teams/:team/action/repo/:action", org.TeamsRepoAction)
m.Get("/teams", org.Teams)
m.Get("/teams/:team", org.TeamMembers)
m.Get("/teams/:team/repositories", org.TeamRepositories)
m.Get("/teams/:team/action/:action", org.TeamsAction)
m.Get("/teams/:team/action/repo/:action", org.TeamsRepoAction)
}, middleware.OrgAssignment(true, true))
m.Group("/:org", func(r *macaron.Router) {
r.Get("/teams/new", org.NewTeam)
r.Post("/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost)
r.Get("/teams/:team/edit", org.EditTeam)
r.Post("/teams/:team/edit", bindIgnErr(auth.CreateTeamForm{}), org.EditTeamPost)
r.Post("/teams/:team/delete", org.DeleteTeam)
m.Group("/:org", func() {
m.Get("/teams/new", org.NewTeam)
m.Post("/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost)
m.Get("/teams/:team/edit", org.EditTeam)
m.Post("/teams/:team/edit", bindIgnErr(auth.CreateTeamForm{}), org.EditTeamPost)
m.Post("/teams/:team/delete", org.DeleteTeam)
m.Group("/settings", func(r *macaron.Router) {
r.Get("", org.Settings)
r.Post("", bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost)
r.Get("/hooks", org.SettingsHooks)
r.Get("/hooks/new", repo.WebHooksNew)
r.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
r.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
r.Get("/hooks/:id", repo.WebHooksEdit)
r.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
r.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
r.Route("/delete", "GET,POST", org.SettingsDelete)
m.Group("/settings", func() {
m.Get("", org.Settings)
m.Post("", bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost)
m.Get("/hooks", org.SettingsHooks)
m.Get("/hooks/new", repo.WebHooksNew)
m.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
m.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
m.Get("/hooks/:id", repo.WebHooksEdit)
m.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
m.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Route("/delete", "GET,POST", org.SettingsDelete)
})
r.Route("/invitations/new", "GET,POST", org.Invitation)
m.Route("/invitations/new", "GET,POST", org.Invitation)
}, middleware.OrgAssignment(true, true, true))
}, reqSignIn)
m.Group("/org", func(r *macaron.Router) {
r.Get("/:org", org.Home)
m.Group("/org", func() {
m.Get("/:org", org.Home)
}, middleware.OrgAssignment(true))
// Repository routers.
m.Group("/repo", func(r *macaron.Router) {
r.Get("/create", repo.Create)
r.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost)
r.Get("/migrate", repo.Migrate)
r.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost)
// Repository.
m.Group("/repo", func() {
m.Get("/create", repo.Create)
m.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost)
m.Get("/migrate", repo.Migrate)
m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost)
m.Get("/fork", repo.Fork)
m.Post("/fork", bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost)
}, reqSignIn)
m.Group("/:username/:reponame", func(r *macaron.Router) {
r.Get("/settings", repo.Settings)
r.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
m.Group("/settings", func(r *macaron.Router) {
r.Route("/collaboration", "GET,POST", repo.SettingsCollaboration)
r.Get("/hooks", repo.Webhooks)
r.Get("/hooks/new", repo.WebHooksNew)
r.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
r.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
r.Get("/hooks/:id", repo.WebHooksEdit)
r.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
r.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Group("/:username/:reponame", func() {
m.Get("/settings", repo.Settings)
m.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
m.Group("/settings", func() {
m.Route("/collaboration", "GET,POST", repo.SettingsCollaboration)
m.Get("/hooks", repo.Webhooks)
m.Get("/hooks/new", repo.WebHooksNew)
m.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
m.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
m.Get("/hooks/:id", repo.WebHooksEdit)
m.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
m.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Group("/hooks/git", func() {
m.Get("", repo.GitHooks)
m.Get("/:name", repo.GitHooksEdit)
m.Post("/:name", repo.GitHooksEditPost)
}, middleware.GitHookService())
})
}, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner)
m.Group("/:username/:reponame", func(r *macaron.Router) {
r.Get("/action/:action", repo.Action)
m.Group("/:username/:reponame", func() {
m.Get("/action/:action", repo.Action)
m.Group("/issues", func(r *macaron.Router) {
r.Get("/new", repo.CreateIssue)
r.Post("/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost)
r.Post("/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue)
r.Post("/:index/label", repo.UpdateIssueLabel)
r.Post("/:index/milestone", repo.UpdateIssueMilestone)
r.Post("/:index/assignee", repo.UpdateAssignee)
r.Get("/:index/attachment/:id", repo.IssueGetAttachment)
r.Post("/labels/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
r.Post("/labels/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
r.Post("/labels/delete", repo.DeleteLabel)
r.Get("/milestones", repo.Milestones)
r.Get("/milestones/new", repo.NewMilestone)
r.Post("/milestones/new", bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
r.Get("/milestones/:index/edit", repo.UpdateMilestone)
r.Post("/milestones/:index/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.UpdateMilestonePost)
r.Get("/milestones/:index/:action", repo.UpdateMilestone)
m.Group("/issues", func() {
m.Get("/new", repo.CreateIssue)
m.Post("/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost)
m.Post("/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue)
m.Post("/:index/label", repo.UpdateIssueLabel)
m.Post("/:index/milestone", repo.UpdateIssueMilestone)
m.Post("/:index/assignee", repo.UpdateAssignee)
m.Get("/:index/attachment/:id", repo.IssueGetAttachment)
m.Post("/labels/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
m.Post("/labels/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
m.Post("/labels/delete", repo.DeleteLabel)
m.Get("/milestones/new", repo.NewMilestone)
m.Post("/milestones/new", bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
m.Get("/milestones/:index/edit", repo.UpdateMilestone)
m.Post("/milestones/:index/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.UpdateMilestonePost)
m.Get("/milestones/:index/:action", repo.UpdateMilestone)
})
r.Post("/comment/:action", repo.Comment)
r.Get("/releases/new", repo.NewRelease)
r.Get("/releases/edit/:tagname", repo.EditRelease)
m.Post("/comment/:action", repo.Comment)
m.Group("/releases", func() {
m.Get("/new", repo.NewRelease)
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
m.Get("/edit/:tagname", repo.EditRelease)
m.Post("/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
}, middleware.RepoRef())
}, reqSignIn, middleware.RepoAssignment(true))
m.Group("/:username/:reponame", func(r *macaron.Router) {
r.Post("/releases/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
r.Post("/releases/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
}, reqSignIn, middleware.RepoAssignment(true, true))
m.Group("/:username/:reponame", func() {
m.Get("/releases", middleware.RepoRef(), repo.Releases)
m.Get("/issues", repo.Issues)
m.Get("/issues/:index", repo.ViewIssue)
m.Get("/issues/milestones", repo.Milestones)
m.Get("/pulls", repo.Pulls)
m.Get("/branches", repo.Branches)
m.Get("/archive/*", repo.Download)
m.Get("/issues2/", repo.Issues2)
m.Get("/pulls2/", repo.PullRequest2)
m.Get("/labels2/", repo.Labels2)
m.Get("/milestone2/", repo.Milestones2)
m.Group("/:username/:reponame", func(r *macaron.Router) {
r.Get("/issues", repo.Issues)
r.Get("/issues/:index", repo.ViewIssue)
r.Get("/pulls", repo.Pulls)
r.Get("/branches", repo.Branches)
m.Group("", func() {
m.Get("/src/*", repo.Home)
m.Get("/raw/*", repo.SingleDownload)
m.Get("/commits/*", repo.RefCommits)
m.Get("/commit/*", repo.Diff)
}, middleware.RepoRef())
m.Get("/compare/:before([a-z0-9]+)...:after([a-z0-9]+)", repo.CompareDiff)
}, ignSignIn, middleware.RepoAssignment(true))
m.Group("/:username/:reponame", func(r *macaron.Router) {
r.Get("/src/:branchname", repo.Home)
r.Get("/src/:branchname/*", repo.Home)
r.Get("/raw/:branchname/*", repo.SingleDownload)
r.Get("/commits/:branchname", repo.Commits)
r.Get("/commits/:branchname/search", repo.SearchCommits)
r.Get("/commits/:branchname/*", repo.FileHistory)
r.Get("/commit/:branchname", repo.Diff)
r.Get("/commit/:branchname/*", repo.Diff)
r.Get("/releases", repo.Releases)
r.Get("/archive/*.*", repo.Download)
r.Get("/compare/:before([a-z0-9]+)...:after([a-z0-9]+)", repo.CompareDiff)
}, ignSignIn, middleware.RepoAssignment(true, true))
m.Group("/:username", func() {
m.Get("/:reponame", ignSignIn, middleware.RepoAssignment(true, true), middleware.RepoRef(), repo.Home)
m.Any("/:reponame/*", ignSignInAndCsrf, repo.Http)
})
m.Group("/:username", func(r *macaron.Router) {
r.Get("/:reponame", ignSignIn, middleware.RepoAssignment(true, true, true), repo.Home)
r.Any("/:reponame/*", ignSignInAndCsrf, repo.Http)
// robots.txt
m.Get("/robots.txt", func(ctx *middleware.Context) {
if setting.HasRobotsTxt {
ctx.ServeFile(path.Join(setting.CustomPath, "robots.txt"))
} else {
ctx.Error(404)
}
})
// Not found handler.
@@ -365,12 +452,15 @@ func runWeb(*cli.Context) {
var err error
listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
log.Info("Listen: %v://%s", setting.Protocol, listenAddr)
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl)
switch setting.Protocol {
case setting.HTTP:
err = http.ListenAndServe(listenAddr, m)
case setting.HTTPS:
err = http.ListenAndServeTLS(listenAddr, setting.CertFile, setting.KeyFile, m)
server := &http.Server{Addr: listenAddr, TLSConfig: &tls.Config{MinVersion: tls.VersionTLS10}, Handler: m}
err = server.ListenAndServeTLS(setting.CertFile, setting.KeyFile)
case setting.FCGI:
err = fcgi.Serve(nil, m)
default:
log.Fatal(4, "Invalid protocol: %s", setting.Protocol)
}

View File

@@ -21,14 +21,21 @@ OFFLINE_MODE = false
DISABLE_ROUTER_LOG = false
; Generate steps:
; $ cd path/to/gogs/custom/https
; $ go run $GOROOT/src/pkg/crypto/tls/generate_cert.go -ca=true -duration=8760h0m0s -host=myhost.example.com
; $ ./gogs cert -ca=true -duration=8760h0m0s -host=myhost.example.com
;
; Or from a .pfx file exported from the Windows certificate store (do
; not forget to export the private key):
; $ openssl pkcs12 -in cert.pfx -out cert.pem -nokeys
; $ openssl pkcs12 -in cert.pfx -out key.pem -nocerts -nodes
CERT_FILE = custom/https/cert.pem
KEY_FILE = custom/https/key.pem
; Upper level of template and static file path
; default is the path where Gogs is executed
STATIC_ROOT_PATH =
; Application level GZIP support
#ENABLE_GZIP = false
ENABLE_GZIP = false
; Landing page for non-logged users, can be "home" or "explore"
LANDING_PAGE = home
[database]
; Either "mysql", "postgres" or "sqlite3", it's your choice
@@ -70,6 +77,7 @@ ENABLE_CACHE_AVATAR = false
ENABLE_NOTIFY_MAIL = false
; More detail: https://github.com/gogits/gogs/issues/165
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
ENABLE_REVERSE_PROXY_AUTO_REGISTERATION = false
[webhook]
; Cron task interval in minutes
@@ -118,13 +126,10 @@ TOKEN_URL = https://accounts.google.com/o/oauth2/token
ENABLED = false
CLIENT_ID =
CLIENT_SECRET =
SCOPES = all
SCOPES = get_user_info
; QQ 互联
; AUTH_URL = https://graph.qq.com/oauth2.0/authorize
; TOKEN_URL = https://graph.qq.com/oauth2.0/token
; Tencent weibo
AUTH_URL = https://open.t.qq.com/cgi-bin/oauth2/authorize
TOKEN_URL = https://open.t.qq.com/cgi-bin/oauth2/access_token
AUTH_URL = https://graph.qq.com/oauth2.0/authorize
TOKEN_URL = https://graph.qq.com/oauth2.0/token
[oauth.weibo]
ENABLED = false
@@ -163,14 +168,13 @@ ENABLE_SET_COOKIE = true
GC_INTERVAL_TIME = 86400
; Session life time, default is 86400
SESSION_LIFE_TIME = 86400
; session id hash func, Either "sha1", "sha256" or "md5" default is sha1
SESSION_ID_HASHFUNC = sha1
; Session hash key, default is use random string
SESSION_ID_HASHKEY =
[picture]
; The place to picture data, either "server" or "qiniu", default is "server"
SERVICE = server
AVATAR_UPLOAD_PATH = data/avatars
; Chinese users can choose "duoshuo"
GRAVATAR_SOURCE = gravatar
DISABLE_GRAVATAR = false
[attachment]
@@ -254,7 +258,13 @@ CONN =
[git]
MAX_GITDIFF_LINES = 10000
; Arguments for command 'git fsck', e.g.: "--unreachable --tags"
; see more on http://git-scm.com/docs/git-fsck/1.7.5
FSCK_ARGS =
; Arguments for command 'git gc', e.g.: "--aggressive --auto"
; see more on http://git-scm.com/docs/git-gc/1.7.5
GC_ARGS =
[i18n]
LANGS = en-US,zh-CN,de-DE,fr-CA
NAMES = English,简体中文,Deutsch,Français
LANGS = en-US,zh-CN,zh-HK,de-DE,fr-CA,nl-NL,lv-LV
NAMES = English,简体中文,繁體中文,Deutsch,Français,Nederlands,Latviešu

6
conf/locale/TRANSLATORS Normal file
View File

@@ -0,0 +1,6 @@
# This file lists all PUBLIC individuals having contributed content to the translation.
# Order of name is meaningless.
Thomas Fanninger <gogs.thomas@fanninger.at>
Łukasz Jan Niemier <lukasz@niemier.pl>
Lafriks <lafriks@gmail.com>

1235
conf/locale/locale_de-DE.ini Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,7 @@ organization = Organization
mirror = Mirror
new_repo = New Repository
new_migrate = New Migration
new_fork = New Fork Repository
new_org = New Organization
manage_org = Manage Organizations
admin_panel = Admin Panel
@@ -41,7 +42,7 @@ cancel = Cancel
[install]
install = Installation
title = Install Steps For First-time Run
requite_db_desc = Gogs requires MySQL, PostgreSQL or SQLite3, but SQLite3 is usually available in the official binary version.
requite_db_desc = Gogs requires MySQL, PostgreSQL or SQLite3.
db_type = Database Type
host = Host
user = User
@@ -74,7 +75,7 @@ confirm_password = Confirm Password
admin_email = E-mail
install_gogs = Install Gogs
test_git_failed = Fail to test 'git' command: %v
sqlite3_not_available = Your release version does not support SQLite3, please download the official binary version from http://gogs.io/docs/installation/install_from_binary.html, NOT the gobuild version.
sqlite3_not_available = Your release version does not support SQLite3, please download the official binary version from %s, NOT the gobuild version.
invalid_db_setting = Database setting is not correct: %v
invalid_repo_path = Repository root path is invalid: %v
run_user_not_match = Run user isn't the current user: %s -> %s
@@ -159,18 +160,33 @@ user_not_exist = Given user does not exist.
last_org_owner = The user to remove is the last member in owner team. There must be another owner.
invalid_ssh_key = Sorry, we're not able to verify your SSH key: %s
unable_verify_ssh_key = Gogs cannot verify your SSH key, but we assume that is valid, please make sure yourself.
auth_failed = Authentication failed: %v
still_own_repo = Your account still have ownership of repository, you have to delete or transfer them first.
still_has_org = Your account still have membership of organization, you have to left or delete them first.
org_still_own_repo = This organization still have ownership of repository, you have to delete or transfer them first.
still_own_user = This authentication still has used by some users, you should move them and then delete again.
target_branch_not_exist = Target branch does not exist
[user]
change_avatar = Change your avatar at gravatar.com
change_custom_avatar = Change your avatar in settings
join_on = Joined on
repositories = Repositories
activity = Public Activity
followers = Followers
starred = Starred
following = Following
[settings]
profile = Profile
password = Password
ssh_keys = SSH Keys
social = Social Accounts
applications = Applications
orgs = Organizations
delete = Delete Account
uid = Uid
@@ -181,7 +197,19 @@ full_name = Full Name
website = Website
location = Location
update_profile = Update Profile
update_profile_success = Your profile has been successfully updated.
update_profile_success = Your profile has been updated successfully.
change_username = Username Changed
change_username_desc = Username has been changed, do you want to continue? This will affect all links relate to your account.
continue = Continue
cancel = Cancel
enable_custom_avatar = Enable Custom Avatar
enable_custom_avatar_helper = Enable this to disable fetch from Gravatar
choose_new_avatar = Choose new avatar
update_avatar = Update Avatar Setting
uploaded_avatar_not_a_image = Uploaded file is not a image.
no_custom_avatar_available = No custom avatar available, cannot enable it.
update_avatar_success = Your avatar setting has been updated successfully.
change_password = Change Password
old_password = Current Password
@@ -192,7 +220,7 @@ change_password_success = Password is changed successfully. You can now sign in
manage_ssh_keys = Manage SSH Keys
add_key = Add Key
ssh_desc = This is a list of SSH keys associated with your account. Remove any keys that you do not recognize.
ssh_helper = <strong>Need help?</strong> Check out our guide to <a href="https://help.github.com/articles/generating-ssh-keys">generating SSH keys</a> or troubleshoot <a href="https://help.github.com/ssh-issues/">common SSH Problems</a>.
ssh_helper = <strong>Need help?</strong> Check out our guide to <a href="%s">generating SSH keys</a> or troubleshoot <a href="%s">common SSH Problems</a>.
add_new_key = Add SSH Key
key_name = Key Name
key_content = Content
@@ -207,9 +235,21 @@ social_desc = This is a list of associated social accounts. Remove any binding t
unbind = Unbind
unbind_success = Social account has been unbound.
manage_access_token = Manage Personal Access Tokens
generate_new_token = Generate New Token
tokens_desc = Tokens you have generated that can be used to access the Gogs API.
new_token_desc = As for now, every token will have full access to your account.
token_name = Token Name
generate_token = Generate Token
generate_token_succees = New access token has been generated successfully! Make sure to copy your new personal access token now. You won't be able to see it again!
delete_token = Delete
delete_token_success = Personal access token has been deleted successfully! Don't forget to update your applications as well.
delete_account = Delete Your Account
delete_prompt = The operation will delete your account permanently, and <strong>CANNOT</strong> be undone!
confirm_delete_account = Confirm Deletion
delete_account_title = Account Deletion
delete_account_desc = This account is going to be deleted permanently, do you want to continue?
[repo]
owner = Owner
@@ -217,6 +257,9 @@ repo_name = Repository Name
repo_name_helper = Great repository names are short, memorable and <strong>unique</strong>.
visibility = Visibility
visiblity_helper = This repository is <span class="label label-red label-radius">Private</span>
fork_repo = Fork Repository
fork_from = Fork From
fork_visiblity_helper = Forked repository cannot change its visiblity
repo_desc = Description
repo_lang = Language
repo_lang_helper = Select a .gitignore file
@@ -235,44 +278,78 @@ migrate_type_helper = This repository will be a <span class="label label-blue la
migrate_repo = Migrate Repository
copy_link = Copy
clone_helper = Need help cloning? Visit <a target="_blank" href="http://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository">Help</a>!
click_to_copy = Copy to clipboard
copied = Copied OK
clone_helper = Need help cloning? Visit <a target="_blank" href="%s">Help</a>!
unwatch = Unwatch
watch = Watch
unstar = Unstar
star = Star
fork = Fork
no_desc = No Description
quick_guide = Quick Guide
clone_this_repo = Clone this repository
create_new_repo_command = Create a new repository on the command line
push_exist_repo = Push an existing repository from the command line
branch = Branch
tree = Tree
branch_and_tags = Branches & Tags
branches = Branches
tags = Tags
issues = Issues
commits = Commits
releases = Releases
file_raw = Raw
file_history = History
file_view_raw = View Raw
commits.commits = Commits
commits.search = Search commits
commits.find = Find
commits.author = Author
commits.message = Message
commits.date = Date
commits.older = Older
commits.newer = Newer
settings = Settings
settings.options = Options
settings.collaboration = Collaboration
settings.hooks = Webhooks
settings.githooks = Git Hooks
settings.deploy_keys = Deploy Keys
settings.basic_settings = Basic Settings
settings.danger_zone = Danger Zone
settings.site = Official Site
settings.update_settings = Update Settings
settings.change_reponame = Repository Name Changed
settings.change_reponame_desc = Repository name has been changed, do you want to continue? This will affect all links relate to this repository.
settings.transfer = Transfer Ownership
settings.transfer_desc = Transfer this repo to another user or to an organization where you have admin rights.
settings.new_owner_has_same_repo = New owner already has a repository with same name.
settings.delete = Delete This Repository
settings.delete_desc = Once you delete a repository, there is no going back. Please be certain.
settings.update_settings_success = Repository options has been successfully updated.
settings.transfer_notices = <p>- You will lose access if new owner is a individual user.</p><p>- You will remain access if new owner is an organization and you're one of the owners.</p>
settings.update_settings_success = Repository options has been updated successfully.
settings.transfer_owner = New Owner
settings.make_transfer = Make Transfer
settings.transfer_succeed = Repository ownership has been successfully transferred.
settings.transfer_succeed = Repository ownership has been transferred successfully.
settings.confirm_delete = Confirm Deletion
settings.add_collaborator = Add New Collaborator
settings.add_collaborator_success = New collaborator has been added.
settings.remove_collaborator_success = Collaborator has been removed.
settings.user_is_org_member = User is organization member who cannot be added as a collaborator.
settings.add_webhook = Add Webhook
settings.hooks_desc = Webhooks allow external services to be notified when certain events happen on Gogs. When the specified events happen, we'll send a POST request to each of the URLs you provide. Learn more in our <a target="_blank" href="http://gogs.io/docs/features/webhook.html">Webhooks Guide</a>.
settings.hooks_desc = Webhooks allow external services to be notified when certain events happen on Gogs. When the specified events happen, we'll send a POST request to each of the URLs you provide. Learn more in our <a target="_blank" href="%s">Webhooks Guide</a>.
settings.githooks_desc = Git Hooks are powered by Git itself, you can edit files of supported hooks in the list below to apply custom operations.
settings.githook_edit_desc = If hook is not active, sample content will be presented. Leave content to be blank will disable this hook.
settings.githook_name = Hook Name
settings.githook_content = Hook Content
settings.update_githook = Update Hook
settings.remove_hook_success = Webhook has been removed.
settings.add_webhook_desc = Well send a <code>POST</code> request to the URL below with details of any subscribed events. You can also specify which data format you'd like to receive (JSON, <code>x-www-form-urlencoded</code>, <em>etc</em>). More information can be found in <a target="_blank" href="http://gogs.io/docs/features/webhook.html">Webhooks Guide</a>.
settings.add_webhook_desc = Well send a <code>POST</code> request to the URL below with details of any subscribed events. You can also specify which data format you'd like to receive (JSON, <code>x-www-form-urlencoded</code>, <em>etc</em>). More information can be found in <a target="_blank" href="%s">Webhooks Guide</a>.
settings.payload_url = Payload URL
settings.content_type = Content Type
settings.secret = Secret
@@ -286,11 +363,44 @@ settings.update_hook_success = Webhook has been updated.
settings.delete_webhook = Delete Webhook
settings.recent_deliveries = Recent Deliveries
settings.hook_type = Hook Type
settings.add_slack_hook_desc = Add <a href="http://slack.com">Slack</a> integration to your repository.
settings.add_slack_hook_desc = Add <a href="%s">Slack</a> integration to your repository.
settings.slack_token = Token
settings.slack_domain = Domain
settings.slack_channel = Channel
diff.browse_source = Browse Source
diff.parent = parent
diff.commit = commit
diff.data_not_available = Diff Data Not Available.
diff.show_diff_stats = Show Diff Stats
diff.stats_desc = <strong> %d changed files</strong> with <strong>%d additions</strong> and <strong>%d deletions</strong>
diff.bin = BIN
diff.view_file = View File
release.releases = Releases
release.new_release = New Release
release.draft = Draft
release.prerelease = Pre-Release
release.stable = Stable
release.edit = edit
release.ahead = <strong>%d</strong> commits to %s since this release
release.source_code = Source Code
release.tag_name = Tag name
release.target = Target
release.tag_helper = Choose an existing tag, or create a new tag on publish.
release.release_title = Release title
release.content_with_md = Content with <a href="%s">Markdown</a>
release.write = Write
release.preview = Preview
release.content_placeholder = Write some content
release.loading = Loading...
release.prerelease_desc = This is a pre-release
release.prerelease_helper = Well point out that this release is identified as non-production ready.
release.publish = Publish Release
release.save_draft = Save Draft
release.edit_release = Edit Release
release.tag_name_already_exist = Release with this tag name has already existed.
[org]
org_name_holder = Organization Name
org_name_helper = Great organization names are short and memorable.
@@ -316,11 +426,15 @@ settings.full_name = Full Name
settings.website = Website
settings.location = Location
settings.update_settings = Update Settings
settings.update_setting_success = Organization setting has been successfully updated.
settings.change_orgname = Organization Name Changed
settings.change_orgname_desc = Organization name has been changed, do you want to continue? This will affect all links relate to this organization.
settings.update_setting_success = Organization setting has been updated successfully.
settings.delete = Delete Organization
settings.delete_account = Delete This Organization
settings.delete_prompt = The operation will delete this organization permanently, and <strong>CANNOT</strong> be undone!
settings.confirm_delete_account = Confirm Deletion
settings.delete_org_title = Organization Deletion
settings.delete_org_desc = This organization is going to be deleted permanently, do you want to continue?
settings.hooks_desc = Add webhooks that will be triggered for <strong>all repositories</strong> under this organization.
members.public = Public
@@ -350,13 +464,16 @@ teams.members = Team Members
teams.update_settings = Update Settings
teams.delete_team = Delete This Team
teams.add_team_member = Add Team Member
teams.delete_team_success = Given team has been successfully deleted.
teams.delete_team_title = Team Deletion
teams.delete_team_desc = This team is going to be deleted, do you want to continue? Members of this team may lose access to some repositories.
teams.delete_team_success = Given team has been deleted successfully.
teams.read_permission_desc = This team grants <strong>Read</strong> access: members can view and clone the team's repositories.
teams.write_permission_desc = This team grants <strong>Write</strong> access: members can read from and push to the team's repositories.
teams.admin_permission_desc = This team grants <strong>Admin</strong> access: members can read from, push to, and add collaborators to the team's repositories.
teams.repositories = Team Repositories
teams.add_team_repository = Add Team Repository
teams.remove_repo = Remove
teams.add_nonexistent_repo = The repository you're trying to add does not exist, please create it first.
[admin]
dashboard = Dashboard
@@ -365,6 +482,7 @@ organizations = Organizations
repositories = Repositories
authentication = Authentications
config = Configuration
notices = System Notices
monitor = Monitoring
prev = Prev.
next = Next
@@ -377,7 +495,13 @@ dashboard.operation_name = Operation Name
dashboard.operation_switch = Switch
dashboard.operation_run = Run
dashboard.clean_unbind_oauth = Clean unbound OAuthes
dashboard.clean_unbind_oauth_success = All unbind OAuthes have been deleted successfully.
dashboard.delete_inactivate_accounts = Delete all inactive accounts
dashboard.delete_inactivate_accounts_success = All inactivate accounts have been deleted successfully.
dashboard.delete_repo_archives = Delete all repositories archives
dashboard.delete_repo_archives_success = All repositories archives have been deleted successfully.
dashboard.git_gc_repos = Do garbage collection on repositories
dashboard.git_gc_repos_success = All repositories have done garbage collection successfully.
dashboard.server_uptime = Server Uptime
dashboard.current_goroutine = Current Goroutines
dashboard.current_memory_usage = Current Memory Usage
@@ -419,13 +543,15 @@ users.edit = Edit
users.auth_source = Authorization Source
users.local = Local
users.auth_login_name = Authorization Login Name
users.update_profile_success = Account profile has been successfully updated.
users.update_profile_success = Account profile has been updated successfully.
users.edit_account = Edit Account
users.is_activated = This account is activated
users.is_admin = This account has administrator permissions
users.allow_git_hook = This account has permissions to create Git hooks
users.update_profile = Update Account Profile
users.delete_account = Delete This Account
users.still_own_repo = This account still have ownership of repository, you have to delete or transfer them first.
users.still_has_org = This account still have membership of organization, you have to left or delete them first.
orgs.org_manage_panel = Organization Manage Panel
orgs.name = Name
@@ -463,9 +589,11 @@ auths.enable_auto_register = Enable Auto Registration
auths.tips = Tips
auths.edit = Edit Authorization Setting
auths.activated = This authentication has activated
auths.update_success = Authorization setting has been successfully updated.
auths.update_success = Authorization setting has been updated successfully.
auths.update = Update Authorization Setting
auths.delete = Delete This Authorization
auths.delete_auth_title = Authorization Deletion
auths.delete_auth_desc = This authorization is going to be deleted, do you want to continue?
config.server_config = Server Configuration
config.app_name = Application Name
@@ -521,8 +649,6 @@ config.gc_interval_time = GC Interval Time
config.session_life_time = Session Life Time
config.https_only = HTTPS Only
config.cookie_life_time = Cookie Life Time
config.session_hash_function = Session ID Hash Function
config.session_hash_key = Session ID Hash Key
config.picture_config = Picture Configuration
config.picture_service = Picture Service
config.disable_gravatar = Disable Gravatar
@@ -540,11 +666,21 @@ monitor.desc = Description
monitor.start = Start Time
monitor.execute_time = Execution Time
notices.system_notice_list = System Notices
notices.type = Type
notices.type_1 = Repository
notices.desc = Description
notices.op = Op.
notices.delete_success = System notice has been deleted successfully.
[action]
create_repo = created repository <a href="/%s">%s</a>
commit_repo = pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a>
create_issue = opened issue <a href="/%s/issues/%s">%s#%s</a>
comment_issue = commented on issue <a href="/%s/issues/%s">%s#%s</a>
create_repo = created repository <a href="%s/%s">%s</a>
commit_repo = pushed to <a href="%s/%s/src/%s">%s</a> at <a href="%s/%s">%s</a>
create_issue = opened issue <a href="%s/%s/issues/%s">%s#%s</a>
comment_issue = commented on issue <a href="%s/%s/issues/%s">%s#%s</a>
transfer_repo = transfered repository <code>%s</code> to <a href="/%s%s">%s</a>
push_tag = pushed tag <a href="%s/%s/src/%s">%s</a> to <a href="%s/%s">%s</a>
compare_2_commits = View comparison for these 2 commits
[tool]
ago = ago

1234
conf/locale/locale_fr-CA.ini Normal file → Executable file

File diff suppressed because it is too large Load Diff

718
conf/locale/locale_lv-LV.ini Executable file
View File

@@ -0,0 +1,718 @@
app_desc=Viegli uzstādāms Git serviss, kas rakstīts valodā Go
home=Sākums
dashboard=Infopanelis
explore=Izpētīt
help=Palīdzība
sign_in=Pierakstīties
social_sign_in=Sociālā pieteikšanās: 2. solis <small>piesaistīt kontu</small>
sign_out=Izrakstīties
sign_up=Pieteikties
register=Reģistrēties
website=Mājas lapa
version=Versija
page=Lapa
template=Sagatave
language=Valoda
username=Lietotājvārds
email=E-pasts
password=Parole
re_type=Parole atkārtoti
captcha=Pārbaudes kods
repository=Repozitorijs
organization=Organizācija
mirror=Spogulis
new_repo=Jauns repozitorijs
new_migrate=Jauna migrācija
new_fork=Jauns atdalītais repozitorijs
new_org=Jauna organizācija
manage_org=Pārvaldīt organizācijas
admin_panel=Admin panelis
account_settings=Konta iestatījumi
settings=Iestatījumi
news_feed=Jaunumu plūsma
pull_requests=Izmaiņu pieprasījumi
issues=Problēmas
cancel=Atcelt
[install]
install=Instalācija
title=Instalācijas soļi pirmo reizi palaižot
requite_db_desc=Gogs ir nepieciešama MySQL, PostgreSQL vai SQLite3 datu bāze.
db_type=Datu bāzes veids
host=Resursdators
user=Lietotājs
password=Parole
db_name=Datu bāzes nosaukums
db_helper=Nepieciešams izmantot MySQL INNODB dzini ar rakstzīmju kopu utf8_general_ci.
ssl_mode=SSL režīms
path=Ceļš
sqlite_helper=SQLite 3 datu bāzes faila atrašanās vieta.
general_title=Gogs vispārīgie iestatījumi
repo_path=Repozitoriju glabāšanas vieta
repo_path_helper=Visi Git attālinātie repozitoriji tiks glabāti šajā direktorijā.
run_user=Izpildes lietotājs
run_user_helper=Lietotājam ir jābūt rakstīšanas tiesībām repozitorija saknes direktorijai un Gogs jābūt palaistam zem šī lietotāja.
domain=Domēns
domain_helper=Tas ietekmē SSH klonēšanas URL.
app_url=Lietotnes URL
app_url_helper=Tas ietekmē HTTP/HTTPS klonēšanas URL un e-pasta saturā izsūtītās saites.
email_title=E-pasta pakalpojuma iestatījumi (neobligāti)
smtp_host=SMTP resursdators
mailer_user=Sūtītāja e-pasta adrese
mailer_password=Sūtītāja parole
notify_title=Paziņojumu iestatījumi (neobligāti)
register_confirm=Iespējot reģistrēšanās apstiprināšanu
mail_notify=Iespējot e-pasta paziņojumus
admin_title=Admin konta iestatījumi
admin_name=Lietotājvārds
admin_password=Parole
confirm_password=Apstipriniet paroli
admin_email=E-pasts
install_gogs=Instalēt Gogs
test_git_failed=Kļūda pārbaudot 'git' komandu: %v
sqlite3_not_available=Jūsu versija neatbalsta SQLite3, lūdzu lejupielādējiet oficiālo bināro versiju no %s, NEVIS gobuild versiju.
invalid_db_setting=Datu bāzes iestatījums nav pareizs: %v
invalid_repo_path=Repozitorija atrašanās vieta ir nekorekta: %v
run_user_not_match=Izpildes lietotājs nav pašreizējais lietotājs: %s -> %s
save_config_failed=Neizdevās saglabāt konfigurāciju: %v
invalid_admin_setting=Nekorekts admin konta iestatījums: %v
install_success=Laipni lūdzam! Mēs priecājamies, ka Jūs izvēlaties Gogs, patīkamu lietošanu!
[home]
uname_holder=Lietotājvārds vai e-pasts
password_holder=Parole
switch_dashboard_context=Mainīt infopaneļa kontekstu
my_repos=Mani repozitoriji
collaborative_repos=Sadarbības repozitoriji
my_orgs=Manas organizācijas
my_mirrors=Mani spoguļi
[explore]
repos=Repozitoriji
[auth]
create_new_account=Izveidot jaunu kontu
register_hepler_msg=Jau ir konts? Pieraksties tagad!
social_register_hepler_msg=Jau ir konts? Sasaisti tagad!
disable_register_prompt=Atvainojiet, reģistrācija ir atspējota. Lūdzu, sazinieties ar vietnes administratoru.
disable_register_mail=Atvainojiet, reģistrācijas e-pasta apstiprināšana ir atspējota.
remember_me=Atcerēties mani
forgot_password=Aizmirsu paroli
forget_password=Aizmirsi paroli?
sign_up_now=Nepieciešams konts? Reģistrējies tagad.
confirmation_mail_sent_prompt=Jauns apstiprināšanas e-pasts ir nosūtīts uz <b>%s</b>, lūdzu, pārbaudies savu e-pasta kontu tuvāko %d stundu laikā, lai pabeigtu reģistrācijas procesu.
sign_in_email=Atvērt savu e-pasta kontu
active_your_account=Aktivizēt savu kontu
resent_limit_prompt=Atvainojiet, Jūs sūtījāt aktivizācijas e-pastu pārāk bieži. Lūdzu, gaidiet 3 minūtes.
has_unconfirmed_mail=Sveiki %s, Jums ir neapstiprināta e-pasta adrese (<b>%s</b>). Ja neesat saņēmis apstiprināšanas e-pastu vai Jums ir nepieciešams nosūtīt jaunu, lūdzu, nospiediet pogu, kas atrodas zemāk.
resend_mail=Nospiediet šeit, lai vēlreiz nosūtītu aktivizācijas e-pastu
email_not_associate=Šī e-pasta adrese nav saistīta ar Jūsu kontu.
send_reset_mail=Spiediet šeit, lai nosūtītu paroles maiņas vēstuli uz Jūsu e-pastu
reset_password=Atjaunot savu paroli
invalid_code=Atvainojiet, Jūsu apstiprināšanas kodam ir beidzies derīguma termiņš vai arī tas ir nepareizs.
reset_password_helper=Nospiediet šeit, lai atjaunotu paroli
password_too_short=Paroles garums nedrīkst būt mazāks par 6.
[form]
UserName=Lietotājvārds
RepoName=Repozitorija nosaukums
Email=E-pasta adrese
Password=Parole
Retype=Parole atkārtoti
SSHTitle=SSH atslēgas nosaukums
HttpsUrl=HTTPS URL
PayloadUrl=Vērtuma URL
TeamName=Komandas nosaukums
AuthName=Autorizācijas nosaukums
AdminEmail=Admin e-pasta adrese
require_error=` nedrīkst būt tukšs.`
alpha_dash_error=` drīkst saturēt tikai latīņu alfabēta burtus, ciparus vai domuzīmes (-_).`
alpha_dash_dot_error=` drīkst saturēt tikai latīņu alfabēta burtus, ciparus, domuzīmes (-_) vai punktu.`
min_size_error=` jabūt vismaz %s simbolu garumā.`
max_size_error=` jabūt ne mazāk kā %s simbolu garumā.`
email_error=` nav derīga e-pasta adrese.`
url_error=` nav korekts URL.`
unknown_error=Nezināma kļūda:
captcha_incorrect=Pārbaudes kods nesakrīt ar attēlā redzamo.
password_not_match=Parole un atkārtoti ievadītā parole nav vienādas.
username_been_taken=Lietotājvārds ir jau aizņemts.
repo_name_been_taken=Repozitorija vārds ir jau aizņemts.
org_name_been_taken=Organizācijas nosaukums ir jau aizņemts.
team_name_been_taken=Komandas nosaukums ir jau aizņemts.
email_been_used=E-pasta adrese jau tiek izmantota.
ssh_key_been_used=Publiskās atslēgas nosaukums jau tiek izmantos.
illegal_username=Jūsu lietotājvārds satur neatļautas rakstzīmes.
illegal_repo_name=Krātuves nosaukums satur neatļautas rakstzīmes.
illegal_org_name=Organizācijas nosaukums satur neatļautas rakstzīmes.
illegal_team_name=Grupas nosaukums satur neatļautas rakstzīmes.
username_password_incorrect=Lietotājvārds vai parole nav pareiza.
enterred_invalid_repo_name=Lūdzu, pārliecinieties, vai ievadītā repozitorija nosaukums ir pareizs.
enterred_invalid_owner_name=Lūdzu, pārliecinieties, vai ievadītā īpašnieka vārds ir pareizs.
enterred_invalid_password=Lūdzu pārliecinieties, vai Jūsu ievadītā parole ir pareiza.
user_not_exist=Šāds lietotājs neeksistē.
last_org_owner=Nav iespējams noņemt vienīgo komandas īpašnieku. Pirms tam ir nepieciešams norādīt jauno īpašnieku.
invalid_ssh_key=Atvainojiet, nav iespējams pārbaudīt Jūsu SSH atslēgu: %s
unable_verify_ssh_key=Nav iespējams pārbaudīt jūsu SSH atslēgu, bet tiks pieņemts, ka tā ir derīga, lūdzu, pārliecinieties par to pats.
auth_failed=Autentifikācija neizdevās: %v
still_own_repo=Jūsu esat vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai nomainīt to īpašnieku.
still_has_org=Jūsu esat vismaz vienas organizācijas biedrs, sākumā nepieciešams pamest vai izdzēst šo organizāciju.
org_still_own_repo=Šī organizācija ir vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai nomainīt to īpašnieku.
still_own_user=Šo autentifikāciju joprojām izmanto vismaz viens lietotājs, nepieciešams šiem lietotājiem nomainīt autentifikācijas veidu vai tos izdzēst.
target_branch_not_exist=Mērķa atzars neeksistē
[user]
change_avatar=Mainīt savu profila attēlu vietnē gravatar.com
change_custom_avatar=Mainīt savu profila attēlu iestatījumos
join_on=Pievienojās
repositories=Repozitoriji
activity=Publiskā aktivitāte
followers=Sekotāji
starred=Atzīmēti ar zvaigznīti
following=Seko
[settings]
profile=Profils
password=Parole
ssh_keys=SSH atslēgas
social=Sociālie konti
applications=Lietotnes
orgs=Organizācijas
delete=Dzēst kontu
uid=Lietotāja ID
public_profile=Publiskais profils
profile_desc=Jūsu e-pasta adrese ir publiska un tiks izmantota, lai nosūtītju Jums paziņojumus, kas saistīti ar Jūsu kontu vai darbībām veiktām caur šo mājas lapu.
full_name=Pilns vārds
website=Mājas lapa
location=Atrašanās vieta
update_profile=Mainīt profilu
update_profile_success=Jūsu profila dati ir veiksmīgi saglabāti.
change_username=Lietotāja vārds mainīts
change_username_desc=Lietotājvārds tiks mainīts, vai vēlaties turpināt? Tas ietekmēs visas saites, kas attiecas uz Jūsu kontu.
continue=Turpināt
cancel=Atcelt
enable_custom_avatar=Iespējot maināmu profila attēlu
enable_custom_avatar_helper=Iespējojiet šo, lai atslēgtu profilu attēlu ņemšanu no gravatar.com
choose_new_avatar=Izvēlēties jaunu profila attēlu
update_avatar=Saglabāt profila bildi
uploaded_avatar_not_a_image=Augšupielādētais fails nav attēls.
no_custom_avatar_available=Nav iespējams mainīt profila bildi.
update_avatar_success=Jūsu profila bilde tika veiksmīgi saglabāta.
change_password=Mainīt paroli
old_password=Pašreizējā parole
new_password=Jauna parole
password_incorrect=Ievadīta nepareiza pašreizējā parole.
change_password_success=Parole tika veiksmīgi nomainīta. Tagad jūs varat pieraksītites, izmantojot jauno paroli.
manage_ssh_keys=Pārvaldīt SSH atslēgas
add_key=Pievienot atslēgu
ssh_desc=Šis ir saraksts ar Jūsu kontam piesaistītajām SSH atslēgām. Dzēsiet visas, kuras Jūs neatpazīstat.
ssh_helper=<strong>Vajadzīga palīdzība?</strong> Apskatieties pamācību kā <a href="%s">ģenerēt SSH atslēgas</a> vai kā novērst <a href="%s">biežāk sastopamās SSH problēmas</a>.
add_new_key=Pievienot SSH atslēgu
key_name=Atslēgas nosaukums
key_content=Saturs
add_key_success=Pievienota jauna SSH atslēga!
delete_key=Dzēst
add_on=Pievienota
last_used=Pēdējo reizi izmantota
no_activity=Nav nesenas aktivitātes
manage_social=Pārvaldīt piesaistītos sociālos kontus
social_desc=Šeit tiek attēloti visi sociālie konti, kas ir piesaistīti Jūsu kontam. Dzēsiet visus, kurus Jūs neatpazīstat.
unbind=Atsaistīt
unbind_success=Sociālais konts tika atsaistīts.
manage_access_token=Pārvaldīt personīgos piekļuves talonus
generate_new_token=Ģenerēt jaunu talonu
tokens_desc=Talons tika uzģēnerēts, tagad varat to izmantot, lai piekļūtu Gogs API.
new_token_desc=Pašlaik visiem taloniem ir pilna piekļuve Jūsu kontam.
token_name=Talona nosaukums
generate_token=Ģenerēt talonu
generate_token_succees=Jauns piekļuves talons tika veiksmīgi uzģenerēts! Pārliecinieties, ka esat to nokopējis, jo to Jums vairāk nebūs iespēja izdarīt!
delete_token=Dzēst
delete_token_success=Jūsu personīgās piekļuves talons tika veiksmīgi izdzēsts! Neazimistiet veikt nepieciešamos labojumus savās lietojumprogrammās, kas to izmantoja.
delete_account=Dzēst savu kontu
delete_prompt=Šī darbība pilnībā izdzēsīs Jūsu kontu, kā arī tā ir <strong>NEATGRIEZENISKA</strong>!
confirm_delete_account=Apstiprināt dzēšanu
delete_account_title=Konta dzēšana
delete_account_desc=Šis konts tiks neatgriezeniski dzēsts, vai vēlaties turpināt?
[repo]
owner=Īpašnieks
repo_name=Repozitorija nosaukums
repo_name_helper=Labi repzotoriju nosaukumi ir īsi, tādi kurus viegli atcerēties un <strong>unikāli</strong>.
visibility=Redzamība
visiblity_helper=Šis repozitorijs ir <span class="label label-red label-radius">Privāts</span>
fork_repo=Atdalīt repozitoriju
fork_from=Atdalīt no
fork_visiblity_helper=Atdalītam repozitorijam nav iespējams nomainīt tā redzamību
repo_desc=Apraksts
repo_lang=Valoda
repo_lang_helper=Izvēlieties .gitignore failu
license=Licence
license_helper=Izvēlieties licences failu
init_readme=Inicializēt šo repozitoriju ar README.md failu
create_repo=Izveidot repozitoriju
default_branch=Noklusējuma atzars
mirror_interval=Spoguļošanas intervāls (stundās)
goget_meta=Go-Get metadati
goget_meta_helper=Šis repozitorijs saturēs <span class="label label-blue label-radius">Go-Get</span> metadatus
need_auth=Nepieciešama autorizācija
migrate_type=Migrācijas veids
migrate_type_helper=Šis repozitorijs būs <span class="label label-blue label-radius">Spoguļots</span>
migrate_repo=Migrēt repozitoriju
copy_link=Kopēt
click_to_copy=Kopēt uz starpliktuvi
copied=Kopēšana notikusi veiksmīgi
clone_helper=Nepieciešama palīdzība kā veikt klonēšana? Apmeklējiet <a target="_blank" href="%s">Palīdzība</a> lapu!
unwatch=Nevērot
watch=Vērot
unstar=Noņemt zvaigznīti
star=Pievienot zvaigznīti
fork=Atdalīts
no_desc=Nav apraksta
quick_guide=Īsa pamācība
clone_this_repo=Klonēt šo repozitoriju
create_new_repo_command=Izveidot jaunu repozitoriju komandrindā
push_exist_repo=Nosūtīt izmaiņas no komandrindas eksistējošam repozitorijam
branch=Atzars
tree=Koks
branch_and_tags=Atzari un birkas
branches=Atzari
tags=Birkas
issues=Problēmas
commits=Revīzijas
releases=Laidieni
file_raw=Neapstrādāts
file_history=Vēsture
file_view_raw=Rādīt neapstrādātu
commits.commits=Revīzijas
commits.search=Meklēt revīzijas
commits.find=Meklēt
commits.author=Autors
commits.message=Ziņojums
commits.date=Datums
commits.older=Vecāki
commits.newer=Jaunāki
settings=Iestatījumi
settings.options=Opcijas
settings.collaboration=Sadarbība
settings.hooks=Tīmekļa āķi
settings.githooks=Git āķi
settings.deploy_keys=Izvietot atslēgas
settings.basic_settings=Pamatiestatījumi
settings.danger_zone=Bīstamā zona
settings.site=Oficiālā mājas lapa
settings.update_settings=Mainīt iestatījumus
settings.change_reponame=Mainīts repozitorija nosaukums
settings.change_reponame_desc=Repozitorija nosaukums tiks mainīts, vai vēlaties turpināt? Tas ietekmēs visas saites, kas saistītas ar šo repozitoriju.
settings.transfer=Mainīt īpašnieku
settings.transfer_desc=Mainīt šī repozitorija īpašnieku uz citu lietotāju vai organizāciju, kurai Jums ir administratora tiesībs.
settings.new_owner_has_same_repo=Jaunajam īpašniekam jau ir repozitorijs ar šādu nosaukumu.
settings.delete=Dzēst šo repozitoriju
settings.delete_desc=Dzēšot repozitoriju, tā datus vairs nebūs iespējams atgūt. Pirms dzēšanas pārliecinieites vai patiešām vēlaties to darīt.
settings.transfer_notices=<p>- Jūs zaudēsiet piekļuvi repozitorijam, ja jaunais īpašnieks ir individuāls lietotājs.</p><p>- Jūs saglabāsiet piekļuvi repzitorijam, ja jaunais īpašneiks ir organizācija un Jūs esat viens no tās īpašniekiem.</p>
settings.update_settings_success=Repozitorija opcijas ir veiksmīgi saglabātas.
settings.transfer_owner=Jaunais īpašnieks
settings.make_transfer=Mainīt
settings.transfer_succeed=Repozitorija īpašnieks ir veiksmīgi nomainīts.
settings.confirm_delete=Apstiprināt dzēšanu
settings.add_collaborator=Pievienot jaunu līdzstrādnieku
settings.add_collaborator_success=Jauns līdzstrādnieks ir pievienots.
settings.remove_collaborator_success=Līdzstrādnieks tika noņemts.
settings.user_is_org_member=Lietotājs ir organizācijas biedrs, kas nevar tikt pievienots kā līdzstrādnieks.
settings.add_webhook=Pievienot tīmekļa āķi
settings.hooks_desc=Tīmekļa āķi ļauj paziņot ārējiem servisiem par noteiktiem notikomiem, kas notiek Git servisā. Kad iestāsies kāds notikums, katram ārējā servisa URL tiks nosūtīts POST pieprasījums. Lai uzzinātu sīkāk skatieties <a target="_blank" href="%s">Tīmekļa āķu rokasgrāmatā</a>.
settings.githooks_desc=Git āķus apstrādā pats Git. Jūs varat labot atbalsīto āku failus sarakstā zemāk, lai veiktu pielāgotas darbības.
settings.githook_edit_desc=Ja āķis nav aktīvs, tiks attēlots piemērs kā to izmantot. Atstājot āķa saturu tukšu, tas tiks atspējots.
settings.githook_name=Āķa nosaukums
settings.githook_content=Āķa saturs
settings.update_githook=Labot āķi
settings.remove_hook_success=Tīmekļa āķis tika noņemts.
settings.add_webhook_desc=Uz norādīto URL tiks nosūtīts <code>POST</code> pieprasījums ar notikuma datiem. Jūs varat norādīt arī datu formātu, kādā datus vēlaties saņemt (JSON, <code>x-www-form-urlencoded</code> <em>utt.</em>). Detalizētāku informāciju ir iespējams uzzināt <a target="_blank" href="%s">Tīmekļa āķu rokasgrāmatā</a>.
settings.payload_url=Vērtuma URL
settings.content_type=Satura tips
settings.secret=Noslēpums
settings.event_desc=Kādu notikumu rezultātā tiktu izsaukts tīmekļā āķis?
settings.event_push_only=Tikai izmaiņu nosūtīšanas notikumiem.
settings.active=Aktīvs
settings.active_helper=Tiks nosūtīti notikuma dati, kad nostrādās šis āķis.
settings.add_hook_success=Jauns tīmekļa āķis tika veiksmīgi pievienots.
settings.update_webhook=Mainīt tīmekļa āķi
settings.update_hook_success=Tīmekļa āķist tika veiksmīgi saglabāts.
settings.delete_webhook=Dzēst tīmekļa āķi
settings.recent_deliveries=Pēdējās piegādes
settings.hook_type=Āķa veids
settings.add_slack_hook_desc=PIevienot <a href="%s">Slack</a> integrāciju Jūsu repozitorijā.
settings.slack_token=Talons
settings.slack_domain=Domēns
settings.slack_channel=Kanāls
diff.browse_source=Pārlūkot izejas kodu
diff.parent=vecāks
diff.commit=revīzija
diff.data_not_available=Salīdzināšanas dati nav pieejami.
diff.show_diff_stats=Rādīt salīdzināšanas statistiku
diff.stats_desc=<strong>%d mainītis faili</strong> ar <strong>%d papildinājumiem</strong> un <strong>%d dzēšanām</strong>
diff.bin=BIN
diff.view_file=Parādīt failu
release.releases=Releases
release.new_release=New Release
release.draft=Draft
release.prerelease=Pre-Release
release.stable=Stable
release.edit=edit
release.ahead=<strong>%d</strong> commits to %s since this release
release.source_code=Source Code
release.tag_name=Tag name
release.target=Target
release.tag_helper=Choose an existing tag, or create a new tag on publish.
release.release_title=Release title
release.content_with_md=Content with <a href="%s">Markdown</a>
release.write=Write
release.preview=Preview
release.content_placeholder=Write some content
release.loading=Loading...
release.prerelease_desc=This is a pre-release
release.prerelease_helper=Well point out that this release is identified as non-production ready.
release.publish=Publish Release
release.save_draft=Save Draft
release.edit_release=Edit Release
release.tag_name_already_exist=Release with this tag name has already existed.
[org]
org_name_holder=Organizācijas nosaukums
org_name_helper=Labi organizāciju nosaukumi ir īsi un tādi, kurus viegli atcerēties.
org_email_helper=Uz organizācijas e-pastu tiks sūtītas visas notifikācias un apstiprinājumi.
create_org=Izveidot organizāciju
repo_updated=Atjaunināts
people=Personas
invite_someone=Uzaicināt kādu
teams=Komandas
lower_members=dalībnieki
lower_repositories=repozitoriji
create_new_team=Izveidot jaunu komandu
org_desc=Apraksts
team_name=Komandas nosaukums
team_desc=Apraksts
team_name_helper=Šo nosaukumu varēs izmantot, lai pieminētu komandu sarunās.
team_desc_helper=Komandas apraksts
team_permission_desc=Kādām tiesībām šai komandai būtu jābūt?
settings=Iestatījumi
settings.options=Opcijas
settings.full_name=Pilns vārds, uzvārds
settings.website=Mājas lapa
settings.location=Atrašanās vieta
settings.update_settings=Mainīt iestatījumus
settings.change_orgname=Mainīts organizācijas nosaukums
settings.change_orgname_desc=Organizācijas nosaukums tiks mainīts, vai vēlaties turpinat? Tas ietekmēs saites, kas attiecas uz šo organizāciju.
settings.update_setting_success=Organizācijas iestatījumi tika veiksmīgi saglabāti.
settings.delete=Dzēst organizāciju
settings.delete_account=Dzēst šo organizāciju
settings.delete_prompt=Šī darbība pilnībā dzēsīs šo organizāciju, kā arī tā ir <strong>NEATGRIEZENISKA</strong>!
settings.confirm_delete_account=Apstiprināt dzēšanu
settings.delete_org_title=Organizācijas dzēšana
settings.delete_org_desc=Šī organizācija tiks pilnībā izdzēsta, vai vēlaties turpināt?
settings.hooks_desc=Pievienot tīmekļa āķus, kas nostrādās <strong>visiem repozitorijiem</strong> šajā organizācijā.
members.public=Publisks
members.public_helper=padarīt privātu
members.private=Privāts
members.private_helper=padarīt publisku
members.owner=Īpašnieks
members.member=Biedri
members.conceal=Noslēpt
members.remove=Noņemt
members.leave=Atstāt
members.invite_desc=Sāciet rakstīt lietotājvārdu, lai uzaicinātu jaunu biedru organizācijā %s:
members.invite_now=Uzaicināt tagad
teams.join=Pievienoties
teams.leave=Atstāt
teams.read_access=Lasīšanas piekļuve
teams.read_access_helper=Komanda varēs skatīties un klonēt šīs organizācijas repozitorijus.
teams.write_access=Rakstīšanas piekļuve
teams.write_access_helper=Komanda varēs skatīties un klonēt, kā arī nosūtīt izmaiņas šīs organizācijas repozitorijiem.
teams.admin_access=Administratora piekļuve
teams.admin_access_helper=Šī komanda varēs veikt push/pull komandas tās repozitorijiem, kā arī tiem pievienot citus līdzstrādniekus.
teams.no_desc=Komandai nav apraksta
teams.settings=Iestatījumi
teams.owners_permission_desc=Īpašniekiem ir pilna piekļuve <strong>visiem repozitorijiem</strong> un ir organizācijas <strong>administratora tiesības</strong>.
teams.members=Komandas biedri
teams.update_settings=Saglabāt iestatījumus
teams.delete_team=Dzēst komandu
teams.add_team_member=Pievienot komandas biedru
teams.delete_team_title=Komandas dzēšana
teams.delete_team_desc=Komanda tiks dzēsta, vai vēlaties turpināt? Komandas biedri var zaudēt piekļuvi dažiem vai pat visiem repozitorijiem.
teams.delete_team_success=Komanda tika veiksmīgi izdzēsta.
teams.read_permission_desc=Šai komandai ir <strong>lasīšanas</strong> tiesības: dalībnieki var skatīties un klonēt komandas repozitorijus.
teams.write_permission_desc=Šai komandai ir <strong>rakstīšanas</strong> tiesības: dalībnieki var lasīt un nosūtīt izmaiņas repozitorijiem.
teams.admin_permission_desc=Šai komandai ir <strong>administratora</strong> tiesības: dalībnieki var lasīt, rakstīt un pievienot citus dalībniekus komandas repozitorijiem.
teams.repositories=Komandas repozitoriji
teams.add_team_repository=Pievienot komandas repozitoriju
teams.remove_repo=Noņemt
teams.add_nonexistent_repo=Repozitorijs, kuram Jūs mēģinat pievienot neeksistē, sākumā izveidojiet to.
[admin]
dashboard=Infopanelis
users=Lietotāji
organizations=Organizācijas
repositories=Repozitoriji
authentication=Autentifikācijas
config=Konfigurācija
notices=Sistēmas paziņojumi
monitor=Uzraudzība
prev=Iepr.
next=Tālāk
dashboard.statistic=Statistika
dashboard.operations=Darbības
dashboard.system_status=Sistēmas uzraudzības statuss
dashboard.statistic_info=Gogs datu bāze satur <b>%d</b> lietotājus, <b>%d</b> organizācijas, <b>%d</b> publiskās atslēgas, <b>%d</b> repozitorijus, <b>%d</b> vērošanas, <b>%d</b> atzīmētas zvaigznītes, <b>%d</b> darbības, <b>%d</b> piekļuves, <b>%d</b> problēmas, <b>%d</b> komentārus, <b>%d</b> sociālos kontus, <b>%d</b> sekošanas, <b>%d</b> spoguļošanas, <b>%d</b> izlaides, <b>%d</b> login sources, <b>%d</b> tīmekļa āķus, <b>%d</b> starpposmus, <b>%d</b> etiķetes, <b>%d</b> āķu uzdevumus, <b>%d</b> komandas, <b>%d</b> labotus uzdevumus, <b>%d</b> pielikumus.
dashboard.operation_name=Darbības nosaukums
dashboard.operation_switch=Pārslēgt
dashboard.operation_run=Palaist
dashboard.clean_unbind_oauth=Notīrīt nesaistītās OAuth biļetes
dashboard.clean_unbind_oauth_success=Visas nesaistītās OAuth biļetes tika veiksmīgi izdzēstas.
dashboard.delete_inactivate_accounts=Dzēst visus neaktīvos kontus
dashboard.delete_inactivate_accounts_success=Visi neaktīvie kotni tika veiksmīgi izdzēsti.
dashboard.delete_repo_archives=Dzēst visu repozitoriju arhīvus
dashboard.delete_repo_archives_success=Visu repozitoriju arhīvi tika veiksmīgi izdzēsti.
dashboard.git_gc_repos=Veikt repozitoriju datu sakārtošānu (git gc)
dashboard.git_gc_repos_success=Datu sakārtošana visiem repozitorijiem veiksmīgi pabeigta.
dashboard.server_uptime=Servera darbības laiks
dashboard.current_goroutine=Izmantotās Gorutīnas
dashboard.current_memory_usage=Pašreiz izmantotā atmiņa
dashboard.total_memory_allocated=Kopējā piešķirtā atmiņa
dashboard.memory_obtained=Iegūtā atmiņa
dashboard.pointer_lookup_times=Rādītāju meklēšanas reizes
dashboard.memory_allocate_times=Atmiņas piešķiršanas reizes
dashboard.memory_free_times=Atmiņas atbrīvošanas reizes
dashboard.current_heap_usage=Pašreizējā kaudzes izmantošana
dashboard.heap_memory_obtained=Iegūtā kaudzes atmiņa
dashboard.heap_memory_idle=Neizmantotā kaudzes atmiņa
dashboard.heap_memory_in_use=Izmantotā kaudzes atmiņa
dashboard.heap_memory_released=Atbrīvotā kaudzes atmiņa
dashboard.heap_objects=Kaudzes atmiņas objekti
dashboard.bootstrap_stack_usage=Izmantotais sāknēšanas steka lielums
dashboard.stack_memory_obtained=Iegūtā steka atmiņa
dashboard.mspan_structures_usage=Izmantotās MSpan struktūras
dashboard.mspan_structures_obtained=Iegūtās MSpan struktūras
dashboard.mcache_structures_usage=Izmantotās MCache struktūras
dashboard.mcache_structures_obtained=Iegūtās MCache struktūras
dashboard.profiling_bucket_hash_table_obtained=Iegūtā profilēšanas kausa jaucējtabula
dashboard.gc_metadata_obtained=Iegūtie GC metadati
dashboard.other_system_allocation_obtained=Iegūtās citas sistēmas sadales
dashboard.next_gc_recycle=Nākošā GC atkritne
dashboard.last_gc_time=Laiks kopš pēdējās GC
dashboard.total_gc_time=Kopējais GC izpildes laiks
dashboard.total_gc_pause=Kopējais GC izpildes laiks
dashboard.last_gc_pause=Pedējās GC izpildes laiks
dashboard.gc_times=GC reizes
users.user_manage_panel=Lietotāju pārvaldības panelis
users.new_account=Izveidot jaunu kontu
users.name=Vārds
users.activated=Aktivizēts
users.admin=Administrators
users.repos=Repozitoriji
users.created=Izveidots
users.edit=Labot
users.auth_source=Autorizācijas avots
users.local=Iebūvētā
users.auth_login_name=Autorizāciju, pietiekšanās vārds
users.update_profile_success=Konta profils tika veiksmīgi saglabāts.
users.edit_account=Labot kontu
users.is_activated=Konts ir aktivizēts
users.is_admin=Šim kontam ir administratora piekļuves tiesības
users.allow_git_hook=Šim kontam ir tiesības pievienot/labot Git āķus
users.update_profile=Mainīt konta profilu
users.delete_account=Dzēst šo kontu
users.still_own_repo=Šis konts ir vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai nomainīt to īpašnieku.
users.still_has_org=Šis konts ir vismaz vienas organizācijas biedrs, sākumā nepieciešams pamest vai izdzēst šo organizāciju.
orgs.org_manage_panel=Organizāciju pārvaldības panelis
orgs.name=Nosaukums
orgs.teams=Komandas
orgs.members=Dalībnieki
repos.repo_manage_panel=Repozitoriju pārvaldības panelis
repos.owner=Īpašnieks
repos.name=Vārds
repos.private=Privāts
repos.watches=Vērošana
repos.stars=Atzīmētās zvaigznītes
repos.issues=Problēmas
auths.auth_manage_panel=Autorizāciju pārvaldības panelis
auths.new=Pievienot jaunu autorizācijas veidu
auths.name=Nosaukums
auths.type=Veids
auths.enabled=Iespējota
auths.updated=Atjaunināta
auths.auth_type=Autorizācijas veids
auths.auth_name=Autorizācijas nosaukums
auths.domain=Domēns
auths.host=Resursdators
auths.port=Ports
auths.base_dn=Pamata DN
auths.attributes=Meklēšanas atribūti
auths.filter=Meklēšanas filtrs
auths.ms_ad_sa=MS Ad SA
auths.smtp_auth=SMTP autorizācijas veids
auths.smtphost=SMTP resursdators
auths.smtpport=SMTP ports
auths.enable_tls=Iespējot TLS šifrēšanu
auths.enable_auto_register=Iespējot automātisko reģistrāciju
auths.tips=Padomi
auths.edit=Labot autorizācijas iestatījumus
auths.activated=Autentifikācija ir aktivizēta
auths.update_success=Autorizācijas iestatījumi tika veiksmīgi saglabāti.
auths.update=Mainīt autorizācijas iestatījumus
auths.delete=Dzēst šo autorizāciju
auths.delete_auth_title=Autorizācijas dzēšana
auths.delete_auth_desc=Šī autorizācija tiks dzēsta, vai vēlaties turpināt?
config.server_config=Servera konfigurācija
config.app_name=Lietotnes nosaukums
config.app_ver=Lietotnes versija
config.app_url=Lietotnes URL
config.domain=Domēns
config.offline_mode=Bezsaistes režīms
config.disable_router_log=Atspējot maršrutētāja žurnalizēšanu
config.run_user=Izpildes lietotājs
config.run_mode=Izpildes režīms
config.repo_root_path=Repozitoriju glabāšanas vieta
config.static_file_root_path=Statisko failu atrašanās vieta
config.log_file_root_path=Žurnalizēšanas failu glabāšanas vieta
config.script_type=Skripta veids
config.reverse_auth_user=Reversā lietotāja autentifikācija
config.db_config=Datu bāzes konfigurācija
config.db_type=Veids
config.db_host=Resursdators
config.db_name=Nosaukums
config.db_user=Lietotājs
config.db_ssl_mode=SSL režīms
config.db_ssl_mode_helper=(tikai PostgreSQL datu bāzei)
config.db_path=Ceļš
config.db_path_helper=(tikai Sqlite3 datu bāzei)
config.service_config=Pakalpojuma konfigurācija
config.register_email_confirm=E-pasta reģistrēšanas apstiprināšana
config.disable_register=Atspējot jaunu lietotāju reģistrāciju
config.require_sign_in_view=Nepieciešama autorizācija
config.mail_notify=Pasta paziņojumi
config.enable_cache_avatar=Glabāt profila attēlus kešatmiņā
config.active_code_lives=Aktīvā koda ilgums
config.reset_password_code_lives=Paroles atiestatīšanas koda ilgums
config.webhook_config=Tīkla āķu konfigurācija
config.task_interval=Uzdevuma intervāls
config.deliver_timeout=Piegādes noildze
config.mailer_config=Sūtītāja konfigurācija
config.mailer_enabled=Iespējots
config.mailer_name=Nosaukums
config.mailer_host=Resursdators
config.mailer_user=Lietotājs
config.oauth_config=OAuth konfigurācija
config.oauth_enabled=Iespējota
config.cache_config=Kešatmiņas konfigurācija
config.cache_adapter=Kešatmiņas adapteris
config.cache_interval=Kešatmiņas intervāls
config.cache_conn=Kešatmiņas pieslēguma parametri
config.session_config=Sesijas konfigurācja
config.session_provider=Sesijas nodrošinātājs
config.provider_config=Pakalpojumu sniedzēja konfigurācija
config.cookie_name=Sīkdatnes nosaukums
config.enable_set_cookie=Ļaut izmantot sīkdatnes
config.gc_interval_time=GC laika intervāls
config.session_life_time=Sesijas ilgums
config.https_only=Tikai HTTPS
config.cookie_life_time=Sīkdatņu glabāšanas ilgums
config.picture_config=Attēlu konfigurācija
config.picture_service=Lokāli attēli
config.disable_gravatar=Atspējot Gravatar
config.log_config=Žurnalizēšanas konfigurācija
config.log_mode=Žurnalizēšanas veids
monitor.cron=Cron uzdevumi
monitor.name=Nosaukums
monitor.schedule=Grafiks
monitor.next=Nākošās izpildes laiks
monitor.previous=Pēdējās izpildes laiks
monitor.execute_times=Izpilžu skaits
monitor.process=Darbojošies procesi
monitor.desc=Apraksts
monitor.start=Sākuma laiks
monitor.execute_time=Izpildes laiks
notices.system_notice_list=Sistēmas paziņojumi
notices.type=Veids
notices.type_1=Repozitorijs
notices.desc=Apraksts
notices.op=Op.
notices.delete_success=Sistēmas paziņojums tika veiksmīgi izdzēsts.
[action]
create_repo=izveidoja repozitoriju <a href="%s/%s">%s</a>
commit_repo=veica izmaiņu nosūtīšanu atzaram <a href="%s/%s/src/%s">%s</a> repozitorijā <a href="%s/%s">%s</a>
create_issue=reģistrēja problēmu <a href="%s/%s/issues/%s">%s#%s</a>
comment_issue=pievienoja komentāru problēmai <a href="%s/%s/issues/%s">%s#%s</a>
transfer_repo=mainīja repozitorija <code>%s</code> īpašnieku uz <a href="/%s%s">%s</a>
push_tag=pievienoja birku <a href="%s/%s/src/%s">%s</a> repozitorijam <a href="%s/%s">%s</a>
compare_2_commits=Veikt salīdzināšanu starp šīm 2 revīzijām
[tool]
ago=atpakaļ
from_now=no šī brīža
now=tagad
1s=1 sekundi %s
1m=1 minūti %s
1h=1 stundu %s
1d=1 dienu %s
1w=1 nedēļu %s
1mon=1 mēnesi %s
1y=1 gadu %s
seconds=%d sekundes %s
minutes=%d minūtes %s
hours=%d stundas %s
days=%d dienas %s
weeks=%d nedēļas %s
months=%d mēneši %s
years=%d gadi %s
raw_seconds=sekundes
raw_minutes=minūtes

718
conf/locale/locale_nl-NL.ini Executable file
View File

@@ -0,0 +1,718 @@
app_desc=Een pijnloze self-hosted Git-dienst geschreven in Go
home=Huis
dashboard=Dashboard
explore=Verkennen
help=Help
sign_in=Inloggen
social_sign_in=Social netwerk inlog: tweede stap <small>account koppelen</small>
sign_out=Afmelden
sign_up=Aanmelden
register=Registreer
website=Website
version=Versie
page=Pagina
template=Sjabloon
language=Taal
username=Gebruikersnaam
email=E-mail
password=Wachttwoord
re_type=Verificatie
captcha=CAPTCHA
repository=Repositorie
organization=Organisatie
mirror=Spiegel
new_repo=Nieuwe repositorie
new_migrate=Nieuwe migratie
new_fork=Nieuwe vork Repository
new_org=Nieuwe organisatie
manage_org=Beheer organisaties
admin_panel=Adminpaneel
account_settings=Accountinstellingen
settings=Instellingen
news_feed=Nieuwsfeed
pull_requests=Pull-aanvragen
issues=Kwesties
cancel=Annuleer
[install]
install=Installatie
title=Installatiestappen voor de eerste keer opstarten
requite_db_desc=Om Gogs te gebruiken is MySQL, PostgreSQL of SQLite3 vereist (SQLite3 is beschikbaar in de officiële versie).
db_type=Database-type
host=Host
user=Gebruikersnaam
password=Wachtwoord
db_name=Database naam
db_helper=Gebruik InnoDB engine met utf8_general_ci karakterset voor MySQL.
ssl_mode=SSL-modus
path=Pad
sqlite_helper=Het pad naar de SQLite3 database.
general_title=Algemene instellingen van Gogs
repo_path=Repositories basis directorie
repo_path_helper=Alle remote Git repositories worden in deze directorie opgeslagen
run_user=Uitvoerende gebruikersnaam
run_user_helper=Deze gebruiker moet toegang hebben tot de git repositorie directorie en moet Gogs kunnen starten
domain=Domein
domain_helper=Dit heeft invloed op de SSH kloon URLs
app_url=Applicatie URL
app_url_helper=Dit heeft invloed op de HTTP/HTTPS kloon urls en de urls die in de email worden gebruikt
email_title=Email service instellingen (Optioneel)
smtp_host=SMTP host
mailer_user=Afzender e-mail / gebruikersnaam
mailer_password=Wachtwoord
notify_title=Notificatie-instelligen (optioneel)
register_confirm=Activeer registratie emails
mail_notify=Activeer e-mailnotificaties
admin_title=Instellingen beheerdersaccount
admin_name=Gebruikersnaam
admin_password=Wachtwoord
confirm_password=Verifieer wachtwoord
admin_email=E-mailadres
install_gogs=Installeer Gogs
test_git_failed=Git test niet gelukt: 'git' commando %v
sqlite3_not_available=Uw versie biedt geen ondersteuning voor SQLite3, download de officiële binaire versie van %s, niet de gobuild versie.
invalid_db_setting=Uw database instellingen zijn niet correct: %v
invalid_repo_path=Repositorie basis pad is niet correct: %v
run_user_not_match=De uitvoerende gebruiker is niet de huidig gebruiker: %s -> %s
save_config_failed=Kan de configuratie niet opslaan: %v
invalid_admin_setting=Uw admin-instellingen zijn niet geldig: %v
install_success=Welkom! Wij zijn veheugd dat u voor Gogs heeft gekozen, veel plezier en tot ziens
[home]
uname_holder=Gebruikersnaam of e-mail
password_holder=Wachtwoord
switch_dashboard_context=Wissel voorpaginacontext
my_repos=Mijn repositories
collaborative_repos=Gedeelde repositories
my_orgs=Mijn organisaties
my_mirrors=Mijn mirrors
[explore]
repos=Repositories
[auth]
create_new_account=Maak nieuw account aan
register_hepler_msg=Heeft u al een account? Meld u nu aan!
social_register_hepler_msg=Heeft u al een account? Koppel nu!
disable_register_prompt=Sorry, registratie is uitgeschakeld. Neem contact op met de beheerder van deze site.
disable_register_mail=Sorry, bevestiging van registratie per e-mail is uitgeschakeld.
remember_me=Onthoud mij
forgot_password=Wachtwoord vergeten
forget_password=Wachtwoord vergeten?
sign_up_now=Een account nodig? Meld u nu aan.
confirmation_mail_sent_prompt=Een bevestigingsemail is gestuurd naar <b>%s</b>, Bevestig u aanvraag binnen %d uren om uw registratie te voltooien.
sign_in_email=Meld u aan met uw e-mailadres
active_your_account=Activeer uw account
resent_limit_prompt=Sorry, u heeft te snel na elkaar een aanvraag gedaan voor een activatie mail. Wacht drie minuten voor uw volgende aanvraag.
has_unconfirmed_mail=Beste %s, u heeft een onbevestigde e-mailadres (<b>%s</b>). Als u nog geen bevestiging per e-mail heeft ontvangen, of u een nieuwe aanvraag wilt doen, klik dan op de onderstaande knop.
resend_mail=Klik hier om uw activatie mail nog een keer te verzenden
email_not_associate=Dit e-mailadres is niet gekoppeld aan een account.
send_reset_mail=Klik hier om uw wachtwoord reset mail (nogmaals) te versturen
reset_password=Reset uw wachtwoord
invalid_code=Sorry, uw bevestigingscode is verlopen of niet meer geldig.
reset_password_helper=Klik hier om uw wachtwoord opnieuw in te stellen.
password_too_short=De lengte van uw wachtwoord moet minimaal zes karakters zijn.
[form]
UserName=Gebruikersnaam
RepoName=Repositorie naam
Email=e-mailadres
Password=Wachtwoord
Retype=Verifieer wachtwoord
SSHTitle=SSH sleutel naam
HttpsUrl=HTTPS-URL
PayloadUrl=Nettolading URL
TeamName=Team naam
AuthName=Autorisatienaam
AdminEmail=E-mail beheerder
require_error=kan niet leeg zijn.
alpha_dash_error=moet een valide alfanumeriek of dash(-_) karakter zijn.
alpha_dash_dot_error=moet een valide alfanumeriek, dash(-_) of (.) punt karakter zijn.
min_size_error=moet minimaal %s karakters bevatten.
max_size_error=mag maximaal %s karakters bevatten.
email_error=is niet een valide e-mail adres.
url_error=is niet een valide URL.
unknown_error=Onbekende fout:
captcha_incorrect=Captcha komt niet overeen.
password_not_match=Wachtwoord en verificatie wachtwoord komen niet overeen.
username_been_taken=Gebruikersnaam is al in gebruik.
repo_name_been_taken=Repositorie naam is al in gebruik.
org_name_been_taken=Organisatie naam is al in gebruik.
team_name_been_taken=Team naam is al in gebruik.
email_been_used=e-mailadres is al in gebruik.
ssh_key_been_used=Openbare sleutel naam is al in gebruik.
illegal_username=Gebruikersnaam bevat illegale karakters.
illegal_repo_name=Repositorie naam bevat illegale karakters.
illegal_org_name=Organisatie naam bevat illegale karakters.
illegal_team_name=Team naam bevat illegale karakters.
username_password_incorrect=Gebruikersnaam of wachtwoord is niet correct.
enterred_invalid_repo_name=U heeft een onjuiste repositorie naam ingevoerd.
enterred_invalid_owner_name=U heeft een onjuiste eigenaar ingevoerd.
enterred_invalid_password=U heeft een onjuiste wachtwoord ingevoerd.
user_not_exist=Gegeven gebruiker bestaat niet.
last_org_owner=De gebruiker die u probeert te verwijderen is het enige lid (eigenaar) van dit team. U moet eerst nieuwe lid (eigenaar) aanstellen.
invalid_ssh_key=Sorry, we zijn niet in staat om uw SSH-sleutel te verifiëren: %s
unable_verify_ssh_key=Gogs kan niet controleren of uw SSH-sleutel, maar wij gaan ervan uit dat is geldig, zorg ervoor dat uzelf.
auth_failed=Verificatie mislukt: %v
still_own_repo=Uw account heeft nog een eigendom op een repositorie. U moet deze eerst verwijderen of overdragen.
still_has_org=Uw account nog steeds lidmaatschap van organisatie, u hebt naar links of hen eerst verwijderen.
org_still_own_repo=De organisatie heeft nog eigendomen op repositories. U moet deze eerst verwijderen of overdragen.
still_own_user=Deze authenticatie methode wordt nog gebruikt door sommige gebruikers. U moet hen eerst verplaatsen of verwijderen.
target_branch_not_exist=Doel branch bestaat niet
[user]
change_avatar=Verander uw avatar op Gravatar.com
change_custom_avatar=Change your avatar in settings
join_on=Aangemeld op
repositories=repositories
activity=Openbare activiteit
followers=Volgers
starred=Sterren
following=Volgt
[settings]
profile=Profiel
password=Wachtwoord
ssh_keys=SSH-sleutels
social=Sociale netwerk-accounts
applications=Toepassingen
orgs=Organisaties
delete=Verwijder account
uid=uid
public_profile=Openbaar profiel
profile_desc=Uw e-mailadres is openbaar zichtbaar en zal gebruikt worden gebruikt voor alle account gerlateerde berichtgevingen en web bewerking gemaakt via de website.
full_name=Volledige naam
website=Website
location=Locatie
update_profile=Profile bijwerken
update_profile_success=Uw profiel is succesvol bijgewerkt.
change_username=Username veranderd
change_username_desc=Gebruikersnaam is gewijzigd. Wilt u doorgaan? Dit zal gevolgen hebben voor alle koppelingen die betrekking hebben op uw account.
continue=Doorgaan
cancel=Annuleren
enable_custom_avatar=Enable Custom Avatar
enable_custom_avatar_helper=Enable this to disable fetch from Gravatar
choose_new_avatar=Choose new avatar
update_avatar=Update Avatar Setting
uploaded_avatar_not_a_image=Uploaded file is not a image.
no_custom_avatar_available=No custom avatar available, cannot enable it.
update_avatar_success=Your avatar setting has been updated successfully.
change_password=Verander wachtwoord
old_password=Huidige wachtwoord
new_password=Nieuw wachtwoord
password_incorrect=Huidig wachtwoord is niet correct.
change_password_success=Wachtwoord is succesvol gewijzigd. U kunt nu met uw nieuwe wachtwoord inloggen.
manage_ssh_keys=Beheer SSH sleutels
add_key=Sleutel toevoegen
ssh_desc=Dit is een lijst van alle SSH sleutels die gekoppeld zijn aan uw account. Verwijder alle sleutels die u niet herkent.
ssh_helper=<strong>De behoeftehulp van?</strong> Check out onze gids voor het <a href="%s"> genereren van SSH sleutels</a> of <a href="%s"> algemene SSH</a> problemen.
add_new_key=SSH sleutel toevoegen
key_name=Sleutel naam
key_content=Inhoud
add_key_success=Nieuwe SSH sleutel is toegevoegd!
delete_key=Verwijder
add_on=Toegevoegd op
last_used=Laatst gebruikt op
no_activity=Geen recente activiteiten
manage_social=Beheer gekoppelde sociale accounts
social_desc=Dit is een lijst van de bijbehorende sociale accounts koppelingen, Verwijder eventueel koppelingen die u niet herkent.
unbind=Loskoppelen
unbind_success=Sociaal account is ontkoppeld.
manage_access_token=Persoonlijke toegangstokens beheren
generate_new_token=Nieuwe Token genereren
tokens_desc=Tokens u hebt gegenereerd die kunnen worden gebruikt voor toegang tot de API Gogs.
new_token_desc=Zoals voor nu, moet elke token zal hebben volledige toegang tot uw account.
token_name=Symbolische naam
generate_token=Token genereren
generate_token_succees=Nieuwe toegangstoken is met succes gegenereerd! Zorg ervoor dat uw nieuwe persoonlijke toegangstoken nu kopiëren. U zal niet zitten kundig voor zien het weer!
delete_token=Verwijderen
delete_token_success=Persoonlijke toegangstoken is met succes verwijderd! Vergeet niet uw toepassingen ook wilt bijwerken.
delete_account=Verwijder uw account
delete_prompt=Deze handeling zal uw account definitief verwijderen, u kunt dit <strong> NIET </strong> terug draaien!
confirm_delete_account=Bevestig verwijdering
delete_account_title=Account verwijderen
delete_account_desc=Dit account zal permanent worden verwijderd. Wilt u doorgaan?
[repo]
owner=Eigenaar
repo_name=Repositorie naam
repo_name_helper=Een goede repositorie naam is kort, memorabel en <strong>uniek</strong>.
visibility=Zichtbaarheid
visiblity_helper=Deze repositorie is <span class="label label-red label-radius">prive</span>
fork_repo=Vork Repository
fork_from=Afsplitsing van
fork_visiblity_helper=Gevorkte repository wijzigen zijn bereik potentiële kopers niet
repo_desc=Omschrijving
repo_lang=Taal
repo_lang_helper=Selecteer een .gitignore bestand
license=Licentie
license_helper=Selecteer een licentie bestand
init_readme=Initialiseer deze repositorie met een README.md
create_repo=Nieuwe Repositorie
default_branch=Standaard branch
mirror_interval=Mirror interval(uur)
goget_meta=Go-Get Meta
goget_meta_helper=Deze repositorie is nu beschikbaar voor <span class="label label-blue label-radius">Go-Get</span>
need_auth=Autorisatie vereist
migrate_type=Migratie type
migrate_type_helper=Deze repositorie zal een <span class="label label-blue label-radius">mirror</span> worden
migrate_repo=Migreer repositorie
copy_link=Kopieer
click_to_copy=Kopieer link naar plakbord
copied=Gekopieerd
clone_helper=De behoeftehulp van klonen? Bezoek <a target="_blank" href="%s"> helpen</a>!
unwatch=Negeren
watch=Volgen
unstar=Ontster
star=Ster
fork=Vork
no_desc=Geen omschrijving
quick_guide=Snelstart gids
clone_this_repo=Kloon deze repositorie
create_new_repo_command=Maak een nieuwe repositorie aan vanaf de console
push_exist_repo=Push een bestaande repositorie vanaf de console
branch=Aftakking
tree=Boom
branch_and_tags=Aftakkingen & labels
branches=Aftakkingen
tags=Labels
issues=Kwesties
commits=Commits
releases=Publicaties
file_raw=Ruwe
file_history=Geschiedenis
file_view_raw=Weergave ruwe
commits.commits=Commits
commits.search=Zoeken
commits.find=zoek
commits.author=Auteur
commits.message=Bericht
commits.date=Datum
commits.older=Ouder
commits.newer=Nieuwer
settings=Instellingen
settings.options=Opties
settings.collaboration=Samenwerking
settings.hooks=Webhooks
settings.githooks=Git haken
settings.deploy_keys=Installeer sleutels
settings.basic_settings=Basis instellingen
settings.danger_zone=Gevaren zone
settings.site=Officiële site
settings.update_settings=Instellingen bewerken
settings.change_reponame=Repositorienaam aangepast
settings.change_reponame_desc=De repositorienaam is veranderd. Wilt u doorgaan? Dit zal gevolgen hebben voor alle koppelingen die betrekking hebben op deze repositorie.
settings.transfer=Eigendom overdragen
settings.transfer_desc=Draag deze repo over aan een andere gebruiker of een organisatie waar u beheerders rechten heeft.
settings.new_owner_has_same_repo=De nieuwe eigenaar heeft al een repositorie met deze naam
settings.delete=Verwijder deze repositorie
settings.delete_desc=Als u eenmaal een repositorie verwijderd is er geen weg terug. Gelieve zeker te zijn van uw acties.
settings.transfer_notices=<p> - U kan uw toegang verliezen als de nieuwe eigenaar een individuele gebruiker is</p> <p> - . . U zal uw toegang behouden als de nieuwe eigenaar een organisatie is en u één van de eigenaren bent</p>
settings.update_settings_success=Repositorie instellingen zijn succesvol bijgewerkt.
settings.transfer_owner=Nieuwe eigenaar
settings.make_transfer=Maak overdracht
settings.transfer_succeed=Eigendom repositorie succesvol overgedragen
settings.confirm_delete=Bevestig verwijdering
settings.add_collaborator=Nieuwe medewerker toevoegen
settings.add_collaborator_success=medewerker is toegevoegd.
settings.remove_collaborator_success=medewerker is verwijderd.
settings.user_is_org_member=Gebruiker is lid van de organisatie die als een medewerker kan niet worden toegevoegd.
settings.add_webhook=Webhook toevoegen
settings.hooks_desc=Webhooks dat de externe diensten om kennisgevingen te ontvangen wanneer bepaalde gebeurtenissen op Gogs plaatsvinden. Wanneer de opgegeven gebeurtenissen plaatsvinden, sturen we een POST-aanvraag naar elk van de URL's die u opgeeft. Meer informatie vindt u in onze <a target="_blank" href="%s"> Webhooks gids</a>.
settings.githooks_desc=Git haken worden aangedreven door Git zelf, u kunt bestanden van ondersteunde haken in de lijst hieronder om aangepaste acties van toepassing bewerken.
settings.githook_edit_desc=Als haak niet actief is, zal monster inhoud worden gepresenteerd. Verlof inhoud leeg zal deze haak uitschakelen.
settings.githook_name=Haak naam
settings.githook_content=Haak inhoud
settings.update_githook=Haak bijwerken
settings.remove_hook_success=Webhook is verwijderd.
settings.add_webhook_desc=We sturen een <code>POST</code>-aanvraag naar de onderstaande URL met details voor alle geplaatste gebeurtenissen. U kunt ook opgeven welke gegevensindeling u wenst te ontvangen (JSON, <code>x-1-800-www-Dell-vorm-urlencoded</code>, <em>enz</em>.). Meer informatie kan gevonden worden in <a target="_blank" href="%s"> Webhooks gids</a>.
settings.payload_url=Nettolading URL
settings.content_type=Content type
settings.secret=Geheim
settings.event_desc=Bij welke gebeurtenissen wilt u dat deze webhook getriggerd wordt?
settings.event_push_only=Alleen bij de <code>push</code> event.
settings.active=Actief
settings.active_helper=We zullen details van de gebeurtenissen af leveren wanneer deze webhook wordt geactiveerd.
settings.add_hook_success=Nieuwe webhook toegevoegd.
settings.update_webhook=Bewerk webhook
settings.update_hook_success=Webhook is bijgewerkt.
settings.delete_webhook=Webhook verwijderen
settings.recent_deliveries=Recente bezorgingen
settings.hook_type=Type hook
settings.add_slack_hook_desc=<a href="%s"> toegestane vertraging</a> integratie toevoegen aan uw repository.
settings.slack_token=Slack token
settings.slack_domain=Slack domein
settings.slack_channel=Slack kanaal
diff.browse_source=Bladeren bron
diff.parent=bovenliggende
diff.commit=commit
diff.data_not_available=Diff gegevens niet beschikbaar.
diff.show_diff_stats=Toon Diff Stats
diff.stats_desc=<strong>%d gewijzigde bestanden</strong> met <strong>toevoegingen van %d</strong> en <strong>%d verwijderingen</strong>
diff.bin=BIN
diff.view_file=Bestand weergeven
release.releases=Releases
release.new_release=New Release
release.draft=Draft
release.prerelease=Pre-Release
release.stable=Stable
release.edit=edit
release.ahead=<strong>%d</strong> commits to %s since this release
release.source_code=Source Code
release.tag_name=Tag name
release.target=Target
release.tag_helper=Choose an existing tag, or create a new tag on publish.
release.release_title=Release title
release.content_with_md=Content with <a href="%s">Markdown</a>
release.write=Write
release.preview=Preview
release.content_placeholder=Write some content
release.loading=Loading...
release.prerelease_desc=This is a pre-release
release.prerelease_helper=Well point out that this release is identified as non-production ready.
release.publish=Publish Release
release.save_draft=Save Draft
release.edit_release=Edit Release
release.tag_name_already_exist=Release with this tag name has already existed.
[org]
org_name_holder=Organisatienaam
org_name_helper=Een goede organisatienaam is kort en memorabel.
org_email_helper=Alle notificaties en bevestigingen worden op het e-mailadres van de organisatie ontvangen.
create_org=Nieuwe organisatie aanmaken
repo_updated=Geupdate
people=Mensen
invite_someone=Iemand uitnodigen
teams=Teams
lower_members=leden
lower_repositories=repositories
create_new_team=Nieuw team aanmaken
org_desc=Omschrijving
team_name=Teamnaam
team_desc=Omschrijving
team_name_helper=U gebruikt deze naam om dit team te vermelden in conversaties.
team_desc_helper=Waar gaat dit team doen?
team_permission_desc=Welke privileges zou dit team moeten hebben?
settings=Instellingen
settings.options=Opties
settings.full_name=Volledige naam
settings.website=Website
settings.location=Locatie
settings.update_settings=Instellingen bijwerken
settings.change_orgname=Organisatie naam veranderd
settings.change_orgname_desc=De naam van de organisatie is veranderd, wilt u doorgaan? Dit zal gevolgen hebben voor alle koppelingen die betrekking hebben op deze organisatie.
settings.update_setting_success=Organisatie instellingen zijn succesvol bijgewerkt.
settings.delete=Verwijder organisatie
settings.delete_account=Verwijder deze organisatie
settings.delete_prompt=Deze actie zal de origanisatie permanent verwijderen. U kunt dit <strong>NIET</strong> terug draaien!
settings.confirm_delete_account=Bevestig verwijdering
settings.delete_org_title=Verwijderen organsiatie
settings.delete_org_desc=Deze organisatie zal permanent worden verwijderd, wilt u doorgaan?
settings.hooks_desc=Een webhook toevoegen die door <strong>alle repositories</strong> in deze organisatie getriggerd kan worden.
members.public=Openbaar
members.public_helper=maak prive
members.private=Prive
members.private_helper=maak openbaar
members.owner=Eigenaar
members.member=Lid
members.conceal=Verbergen
members.remove=Verwijderen
members.leave=Verlaat
members.invite_desc=Begin met het typen van een gebruikersnaam om een nieuw lid aan %s uit te nodigen:
members.invite_now=Nu uitnodigen
teams.join=Lid worden
teams.leave=Vertlaat
teams.read_access=Leestoegang
teams.read_access_helper=Dit team is in staat om zijn repositories te bekijken en te klonen.
teams.write_access=Schrijf toegang
teams.write_access_helper=Dit team is in staat om zijn repositories te bekijken en push aanvragen te verwerken.
teams.admin_access=Beheerder toegang
teams.admin_access_helper=Dit team is in staat om push & pull aanvragen te verwerken en om nieuwe medewerkers toe te voegen.
teams.no_desc=Dit team heeft geen omschrijving
teams.settings=Instellingen
teams.owners_permission_desc=Eigenaren hebben volledige toegang tot <strong>alle repositories</strong> en hebben <strong>beheerder rechten</strong> over de organisatie.
teams.members=Team leden
teams.update_settings=Instellingen bijwerken
teams.delete_team=Verwijder deze team
teams.add_team_member=Nieuwe team lid aanmaken
teams.delete_team_title=Team verwijderen
teams.delete_team_desc=Dit team zal worden verwijderd. De leden van dit team zullen toegang tot alle repositories van het team verliezen. Wilt u doorgaan?
teams.delete_team_success=Gekozen team is succesvol verwijderd.
teams.read_permission_desc=Dit team heeft <strong>Lees</strong> rechten : leden kunnen repositories lezen en klonen.
teams.write_permission_desc=Dit team heeft <strong>Schrijf</strong> rechten : leden kunnen repositories lezen en push aanvragen verwerken.
teams.admin_permission_desc=Dit team heeft <strong>Beheerders</strong> rechten : leden kunnen repositories lezen en push aanvragen verwerken en medewerkers toevoegen.
teams.repositories=Teamrepositories
teams.add_team_repository=Nieuwe teamrepositorie aanmaken
teams.remove_repo=Verwijder
teams.add_nonexistent_repo=De opslagplaats die u probeert toe te voegen niet bestaat, kunt u het eerst aanmaken.
[admin]
dashboard=Dashboard
users=Gebruikers
organizations=Orgranisaties
repositories=Repositories
authentication=Autenticaties
config=Configuratie
notices=Systeem aankondigingen
monitor=Bijhouden
prev=Vorige
next=Volgende
dashboard.statistic=Statistieken
dashboard.operations=Bewerkingen
dashboard.system_status=Status Systeemmonitor
dashboard.statistic_info=Gogs database heeft <b>%d</b> gebruikers, <b>%d</b> organisaties, <b>%d</b> openbare sleutels, <b>%d</b> repositories, <b>%d</b> volgers, <b>%d</b> sterren, <b>%d</b> acties, <b>%d</b> participanten, <b>%d</b> issues, <b>%d</b> reacties, <b>%d</b> sociale accounten, <b>%d</b> volgers, <b>%d</b> mirrors, <b>%d</b> publicaties, <b>%d</b> login bronnen, <b>%d</b> webhooks, <b>%d</b> mijlpalen, <b>%d</b> labels, <b>%d</b> hook taken, <b>%d</b> teams, <b>%d</b> bijgewerkte taken, <b>%d</b> bijlagen.
dashboard.operation_name=Bewerking naam
dashboard.operation_switch=Omschakelen
dashboard.operation_run=Uitvoeren
dashboard.clean_unbind_oauth=Clean unbound OAuths
dashboard.clean_unbind_oauth_success=Alle OAuthes binding hebben verwijderd.
dashboard.delete_inactivate_accounts=Verwijder alle inactieve accounts
dashboard.delete_inactivate_accounts_success=Alle inactivering van rekeningen hebben verwijderd.
dashboard.delete_repo_archives=Verwijderen van alle repositories archieven
dashboard.delete_repo_archives_success=Alle repositories archieven hebben verwijderd.
dashboard.git_gc_repos=Do garbage collection on repositories
dashboard.git_gc_repos_success=All repositories have done garbage collection successfully.
dashboard.server_uptime=Uptime server
dashboard.current_goroutine=Huidige Goroutines
dashboard.current_memory_usage=Huidige geheugen gebruik
dashboard.total_memory_allocated=Totaal toegewezen geheugen
dashboard.memory_obtained=Geheugen gebruikt
dashboard.pointer_lookup_times=Aanwijzer Lookup keer
dashboard.memory_allocate_times=Geheugen toewijzen keer
dashboard.memory_free_times=Geheugen gratis keer
dashboard.current_heap_usage=Huidige Heap gebruik
dashboard.heap_memory_obtained=Heap-geheugen verkregen
dashboard.heap_memory_idle=Heap Memory inactief
dashboard.heap_memory_in_use=Hoop geheugen In gebruik
dashboard.heap_memory_released=Heap-geheugen vrijgegeven
dashboard.heap_objects=Heap-objecten
dashboard.bootstrap_stack_usage=Bootstrap Stack gebruik
dashboard.stack_memory_obtained=Stapel geheugen verkregen
dashboard.mspan_structures_usage=MSpan structuren gebruik
dashboard.mspan_structures_obtained=MSpan structuren verkregen
dashboard.mcache_structures_usage=MCache structuren gebruik
dashboard.mcache_structures_obtained=MCache structuren verkregen
dashboard.profiling_bucket_hash_table_obtained=Profilering emmer hashtabel verkregen
dashboard.gc_metadata_obtained=GC Metadada verkregen
dashboard.other_system_allocation_obtained=Andere systeem toewijzing verkregen
dashboard.next_gc_recycle=Volgende GC recycle
dashboard.last_gc_time=Sinds vorige GC verwerkingstijd
dashboard.total_gc_time=Totaal GC verwerkingstijd
dashboard.total_gc_pause=Totaal GC verwerkingstijd
dashboard.last_gc_pause=Laatste GC verwerkingstijd
dashboard.gc_times=GC verwerkingen
users.user_manage_panel=Gebruikers beheren
users.new_account=Nieuw account aanmaken
users.name=Naam
users.activated=Geactiveerd
users.admin=Admin
users.repos=Repos
users.created=Aangemaakt
users.edit=Bewerken
users.auth_source=Autorisatiebron
users.local=Lokaal
users.auth_login_name=Autorisatie inlognaam
users.update_profile_success=Profiel is succesvol bijgewerkt.
users.edit_account=Bewerk account
users.is_activated=Dit account is geactiveerd
users.is_admin=Dit account heeft beheerdersrechten
users.allow_git_hook=Deze account beschikt over machtigingen voor het maken van Git haken
users.update_profile=Account profiel bijwerken
users.delete_account=Dit account verwijderen
users.still_own_repo=Dit account is nog steeds eigendom van een repositorie. U moet deze repositorie eerst verwijderen of overdragen.
users.still_has_org=Deze account nog steeds lidmaatschap van organisatie, u hebt naar links of hen eerst verwijderen.
orgs.org_manage_panel=Organisaties beheren
orgs.name=Naam
orgs.teams=Teams
orgs.members=Leden
repos.repo_manage_panel=Repositoriebeheerpaneel
repos.owner=Eigenaar
repos.name=Naam
repos.private=Prive
repos.watches=Volgers
repos.stars=Sterren
repos.issues=Kwesties
auths.auth_manage_panel=Autorisatiebeheerpaneel
auths.new=Nieuwe autorisatiebron
auths.name=Naam
auths.type=Type
auths.enabled=Ingeschakeld
auths.updated=Bijgewerkt
auths.auth_type=Autorisatietype
auths.auth_name=Autorisatienaam
auths.domain=Domein
auths.host=Host
auths.port=Poort
auths.base_dn=Base DN
auths.attributes=Zoek attributen
auths.filter=Zoek filter
auths.ms_ad_sa=MS Ad SA
auths.smtp_auth=SMTP authenticatietype
auths.smtphost=SMTP host
auths.smtpport=SMTP poort
auths.enable_tls=Activeer TLS-encryptie
auths.enable_auto_register=Activeer automatische registratie
auths.tips=Tips
auths.edit=Bewerk autorisatie-instellingen
auths.activated=Deze autorisatiemethode is geactiveerd
auths.update_success=Autorisatie-instellingen zijn succesvol bijgewerkt.
auths.update=Update autorisatie-instellingen
auths.delete=Verwijder deze autorisatie
auths.delete_auth_title=Verwijderings-autorisatie
auths.delete_auth_desc=Deze autorisatiemethode wordt verwijderd. Weet u zeker dat u wilt doorgaan?
config.server_config=Serverconfiguratie
config.app_name=Applicatienaam
config.app_ver=Applicatieversie
config.app_url=Applicatie-URL
config.domain=Domein
config.offline_mode=Offline-modus
config.disable_router_log=Router-log uitschakelen
config.run_user=Uitvoerende gebruiker
config.run_mode=Uitvoer modus
config.repo_root_path=Repositorie basis pad
config.static_file_root_path=Statische bestanden basis pad
config.log_file_root_path=Log bestand basis pad
config.script_type=Script type
config.reverse_auth_user=Omgekeerde verificatie gebruiker
config.db_config=Databaseconfiguratie
config.db_type=Type
config.db_host=Host
config.db_name=Naam
config.db_user=Gebruiker
config.db_ssl_mode=SSL modus
config.db_ssl_mode_helper=(alleen voor "postgres")
config.db_path=Pad
config.db_path_helper=(alleen voor "sqlite3")
config.service_config=Serviceconfiguratie
config.register_email_confirm=E-mailbevestiging registreren
config.disable_register=Registratie uitgeschakeld
config.require_sign_in_view=Inloggen vereist om te kunnen inzien
config.mail_notify=E-mailnotificaties
config.enable_cache_avatar=Avatar Cache inschakelen
config.active_code_lives=Actieve Code leven
config.reset_password_code_lives=Reset wachtwoord Code leven
config.webhook_config=Webhook configuratie
config.task_interval=Taakinterval
config.deliver_timeout=Bezorging verlooptijd
config.mailer_config=Mailerconfiguatie
config.mailer_enabled=Ingeschakeld
config.mailer_name=Naam
config.mailer_host=Host
config.mailer_user=Gebruiker
config.oauth_config=OAuth-configuratie
config.oauth_enabled=Ingeschakeld
config.cache_config=Cache-configuratie
config.cache_adapter=Cache-adapter
config.cache_interval=Cache-interval
config.cache_conn=Cache-connectie
config.session_config=Sessieconfiguratie
config.session_provider=Sessieprovider
config.provider_config=Provider config
config.cookie_name=Cookie naam
config.enable_set_cookie=Set Cookie inschakelen
config.gc_interval_time=GC interval time
config.session_life_time=Sessie duur
config.https_only=Alleen HTTPS
config.cookie_life_time=Cookie duur leeftijd
config.picture_config=Foto configuratie
config.picture_service=Foto service
config.disable_gravatar=Gravatar uitschakelen
config.log_config=Logconfiguratie
config.log_mode=Log-modus
monitor.cron=Cron-taken
monitor.name=Naam
monitor.schedule=Planning
monitor.next=Volgende
monitor.previous=Vorige
monitor.execute_times=Aantal keren uitgevoerd
monitor.process=Draaiende processen
monitor.desc=Omschrijving
monitor.start=Starttijd
monitor.execute_time=Uitvoertijd
notices.system_notice_list=Systeem aankondigingen
notices.type=Type
notices.type_1=Opslagplaats
notices.desc=Beschrijving
notices.op=Op.
notices.delete_success=Systeem bericht is met succes verwijderd.
[action]
create_repo=repositorie aangemaakt in <a href="%s/%s">%s</a>
commit_repo=push update naar <a href="%s/%s/src/%s">%s</a> in <a href="%s/%s">%s</a
create_issue=opende issue in <a href="%s/%s/issues/%s">%s#%s</a>
comment_issue=reactie op issue <a href="%s/%s/issues/%s">%s#%s</a>
transfer_repo=repositorie verplaatst naar <code>%s</code> naar <a href="/%s%s">%s</a>
push_tag=geduwd label <a href="%s/%s/src/%s"> %s</a> naar <a href="%s/%s"> %s</a>
compare_2_commits=Weergave vergelijking voor deze 2 commits
[tool]
ago=geleden
from_now=vanaf nu
now=nu
1s=1 seconde %s
1m=1 minuut %s
1h=1 uur %s
1d=1 dag %s
1w=1 week %s
1mon=1 maand %s
1y=1 jaar %s
seconds=%d seconden %s
minutes=%d minuten %s
hours=%d uur %s
days=%d dagen %s
weeks=%d weken %s
months=%d maanden %s
years=%d jaren %s
raw_seconds=seconden
raw_minutes=minuten

1296
conf/locale/locale_zh-CN.ini Normal file → Executable file

File diff suppressed because it is too large Load Diff

718
conf/locale/locale_zh-HK.ini Executable file
View File

@@ -0,0 +1,718 @@
app_desc=基於 Go 語言的自助 Git 服務
home=首頁
dashboard=控制面版
explore=探索
help=幫助
sign_in=登錄
social_sign_in=社交帳號登錄:第 2 步 <small>關聯帳號</small>
sign_out=退出
sign_up=註冊
register=註冊
website=官方網站
version=當前版本
page=頁面
template=模版
language=語言選項
username=用戶名
email=郵箱
password=密碼
re_type=確認密碼
captcha=驗證碼
repository=倉庫
organization=組織
mirror=鏡像
new_repo=創建新的倉庫
new_migrate=遷移外部倉庫
new_fork=新的派生倉庫
new_org=創建新的組織
manage_org=管理我的組織
admin_panel=管理面版
account_settings=帳戶設置
settings=帳戶設置
news_feed=最新活動
pull_requests=合併請求
issues=問題管理
cancel=取消
[install]
install=安裝頁面
title=首次執行安裝程序
requite_db_desc=Gogs 允許後端數據庫為 MySQL、PostgreSQL 或 SQLite3但是 SQLite3 一般只有官方二進制發行版才支持。
db_type=數據庫類型
host=數據庫主機
user=數據庫用戶
password=數據庫用戶密碼
db_name=數據庫名稱
db_helper=如果您使用 MySQL請使用 INNODB 引擎以及 utf8_general_ci 字符集。
ssl_mode=SSL 模式
path=數據庫文件路徑
sqlite_helper=SQLite3 數據庫的文件路徑。
general_title=應用基本設置
repo_path=倉庫根目錄
repo_path_helper=所有 Git 遠程倉庫都將被存放於該目錄。
run_user=執行系統用戶
run_user_helper=該用戶必須具有對倉庫根目錄和執行 Gogs 的操作權限。
domain=域名
domain_helper=該設置影響 SSH 復製地址。
app_url=應用 URL
app_url_helper=該設置影響 HTTP/HTTPS 復製地址和一些郵箱中的連結。
email_title=郵件服務設置(可選)
smtp_host=SMTP 主機
mailer_user=發送郵箱
mailer_password=發送郵箱密碼
notify_title=通知提醒設置(可選)
register_confirm=啟用註冊郵箱確認
mail_notify=啟用郵件通知提醒
admin_title=管理員帳號設置
admin_name=管理員用戶名
admin_password=管理員密碼
confirm_password=確認密碼
admin_email=管理員郵箱
install_gogs=立即安裝
test_git_failed=無法識別 'git' 命令:%v
sqlite3_not_available=您所使用的發行版本不支持 SQLite3請從 %s 下載官方構建版,而不是 gobuild 版本。
invalid_db_setting=數據庫設置不正確:%v
invalid_repo_path=倉庫根目錄設置不正確:%v
run_user_not_match=執行系統用戶非當前用戶:%s -> %s
save_config_failed=應用配置保存失敗:%v
invalid_admin_setting=管理員帳戶設置不正確:%v
install_success=您好!我們很高興您選擇使用 Gogs祝您使用愉快代碼從此無 BUG
[home]
uname_holder=用戶名或郵箱
password_holder=密碼
switch_dashboard_context=切換控制面版用戶
my_repos=我的倉庫
collaborative_repos=參與協作的倉庫
my_orgs=我的組織
my_mirrors=我的鏡像
[explore]
repos=探索倉庫
[auth]
create_new_account=創建帳戶
register_hepler_msg=已經註冊?立即登錄!
social_register_hepler_msg=已經註冊?立即綁定!
disable_register_prompt=對不起,註冊功能已被關閉。請聯系網站管理員。
disable_register_mail=對不起,註冊郵箱確認功能已被關閉。
remember_me=記住登錄
forgot_password=忘記密碼
forget_password=忘記密碼?
sign_up_now=還沒帳戶?馬上註冊。
confirmation_mail_sent_prompt=一封新的確認郵件已經被發送至 <b>%s</b>,請檢查您的收件箱並在 %d 小時內完成確認註冊操作。
sign_in_email=登錄到您的郵箱
active_your_account=激活您的帳戶
resent_limit_prompt=對不起,您請求發送激活郵件過於頻繁,請等待 3 分鐘後再試!
has_unconfirmed_mail=%s 您好,系統檢測到您有一封發送至 <b>%s</b> 但未被確認的郵件。如果您未收到激活郵件,或需要重新發送,請單擊下方的按鈕。
resend_mail=單擊此處重新發送確認郵件
email_not_associate=您輸入的郵箱地址未被關聯到任何帳號!
send_reset_mail=單擊此處(重新)發送您的密碼重置郵件
reset_password=重置密碼
invalid_code=對不起,您的確認代碼已過期或已失效。
reset_password_helper=單擊此處重置密碼
password_too_short=密碼長度不能少於 6 位!
[form]
UserName=用戶名
RepoName=倉庫名稱
Email=郵箱地址
Password=密碼
Retype=確認密碼
SSHTitle=SSH 密鑰名稱
HttpsUrl=HTTPS URL 地址
PayloadUrl=推送地址
TeamName=團隊名稱
AuthName=認證名稱
AdminEmail=管理員郵箱
require_error=不能為空。
alpha_dash_error=必須為英文字母、阿拉伯數字或橫線(-_
alpha_dash_dot_error=必須為英文字母、阿拉伯數字、橫線(-_或點。
min_size_error=長度最小為 %s 個字符。
max_size_error=長度最大為 %s 個字符。
email_error=不是一個有效的郵箱地址。
url_error=不是一個有效的 URL。
unknown_error=未知錯誤:
captcha_incorrect=驗證碼未匹配。
password_not_match=密碼與確認密碼未匹配。
username_been_taken=用戶名已經被佔用。
repo_name_been_taken=倉庫名稱已經被佔用。
org_name_been_taken=組織名稱已經被佔用。
team_name_been_taken=團隊名稱已經被佔用。
email_been_used=郵箱地址已經被使用。
ssh_key_been_used=SSH 密鑰已經被使用。
illegal_username=您的用戶名包含不合法字符。
illegal_repo_name=倉庫名稱包含不合法字符。
illegal_org_name=組織名稱包含不合法字符。
illegal_team_name=團隊名稱包含不合法字符。
username_password_incorrect=用戶名或密碼不正確。
enterred_invalid_repo_name=請檢查您輸入的倉庫名稱是正確。
enterred_invalid_owner_name=請檢查您輸入的新所有者用戶名是否正確。
enterred_invalid_password=請檢查您輸入的密碼是否正確。
user_not_exist=被操作的用戶不存在!
last_org_owner=被移除用戶為最後一位管理員。請添加一位新的管理員再進行移除成員操作!
invalid_ssh_key=很抱歉,我們無法驗證您輸入的 SSH 密鑰:%s
unable_verify_ssh_key=Gogs 無法驗證您輸入的 SSH 密鑰,但我們假設那是有效的密鑰,請您自行確保其有效性!
auth_failed=授權驗證失敗:%v
still_own_repo=您的帳戶仍然是某些倉庫的擁有者,您必須先轉移或刪除它們才能執行刪除帳戶操作!
still_has_org=您的帳戶仍舊是某些組織的成員,您必須先離開或刪除組織。
org_still_own_repo=該組織仍然是某些倉庫的擁有者,您必須先轉移或刪除它們才能執行刪除組織操作!
still_own_user=該授權認證依舊被部分用戶使用,請先刪除該部分用戶後再試!
target_branch_not_exist=目標分支不存在
[user]
change_avatar=到 gravatar.com 上修改您的頭像
change_custom_avatar=到個人設置中修改頭像
join_on=加入於
repositories=倉庫列表
activity=公開活動
followers=關註者
starred=已讚好
following=關註中
[settings]
profile=個人信息
password=修改密碼
ssh_keys=管理 SSH 密鑰
social=社交帳號綁定
applications=管理授權應用
orgs=管理組織
delete=刪除帳戶
uid=用戶 ID
public_profile=公開信息
profile_desc=您的郵箱地址將會被公開,並被用於接收帳戶的所有提醒和通知。
full_name=自定義名稱
website=個人網站
location=所在地區
update_profile=更新信息
update_profile_success=您的個人信息更新成功!
change_username=用戶名將被修改
change_username_desc=用戶名被修改,您確定要繼續操作嗎?這將會影響到所有與您帳戶有關的連結。
continue=繼續操作
cancel=取消操作
enable_custom_avatar=啟動自定義頭像
enable_custom_avatar_helper=激活該選項來禁止從 Gravatar 獲取頭像
choose_new_avatar=選擇新的頭像
update_avatar=更新頭像設置
uploaded_avatar_not_a_image=上傳的文件不是一張圖片!
no_custom_avatar_available=沒有任何自定義頭像,無法激活該選項。
update_avatar_success=您的頭像設置更新成功!
change_password=修改密碼
old_password=當前密碼
new_password=新的密碼
password_incorrect=當前密碼不正確!
change_password_success=密碼修改成功!您現在可以使用新的密碼登錄。
manage_ssh_keys=管理 SSH 密鑰
add_key=增加密鑰
ssh_desc=以下是與您帳戶所關聯的 SSH 密鑰,如果您發現有陌生的密鑰,請立即刪除它!
ssh_helper=<strong>需要幫助嗎?</strong> 請查看有關 <a href="%s"> 如何生成 SSH 密鑰</a> 的指南或 <a href="%s"> SSH 的常見問題</a> 的疑難排解。
add_new_key=增加 SSH 密鑰
key_name=密鑰名稱
key_content=密鑰內容
add_key_success=新的 SSH 密鑰添加成功!
delete_key=刪除
add_on=增加於
last_used=上次使用在
no_activity=沒有最近活動
manage_social=管理關聯社交帳戶
social_desc=以下是與您帳戶所關聯的社交帳號,如果您發現有陌生的關聯,請立即解除綁定!
unbind=解除綁定
unbind_success=社交帳號解除綁定成功!
manage_access_token=管理個人操作令牌
generate_new_token=生成新的令牌
tokens_desc=您可以使用這些已生成的令牌來操作 Gogs API。
new_token_desc=目前為止,任何令牌都對您的帳戶擁有完整的操作權限。
token_name=令牌名稱
generate_token=生成令牌
generate_token_succees=新的操作令牌生成成功!您必須立即復製到一個安全的地方,因為該令牌只會顯示一次!
delete_token=删除令牌
delete_token_success=個人操作令牌刪除成功!請更新與該令牌有關的所有應用。
delete_account=刪除當前帳戶
delete_prompt=刪除操作會永久清除您的帳戶信息,並且 <strong>不可恢復</strong>
confirm_delete_account=確認刪除帳戶
delete_account_title=帳戶刪除操作
delete_account_desc=該帳戶將被永久性刪除,您確定要繼續操作嗎?
[repo]
owner=擁有者
repo_name=倉庫名稱
repo_name_helper=偉大的倉庫名稱一般都較短、令人深刻並且 <strong>獨一無二</strong> 的。
visibility=可見度
visiblity_helper=本倉庫將是 <span class="label label-red label-radius">私有的</span>
fork_repo=派生倉庫
fork_from=派生自
fork_visiblity_helper=派生倉庫無法修改可見性。
repo_desc=倉庫描述
repo_lang=倉庫語言
repo_lang_helper=請選擇 .gitignore 文件
license=授權許可
license_helper=請選擇授權許可文件
init_readme=使用 README.md 文件初始化倉庫
create_repo=創建倉庫
default_branch=默認分支
mirror_interval=鏡像同步周期(小時)
goget_meta=Go-Get 支持
goget_meta_helper=本倉庫將可以通過 <span class="label label-blue label-radius">Go Get</span> 獲取
need_auth=需要授權驗證
migrate_type=遷移類型
migrate_type_helper=本倉庫將是 <span class="label label-blue label-radius">鏡像</span>
migrate_repo=遷移倉庫
copy_link=復製連結
click_to_copy=復製到剪切簿
copied=復製成功
clone_helper=不知道如何操作?訪問 <a target="_blank"href="%s"> 帮助説明</a>
unwatch=取消關註
watch=關註
unstar=取消讚好
star=讚好
fork=派生
no_desc=暫無描述
quick_guide=快速幫助
clone_this_repo=復製當前倉庫
create_new_repo_command=從命令行創建一個新的倉庫
push_exist_repo=從命令行推送已經創建的倉庫
branch=分支
tree=目錄樹
branch_and_tags=分支與標籤
branches=分支列表
tags=標籤列表
issues=問題管理
commits=提交歷史
releases=版本發佈
file_raw=原始文件
file_history=文件歷史
file_view_raw=查看原始文件
commits.commits=次代碼提交
commits.search=搜索提交歷史
commits.find=查找
commits.author=作者
commits.message=備註
commits.date=提交日期
commits.older=更舊的提交
commits.newer=更新的提交
settings=倉庫設置
settings.options=基本設置
settings.collaboration=管理協作者
settings.hooks=管理 Web 鉤子
settings.githooks=管理 Git 鉤子
settings.deploy_keys=管理部署密鑰
settings.basic_settings=基本設置
settings.danger_zone=危險操作區
settings.site=官方網站
settings.update_settings=更新倉庫設置
settings.change_reponame=倉庫名稱將被修改
settings.change_reponame_desc=倉庫名稱被修改,您確定要繼續操作嗎?這將會影響到所有與該倉庫有關的連結。
settings.transfer=轉移倉庫所有權
settings.transfer_desc=您可以將倉庫轉移至您擁有管理員權限的帳戶或組織。
settings.new_owner_has_same_repo=新的倉庫擁有者已經存在同名倉庫!
settings.delete=刪除本倉庫
settings.delete_desc=刪除倉庫操作不可逆轉,請三思而後行。
settings.transfer_notices=<p>- 如果您轉移給個人用戶,您將對倉庫失去所有權限。</p><p>- 如果您轉移給您作為擁有者的組織,則可繼續保持操作權限。</p>
settings.update_settings_success=倉庫設置更新成功!
settings.transfer_owner=新擁有者
settings.make_transfer=確認轉移倉庫
settings.transfer_succeed=倉庫所有權轉移成功!
settings.confirm_delete=確認刪除倉庫
settings.add_collaborator=增加新的協作者
settings.add_collaborator_success=成功添加新的協作者!
settings.remove_collaborator_success=被操作的協作者已經被收回權限!
settings.user_is_org_member=被操作的用戶是組織成員,因此無法添加為協作者!
settings.add_webhook=添加 Web 鉤子
settings.hooks_desc=Web 鉤子允許您設定在 Gogs 上發生指定事件時對指定 URL 發送 POST 通知。查看 <a target="_blank" href="%s">Webhooks 文檔</a> 獲取更多信息。
settings.githooks_desc=Git 鉤子是由 Git 本身提供的功能,以下為 Gogs 所支持的鉤子列表。
settings.githook_edit_desc=如果鉤子未啟動,則會顯示樣例文件中的內容。如果想要刪除某個鉤子,則提交空白文本即可。
settings.githook_name=鉤子名稱
settings.githook_content=鉤子文本
settings.update_githook=更新鉤子設置
settings.remove_hook_success=Web 鉤子刪除成功!
settings.add_webhook_desc=我們會通過 <code>POST</code> 請求將訂閱事件信息發送至向指定 URL 地址。您可以設置不同的數據接收方式JSON 或 <code>x-www-form-urlencoded</code>)。 請查閱 <a target="_blank" href="%s">Webhooks 指南</a>.
settings.payload_url=推送地址
settings.content_type=數據格式
settings.secret=密鑰文本
settings.event_desc=請設置您希望觸發 Web 鉤子的事件:
settings.event_push_only=只推送 <code>push</code> 事件。
settings.active=是否激活
settings.active_helper=當指定事件發生時我們將會觸發此 Web 鉤子。
settings.add_hook_success=Web 鉤子添加成功!
settings.update_webhook=更新 Web 鉤子
settings.update_hook_success=Web 鉤子更新成功!
settings.delete_webhook=刪除 Web 鉤子
settings.recent_deliveries=最近推送記錄
settings.hook_type=鉤子類型
settings.add_slack_hook_desc=為您的倉庫增加 <a href="%s">Slack</a> 集成。
settings.slack_token=令牌
settings.slack_domain=域名
settings.slack_channel=頻道
diff.browse_source=瀏覽代碼
diff.parent=父節點
diff.commit=當前提交
diff.data_not_available=暫無可用數據
diff.show_diff_stats=顯示文件統計
diff.stats_desc=共有 <strong> %d 個文件被更改</strong>,包括 <strong>%d 次插入</strong> 和 <strong>%d 次删除</strong>
diff.bin=二進制
diff.view_file=查看文件
release.releases=Releases
release.new_release=New Release
release.draft=Draft
release.prerelease=Pre-Release
release.stable=Stable
release.edit=edit
release.ahead=<strong>%d</strong> commits to %s since this release
release.source_code=Source Code
release.tag_name=Tag name
release.target=Target
release.tag_helper=Choose an existing tag, or create a new tag on publish.
release.release_title=Release title
release.content_with_md=Content with <a href="%s">Markdown</a>
release.write=Write
release.preview=Preview
release.content_placeholder=Write some content
release.loading=Loading...
release.prerelease_desc=This is a pre-release
release.prerelease_helper=Well point out that this release is identified as non-production ready.
release.publish=Publish Release
release.save_draft=Save Draft
release.edit_release=Edit Release
release.tag_name_already_exist=Release with this tag name has already existed.
[org]
org_name_holder=組織名稱
org_name_helper=偉大的組織都有一個簡短而寓意深刻的名字。
org_email_helper=組織的郵箱用於接收所有通知和確認郵件。
create_org=創建組織
repo_updated=最後更新於
people=組織成員
invite_someone=邀請他人加入
teams=組織團隊
lower_members=名成員
lower_repositories=個倉庫
create_new_team=創建新的團隊
org_desc=組織描述
team_name=團隊名稱
team_desc=團隊描述
team_name_helper=您可以使用該名稱來通知改組全體成員。
team_desc_helper=一句話描述這個團隊是做什麼的。
team_permission_desc=請選擇該團隊所具有的權限等級:
settings=組織設置
settings.options=基本設置
settings.full_name=組織全名
settings.website=官方網站
settings.location=所在地區
settings.update_settings=更新組織設置
settings.change_orgname=組織名稱將被修改
settings.change_orgname_desc=組織名稱被修改,您確定要繼續操作嗎?這將會影響到所有與該組織有關的連結。
settings.update_setting_success=組織設置更新成功!
settings.delete=刪除組織
settings.delete_account=刪除當前組織
settings.delete_prompt=刪除操作會永久清除該組織的信息,並且 <strong>不可恢復</strong>
settings.confirm_delete_account=確認刪除組織
settings.delete_org_title=組織刪除操作
settings.delete_org_desc=該組織將被永久性刪除,您確定要繼續操作嗎?
settings.hooks_desc=在此處添加的 Web 鉤子將會應用到該組織下的 <strong>所有倉庫</strong>。
members.public=公開成員
members.public_helper=設為私有
members.private=私有成員
members.private_helper=設為公開
members.owner=管理員
members.member=普通成員
members.conceal=隱藏身份
members.remove=移除成員
members.leave=離開組織
members.invite_desc=請輸入被邀請到組織 %s 的用戶名稱:
members.invite_now=立即邀請
teams.join=加入團隊
teams.leave=離開團隊
teams.read_access=讀取權限
teams.read_access_helper=這個團隊將擁有查看和復製所屬倉庫的權限。
teams.write_access=寫入權限
teams.write_access_helper=這個團隊將擁有查看、復製和推送所屬倉庫的權限。
teams.admin_access=管理權限
teams.admin_access_helper=這個團隊將擁有查看、復製、推送和添加其他組織成員到團隊的權限。
teams.no_desc=該團隊暫無描述
teams.settings=團隊設置
teams.owners_permission_desc=管理員團隊對 <strong>所有倉庫</strong> 具有操作權限,且對組織具有 <strong>管理員權限</strong>。
teams.members=團隊成員
teams.update_settings=更新團隊設置
teams.delete_team=刪除當前團隊
teams.add_team_member=添加團隊成員
teams.delete_team_title=團隊刪除操作
teams.delete_team_desc=刪除操作會永久清除有關該團隊的信息,您確定要繼續操作嗎?團隊成員可能會失去對某些倉庫的操作權限。
teams.delete_team_success=指定團隊刪除成功!
teams.read_permission_desc=該團隊擁有對所屬倉庫的 <strong>讀取</strong> 權限,團隊成員可以進行查看和復製等只讀操作。
teams.write_permission_desc=該團隊擁有對所屬倉庫的 <strong>讀取</strong> 和 <strong>寫入</strong> 的權限。
teams.admin_permission_desc=該團隊擁有一定的 <strong>管理</strong> 權限,團隊成員可以讀取、復製、推送以及添加其它倉庫協作者。
teams.repositories=團隊倉庫
teams.add_team_repository=添加團隊倉庫
teams.remove_repo=移除倉庫
teams.add_nonexistent_repo=您嘗試添加到團隊的倉庫不存在,請先創建倉庫!
[admin]
dashboard=控制面版
users=用戶管理
organizations=組織管理
repositories=倉庫管理
authentication=授權認證管理
config=應用配置管理
notices=系統提示管理
monitor=應用監控面版
prev=上一頁
next=下一頁
dashboard.statistic=應用統計數據
dashboard.operations=管理員操作
dashboard.system_status=系統監視狀態
dashboard.statistic_info=Gogs 數據庫統計:<b>%d</b> 位用戶,<b>%d</b> 個組織,<b>%d</b> 個公鑰,<b>%d</b> 個倉庫,<b>%d</b> 個倉庫關註,<b>%d</b> 個贊,<b>%d</b> 次行為,<b>%d</b> 條權限記錄,<b>%d</b> 個問題,<b>%d</b> 次評論,<b>%d</b> 個社交帳號,<b>%d</b> 個用戶關註,<b>%d</b> 個鏡像,<b>%d</b> 個版本發佈,<b>%d</b> 個登錄源,<b>%d</b> 個 Web 鉤子,<b>%d</b> 個里程碑,<b>%d</b> 個標籤,<b>%d</b> 個鉤子任務,<b>%d</b> 個團隊,<b>%d</b> 個更新任務,<b>%d</b> 個附件。
dashboard.operation_name=操作名稱
dashboard.operation_switch=開關
dashboard.operation_run=執行
dashboard.clean_unbind_oauth=清理未綁定社交帳號
dashboard.clean_unbind_oauth_success=所有未綁定社交數據清除成功!
dashboard.delete_inactivate_accounts=刪除所有未激活帳戶
dashboard.delete_inactivate_accounts_success=所有未激活帳號清除成功!
dashboard.delete_repo_archives=刪除所有倉庫存檔
dashboard.delete_repo_archives_success=所有倉庫存檔清除成功!
dashboard.git_gc_repos=對倉庫進行垃圾回收
dashboard.git_gc_repos_success=所有倉庫的垃圾回收已成功完成!
dashboard.server_uptime=服務執行時間
dashboard.current_goroutine=當前 Goroutines 數量
dashboard.current_memory_usage=當前內存使用量
dashboard.total_memory_allocated=所有被分配的內存
dashboard.memory_obtained=內存佔用量
dashboard.pointer_lookup_times=指針查找次數
dashboard.memory_allocate_times=內存分配次數
dashboard.memory_free_times=內存釋放次數
dashboard.current_heap_usage=當前 Heap 內存使用量
dashboard.heap_memory_obtained=Heap 內存佔用量
dashboard.heap_memory_idle=Heap 內存空閒量
dashboard.heap_memory_in_use=正在使用的 Heap 內存
dashboard.heap_memory_released=被釋放的 Heap 內存
dashboard.heap_objects=Heap 對象數量
dashboard.bootstrap_stack_usage=啟動 Stack 使用量
dashboard.stack_memory_obtained=被分配的 Stack 內存
dashboard.mspan_structures_usage=MSpan 結構內存使用量
dashboard.mspan_structures_obtained=被分配的 MSpan 結構內存
dashboard.mcache_structures_usage=MCache 結構內存使用量
dashboard.mcache_structures_obtained=被分配的 MCache 結構內存
dashboard.profiling_bucket_hash_table_obtained=被分配的剖析哈希表內存
dashboard.gc_metadata_obtained=被分配的垃圾收集元數據內存
dashboard.other_system_allocation_obtained=其它被分配的系統內存
dashboard.next_gc_recycle=下次垃圾收集內存回收量
dashboard.last_gc_time=距離上次垃圾收集時間
dashboard.total_gc_time=垃圾收集執行時間總量
dashboard.total_gc_pause=垃圾收集暫停時間總量
dashboard.last_gc_pause=上次垃圾收集暫停時間
dashboard.gc_times=垃圾收集執行次數
users.user_manage_panel=用戶管理面版
users.new_account=創建新的帳戶
users.name=用戶名
users.activated=已激活
users.admin=管理員
users.repos=倉庫數
users.created=創建時間
users.edit=編輯
users.auth_source=認證源
users.local=本地
users.auth_login_name=認證登錄名
users.update_profile_success=該用戶信息更新成功!
users.edit_account=編輯用戶信息
users.is_activated=該用戶已被激活
users.is_admin=該用戶具有管理員權限
users.allow_git_hook=該帳戶具有創建 Git 鉤子的權限
users.update_profile=更新用戶信息
users.delete_account=刪除該用戶
users.still_own_repo=該帳戶仍然是某些倉庫的擁有者,您必須先轉移或刪除它們才能執行刪除帳戶操作!
users.still_has_org=該帳戶仍舊是某些組織的成員,您必須先使其離開或刪除組織。
orgs.org_manage_panel=組織管理面版
orgs.name=組織名稱
orgs.teams=團隊數
orgs.members=成員數
repos.repo_manage_panel=倉庫管理界面
repos.owner=所有者
repos.name=倉庫名稱
repos.private=私有庫
repos.watches=關註數
repos.stars=讚好數
repos.issues=問題數
auths.auth_manage_panel=授權認證管理面版
auths.new=添加新的認證源
auths.name=認證名稱
auths.type=認證類型
auths.enabled=已啟用
auths.updated=最後更新時間
auths.auth_type=授權類型
auths.auth_name=授權名稱
auths.domain=域名
auths.host=主機地址
auths.port=主機端口
auths.base_dn=Base DN
auths.attributes=搜尋屬性
auths.filter=搜尋過濾
auths.ms_ad_sa=Ms Ad SA
auths.smtp_auth=SMTP 授權類型
auths.smtphost=SMTP 主機地址
auths.smtpport=SMTP 主機端口
auths.enable_tls=啟用 TLS 加密
auths.enable_auto_register=允許授權用戶自動註冊
auths.tips=幫助提示
auths.edit=修改授權認證設置
auths.activated=該授權認證已經啟用
auths.update_success=授權認證設置更新成功!
auths.update=更新授權認證信息
auths.delete=刪除該授權認證
auths.delete_auth_title=授權認證刪除操作
auths.delete_auth_desc=該授權認證將被刪除,您確定要繼續嗎?
config.server_config=服務器配置
config.app_name=應用名稱
config.app_ver=應用版本
config.app_url=應用 URL
config.domain=應用域名
config.offline_mode=離線模式
config.disable_router_log=關閉路由日志
config.run_user=執行用戶
config.run_mode=執行模式
config.repo_root_path=倉庫根目錄
config.static_file_root_path=靜態文件根目錄
config.log_file_root_path=日志文件根目錄
config.script_type=腳本類型
config.reverse_auth_user=反向代理認證
config.db_config=數據庫配置
config.db_type=數據庫類型
config.db_host=主機地址
config.db_name=數據庫名稱
config.db_user=數據庫用戶
config.db_ssl_mode=SSL 模式
config.db_ssl_mode_helper=(僅限 "postgres" 使用)
config.db_path=數據庫路徑
config.db_path_helper=(僅限 "sqlite3" 使用)
config.service_config=服務配置
config.register_email_confirm=註冊郵件確認
config.disable_register=關閉註冊功能
config.require_sign_in_view=強制登錄瀏覽
config.mail_notify=郵件通知提醒
config.enable_cache_avatar=開啟緩存頭像
config.active_code_lives=激活用戶連結有效期
config.reset_password_code_lives=重置密碼連結有效期
config.webhook_config=Web 鉤子配置
config.task_interval=任務周期
config.deliver_timeout=推送超時
config.mailer_config=郵件配置
config.mailer_enabled=啟用服務
config.mailer_name=發送者名稱
config.mailer_host=郵件主機地址
config.mailer_user=發送者帳號
config.oauth_config=社交帳號配置
config.oauth_enabled=啟用服務
config.cache_config=Cache 配置
config.cache_adapter=Cache 適配器
config.cache_interval=Cache 周期
config.cache_conn=Cache 連接字符串
config.session_config=Session 配置
config.session_provider=Session 提供者
config.provider_config=提供者配置
config.cookie_name=Cookie 名稱
config.enable_set_cookie=啟用設置 Cookie
config.gc_interval_time=垃圾收集周期
config.session_life_time=Session 生命周期
config.https_only=僅限 HTTPS
config.cookie_life_time=Cookie 生命周期
config.picture_config=圖片配置
config.picture_service=圖片服務
config.disable_gravatar=禁用 Gravatar 頭像
config.log_config=日誌配置
config.log_mode=日誌模式
monitor.cron=Cron 任務
monitor.name=任務名稱
monitor.schedule=任務安排
monitor.next=下次執行時間
monitor.previous=上次執行時間
monitor.execute_times=執行次數
monitor.process=執行中進程
monitor.desc=進程描述
monitor.start=開始時間
monitor.execute_time=已執行時間
notices.system_notice_list=系統提示管理
notices.type=提示類型
notices.type_1=倉庫
notices.desc=描述
notices.op=操作
notices.delete_success=系統提示刪除成功!
[action]
create_repo=創建了倉庫 <a href="%s/%s">%s</a>
commit_repo=推送了 <a href="%s/%s/src/%s">%s</a> 分支的代碼到 <a href="%s/%s">%s</a>
create_issue=創建了問題 <a href="%s/%s/issues/%s">%s#%s</a>
comment_issue=評論了問題 <a href="%s/%s/issues/%s">%s#%s</a>
transfer_repo=將倉庫 <code>%s</code> 轉移至 <a href="/%s%s">%s</a>
push_tag=推送了標籤 <a href="%s/%s/src/%s">%s</a> 到 <a href="%s/%s">%s</a>
compare_2_commits=查看 2 次提交的內容對比
[tool]
ago=之前
from_now=之後
now=現在
1s=1 秒%s
1m=1 分鐘%s
1h=1 小時%s
1d=1 天%s
1w=1 周%s
1mon=1 月%s
1y=1 年%s
seconds=%d 秒%s
minutes=%d 分鐘%s
hours=%d 小時%s
days=%d 天%s
weeks=%d 周%s
months=%d 月%s
years=%d 年%s
raw_seconds=
raw_minutes=分鐘

View File

@@ -1,12 +1,12 @@
[unix_http_server]
file=/tmp/supervisor.sock ; path to your socket file
file=log/supervisor.sock ; path to your socket file
[supervisord]
logfile=log/supervisord.log ; supervisord log file
logfile_maxbytes=50MB ; maximum size of logfile before rotation
logfile_backups=10 ; number of backed up logfiles
loglevel=warn ; info, debug, warn, trace
pidfile=/tmp/supervisord.pid ; pidfile location
pidfile=log/supervisord.pid ; pidfile location
nodaemon=false ; run supervisord as a daemon
minfds=1024 ; number of startup file descriptors
minprocs=200 ; number of process descriptors
@@ -17,10 +17,10 @@ childlogdir=log
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
serverurl=unix://log/supervisor.sock ; use a unix:// URL for a unix socket
[program:gogs]
command = /root/Developer/gopath/src/github.com/gogits/gogs/start.sh ; here must be the real url, not ~ or $GOROOT like
command = gogs_start
autostart = true
stdout_logfile = log/supervisor-gogs-stderr.log
stderr_logfile = log/supervisor-gogs-error.log
stdout_logfile = log/supervisor-gogs-out.log
stderr_logfile = log/supervisor-gogs-err.log

View File

@@ -17,7 +17,7 @@ import (
"github.com/gogits/gogs/modules/setting"
)
const APP_VER = "0.5.2.0917 Beta"
const APP_VER = "0.5.9.1213 Beta"
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
@@ -35,6 +35,7 @@ func main() {
cmd.CmdUpdate,
cmd.CmdFix,
cmd.CmdDump,
cmd.CmdCert,
}
app.Flags = append(app.Flags, []cli.Flag{}...)
app.Run(os.Args)

View File

@@ -58,6 +58,7 @@ type Action struct {
ActUserId int64 // Action user id.
ActUserName string // Action user name.
ActEmail string
ActAvatar string `xorm:"-"`
RepoId int64
RepoUserName string
RepoName string
@@ -137,7 +138,7 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
return err
}
url := fmt.Sprintf("/%s/%s/commit/%s", repoUserName, repoName, c.Sha1)
url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1)
message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message)
if _, err = CreateComment(userId, issue.RepoId, issue.Id, 0, 0, COMMIT, message, nil); err != nil {
@@ -153,6 +154,8 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
if err = UpdateIssue(issue); err != nil {
return err
} else if err = UpdateIssueUserPairsByStatus(issue.Id, issue.IsClosed); err != nil {
return err
}
if err = ChangeMilestoneIssueStats(issue); err != nil {
@@ -181,13 +184,19 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
commit = &base.PushCommits{}
}
refName := git.RefEndName(refFullName)
repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName)
// if not the first commit, set the compareUrl
if !strings.HasPrefix(oldCommitId, "0000000") {
commit.CompareUrl = fmt.Sprintf("%s/compare/%s...%s", repoLink, oldCommitId, newCommitId)
}
bs, err := json.Marshal(commit)
if err != nil {
return errors.New("action.CommitRepoAction(json): " + err.Error())
}
refName := git.RefEndName(refFullName)
// Change repository bare status and update last updated time.
repo, err := GetRepositoryByName(repoUserId, repoName)
if err != nil {
@@ -211,7 +220,6 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
return errors.New("action.CommitRepoAction(NotifyWatchers): " + err.Error())
}
//qlog.Info("action.CommitRepoAction(end): %d/%s", repoUserId, repoName)
// New push event hook.
if err := repo.GetOwner(); err != nil {
@@ -237,13 +245,6 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
return nil
}
repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName)
compareUrl := ""
// if not the first commit, set the compareUrl
if !strings.HasPrefix(oldCommitId, "0000000") {
compareUrl = fmt.Sprintf("%s/compare/%s...%s", repoLink, oldCommitId, newCommitId)
}
pusher_email, pusher_name := "", ""
pusher, err := GetUserByName(userName)
if err == nil {
@@ -293,7 +294,7 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
},
Before: oldCommitId,
After: newCommitId,
CompareUrl: compareUrl,
CompareUrl: commit.CompareUrl,
}
for _, w := range ws {
@@ -348,15 +349,31 @@ func NewRepoAction(u *User, repo *Repository) (err error) {
return err
}
// TransferRepoAction adds new action for transfering repository.
// TransferRepoAction adds new action for transferring repository.
func TransferRepoAction(u, newUser *User, repo *Repository) (err error) {
if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email,
OpType: TRANSFER_REPO, RepoId: repo.Id, RepoName: repo.Name, Content: newUser.Name,
IsPrivate: repo.IsPrivate}); err != nil {
action := &Action{
ActUserId: u.Id,
ActUserName: u.Name,
ActEmail: u.Email,
OpType: TRANSFER_REPO,
RepoId: repo.Id,
RepoUserName: newUser.Name,
RepoName: repo.Name,
IsPrivate: repo.IsPrivate,
Content: path.Join(repo.Owner.LowerName, repo.LowerName),
}
if err = NotifyWatchers(action); err != nil {
log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name)
return err
}
// Remove watch for organization.
if repo.Owner.IsOrganization() {
if err = WatchRepo(repo.Owner.Id, repo.Id, false); err != nil {
log.Error(4, "WatchRepo", err)
}
}
log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name)
return err
}
@@ -366,7 +383,7 @@ func GetFeeds(uid, offset int64, isProfile bool) ([]*Action, error) {
actions := make([]*Action, 0, 20)
sess := x.Limit(20, int(offset)).Desc("id").Where("user_id=?", uid)
if isProfile {
sess.Where("is_private=?", false).And("act_user_id=?", uid)
sess.And("is_private=?", false).And("act_user_id=?", uid)
}
err := sess.Find(&actions)
return actions, err

64
models/admin.go Normal file
View File

@@ -0,0 +1,64 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"time"
"github.com/Unknwon/com"
)
type NoticeType int
const (
NOTICE_REPOSITORY NoticeType = iota + 1
)
// Notice represents a system notice for admin.
type Notice struct {
Id int64
Type NoticeType
Description string `xorm:"TEXT"`
Created time.Time `xorm:"CREATED"`
}
// TrStr returns a translation format string.
func (n *Notice) TrStr() string {
return "admin.notices.type_" + com.ToStr(n.Type)
}
// CreateNotice creates new system notice.
func CreateNotice(tp NoticeType, desc string) error {
n := &Notice{
Type: tp,
Description: desc,
}
_, err := x.Insert(n)
return err
}
// CreateRepositoryNotice creates new system notice with type NOTICE_REPOSITORY.
func CreateRepositoryNotice(desc string) error {
return CreateNotice(NOTICE_REPOSITORY, desc)
}
// CountNotices returns number of notices.
func CountNotices() int64 {
count, _ := x.Count(new(Notice))
return count
}
// GetNotices returns given number of notices with offset.
func GetNotices(num, offset int) ([]*Notice, error) {
notices := make([]*Notice, 0, num)
err := x.Limit(num, offset).Desc("id").Find(&notices)
return notices, err
}
// DeleteNotice deletes a system notice by given ID.
func DeleteNotice(id int64) error {
_, err := x.Id(id).Delete(new(Notice))
return err
}

View File

@@ -6,6 +6,7 @@ package models
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
@@ -15,8 +16,10 @@ import (
"github.com/Unknwon/com"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/mahonia"
"github.com/gogits/gogs/modules/process"
)
@@ -80,6 +83,8 @@ func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff
leftLine, rightLine int
isTooLong bool
// FIXME: use first 30 lines to detect file encoding. Should use cache in the future.
buf bytes.Buffer
)
diff := &Diff{Files: make([]*DiffFile, 0)}
@@ -97,6 +102,11 @@ func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff
i = i + 1
// FIXME: use first 30 lines to detect file encoding.
if i <= 30 {
buf.WriteString(line)
}
// Diff data too large, we only show the first about maxlines lines
if i == maxlines {
isTooLong = true
@@ -181,6 +191,21 @@ func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff
}
}
// FIXME: use first 30 lines to detect file encoding.
charset, err := base.DetectEncoding(buf.Bytes())
if charset != "utf8" && err == nil {
decoder := mahonia.NewDecoder(charset)
if decoder != nil {
for _, f := range diff.Files {
for _, sec := range f.Sections {
for _, l := range sec.Lines {
l.Content = decoder.ConvertString(l.Content)
}
}
}
}
}
return diff, nil
}

View File

@@ -211,7 +211,10 @@ func GetIssues(uid, rid, pid, mid int64, page int, isClosed bool, labelIds, sort
if len(labelIds) > 0 {
for _, label := range strings.Split(labelIds, ",") {
sess.And("label_ids like '%$" + label + "|%'")
// Prevent SQL inject.
if com.StrTo(label).MustInt() > 0 {
sess.And("label_ids like '%$" + label + "|%'")
}
}
}

View File

@@ -18,6 +18,7 @@ import (
"github.com/gogits/gogs/modules/auth/ldap"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/uuid"
)
type LoginType int
@@ -40,7 +41,7 @@ var LoginTypes = map[LoginType]string{
SMTP: "SMTP",
}
// Ensure structs implmented interface.
// Ensure structs implemented interface.
var (
_ core.Conversion = &LDAPConfig{}
_ core.Conversion = &SMTPConfig{}
@@ -149,7 +150,7 @@ func DelLoginSource(source *LoginSource) error {
// UserSignIn validates user name and password.
func UserSignIn(uname, passwd string) (*User, error) {
var u *User
u := new(User)
if strings.Contains(uname, "@") {
u = &User{Email: uname}
} else {
@@ -161,12 +162,8 @@ func UserSignIn(uname, passwd string) (*User, error) {
return nil, err
}
if u.LoginType == NOTYPE {
if has {
u.LoginType = PLAIN
} else {
return nil, ErrUserNotExist
}
if u.LoginType == NOTYPE && has {
u.LoginType = PLAIN
}
// For plain login, user must exist to reach this line.
@@ -229,33 +226,35 @@ func UserSignIn(uname, passwd string) (*User, error) {
}
}
// Query if name/passwd can login against the LDAP direcotry pool
// Query if name/passwd can login against the LDAP directory pool
// Create a local user if success
// Return the same LoginUserPlain semantic
// FIXME: https://github.com/gogits/gogs/issues/672
func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) {
mail, logged := cfg.Ldapsource.SearchEntry(name, passwd)
if !logged {
// user not in LDAP, do nothing
// User not in LDAP, do nothing
return nil, ErrUserNotExist
}
if !autoRegister {
return u, nil
}
// fake a local user creation
// Fallback.
if len(mail) == 0 {
mail = uuid.NewV4().String() + "@localhost"
}
u = &User{
LowerName: strings.ToLower(name),
Name: strings.ToLower(name),
Name: name,
LoginType: LDAP,
LoginSource: sourceId,
LoginName: name,
IsActive: true,
Passwd: passwd,
Email: mail,
IsActive: true,
}
err := CreateUser(u)
return u, err
return u, CreateUser(u)
}
type loginAuth struct {
@@ -319,7 +318,7 @@ func SmtpAuth(host string, port int, a smtp.Auth, useTls bool) error {
return ErrUnsupportedLoginType
}
// Query if name/passwd can login against the LDAP direcotry pool
// Query if name/passwd can login against the LDAP directory pool
// Create a local user if success
// Return the same LoginUserPlain semantic
func LoginUserSMTPSource(u *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {

View File

@@ -5,6 +5,7 @@
package models
import (
"database/sql"
"fmt"
"os"
"path"
@@ -17,10 +18,16 @@ import (
"github.com/gogits/gogs/modules/setting"
)
var (
x *xorm.Engine
tables []interface{}
// Engine represents a xorm engine or session.
type Engine interface {
Delete(interface{}) (int64, error)
Exec(string, ...interface{}) (sql.Result, error)
Insert(...interface{}) (int64, error)
}
var (
x *xorm.Engine
tables []interface{}
HasEngine bool
DbCfg struct {
@@ -32,12 +39,13 @@ var (
)
func init() {
tables = append(tables, new(User), new(PublicKey),
tables = append(tables,
new(User), new(PublicKey), new(Follow), new(Oauth2), new(AccessToken),
new(Repository), new(Watch), new(Star), new(Action), new(Access),
new(Issue), new(Comment), new(Oauth2), new(Follow),
new(Mirror), new(Release), new(LoginSource), new(Webhook), new(IssueUser),
new(Milestone), new(Label), new(HookTask), new(Team), new(OrgUser), new(TeamUser),
new(UpdateTask), new(Attachment))
new(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone),
new(Mirror), new(Release), new(LoginSource), new(Webhook),
new(UpdateTask), new(HookTask), new(Team), new(OrgUser), new(TeamUser),
new(Notice))
}
func LoadModelsConfig() {
@@ -87,7 +95,7 @@ func getEngine() (*xorm.Engine, error) {
func NewTestEngine(x *xorm.Engine) (err error) {
x, err = getEngine()
if err != nil {
return fmt.Errorf("models.init(fail to conntect database): %v", err)
return fmt.Errorf("models.init(fail to connect to database): %v", err)
}
return x.Sync(tables...)
@@ -96,10 +104,10 @@ func NewTestEngine(x *xorm.Engine) (err error) {
func SetEngine() (err error) {
x, err = getEngine()
if err != nil {
return fmt.Errorf("models.init(fail to conntect database): %v", err)
return fmt.Errorf("models.init(fail to connect to database): %v", err)
}
// WARNNING: for serv command, MUST remove the output to os.stdout,
// WARNING: for serv command, MUST remove the output to os.stdout,
// so use log file to instead print to stdout.
logPath := path.Join(setting.LogRootPath, "xorm.log")
os.MkdirAll(path.Dir(logPath), os.ModePerm)
@@ -122,7 +130,7 @@ func NewEngine() (err error) {
if err = SetEngine(); err != nil {
return err
}
if err = x.Sync2(tables...); err != nil {
if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
return fmt.Errorf("sync database struct error: %v\n", err)
}
return nil

View File

@@ -79,7 +79,7 @@ func UpdateOauth2(oa *Oauth2) error {
return err
}
// GetOauthByUserId returns list of oauthes that are releated to given user.
// GetOauthByUserId returns list of oauthes that are related to given user.
func GetOauthByUserId(uid int64) ([]*Oauth2, error) {
socials := make([]*Oauth2, 0, 5)
err := x.Find(&socials, Oauth2{Uid: uid})

View File

@@ -25,8 +25,8 @@ var (
ErrLastOrgOwner = errors.New("The user to remove is the last member in owner team")
)
// IsOrgOwner returns true if given user is in the owner team.
func (org *User) IsOrgOwner(uid int64) bool {
// IsOwnedBy returns true if given user is in the owner team.
func (org *User) IsOwnedBy(uid int64) bool {
return IsOrganizationOwner(org.Id, uid)
}
@@ -77,6 +77,17 @@ func (org *User) RemoveMember(uid int64) error {
return RemoveOrgUser(org.Id, uid)
}
// IsOrgEmailUsed returns true if the e-mail has been used in organization account.
func IsOrgEmailUsed(email string) (bool, error) {
if len(email) == 0 {
return false, nil
}
return x.Get(&User{
Email: email,
Type: ORGANIZATION,
})
}
// CreateOrganization creates record of a new organization.
func CreateOrganization(org, owner *User) (*User, error) {
if !IsLegalName(org.Name) {
@@ -90,7 +101,7 @@ func CreateOrganization(org, owner *User) (*User, error) {
return nil, ErrUserAlreadyExist
}
isExist, err = IsEmailUsed(org.Email)
isExist, err = IsOrgEmailUsed(org.Email)
if err != nil {
return nil, err
} else if isExist {
@@ -159,6 +170,24 @@ func CreateOrganization(org, owner *User) (*User, error) {
return org, sess.Commit()
}
// GetOrgByName returns organization by given name.
func GetOrgByName(name string) (*User, error) {
if len(name) == 0 {
return nil, ErrOrgNotExist
}
u := &User{
LowerName: strings.ToLower(name),
Type: ORGANIZATION,
}
has, err := x.Get(u)
if err != nil {
return nil, err
} else if !has {
return nil, ErrOrgNotExist
}
return u, nil
}
// CountOrganizations returns number of organizations.
func CountOrganizations() int64 {
count, _ := x.Where("type=1").Count(new(User))
@@ -229,7 +258,7 @@ func IsOrganizationMember(orgId, uid int64) bool {
return has
}
// IsPublicMembership returns ture if given user public his/her membership.
// IsPublicMembership returns true if given user public his/her membership.
func IsPublicMembership(orgId, uid int64) bool {
has, _ := x.Where("uid=?", uid).And("org_id=?", orgId).And("is_public=?", true).Get(new(OrgUser))
return has
@@ -845,23 +874,12 @@ func IsTeamMember(orgId, teamId, uid int64) bool {
// GetTeamMembers returns all members in given team of organization.
func GetTeamMembers(orgId, teamId int64) ([]*User, error) {
tus := make([]*TeamUser, 0, 10)
err := x.Where("org_id=?", orgId).And("team_id=?", teamId).Find(&tus)
if err != nil {
return nil, err
}
us := make([]*User, len(tus))
for i, tu := range tus {
us[i], err = GetUserById(tu.Uid)
if err != nil {
return nil, err
}
}
return us, nil
us := make([]*User, 0, 10)
err := x.Sql("SELECT * FROM `user` JOIN `team_user` ON `team_user`.`team_id` = ? AND `team_user`.`uid` = `user`.`id`", teamId).Find(&us)
return us, err
}
// GetUserTeams returns all teams that user belongs to in given origanization.
// GetUserTeams returns all teams that user belongs to in given organization.
func GetUserTeams(orgId, uid int64) ([]*Team, error) {
tus := make([]*TeamUser, 0, 5)
if err := x.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil {

View File

@@ -33,6 +33,7 @@ const (
var (
ErrKeyAlreadyExist = errors.New("Public key already exist")
ErrKeyNotExist = errors.New("Public key does not exist")
ErrKeyUnableVerify = errors.New("Unable to verify public key")
)
var sshOpLocker = sync.Mutex{}
@@ -78,9 +79,9 @@ func init() {
// PublicKey represents a SSH key.
type PublicKey struct {
Id int64
OwnerId int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
Name string `xorm:"UNIQUE(s) NOT NULL"`
Fingerprint string
OwnerId int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
Name string `xorm:"UNIQUE(s) NOT NULL"`
Fingerprint string `xorm:"INDEX NOT NULL"`
Content string `xorm:"TEXT NOT NULL"`
Created time.Time `xorm:"CREATED"`
Updated time.Time
@@ -88,6 +89,11 @@ type PublicKey struct {
HasUsed bool `xorm:"-"`
}
// OmitEmail returns content of public key but without e-mail address.
func (k *PublicKey) OmitEmail() string {
return strings.Join(strings.Split(k.Content, " ")[:2], " ")
}
// GetAuthorizedString generates and returns formatted public key string for authorized_keys file.
func (key *PublicKey) GetAuthorizedString() string {
return fmt.Sprintf(_TPL_PUBLICK_KEY, appPath, key.Id, key.Content)
@@ -101,13 +107,15 @@ var (
"(MCE)": 1702,
"(McE)": 1702,
"(RSA)": 2048,
"(DSA)": 1024,
}
)
// CheckPublicKeyString checks if the given public key string is recognized by SSH.
func CheckPublicKeyString(content string) (bool, error) {
content = strings.TrimRight(content, "\n\r")
if strings.ContainsAny(content, "\n\r") {
return false, errors.New("Only a single line with a single key please")
return false, errors.New("only a single line with a single key please")
}
// write the key to a file…
@@ -125,7 +133,7 @@ func CheckPublicKeyString(content string) (bool, error) {
if err != nil {
return false, errors.New("ssh-keygen -l -f: " + stderr)
} else if len(stdout) < 2 {
return false, errors.New("ssh-keygen returned not enough output to evaluate the key")
return false, errors.New("ssh-keygen returned not enough output to evaluate the key: " + stdout)
}
// The ssh-keygen in Windows does not print key type, so no need go further.
@@ -133,21 +141,22 @@ func CheckPublicKeyString(content string) (bool, error) {
return true, nil
}
fmt.Println(stdout)
sshKeygenOutput := strings.Split(stdout, " ")
if len(sshKeygenOutput) < 4 {
return false, errors.New("Not enough fields returned by ssh-keygen -l -f")
return false, ErrKeyUnableVerify
}
// Check if key type and key size match.
keySize, err := com.StrTo(sshKeygenOutput[0]).Int()
if err != nil {
return false, errors.New("Cannot get key size of the given key")
keySize := com.StrTo(sshKeygenOutput[0]).MustInt()
if keySize == 0 {
return false, errors.New("cannot get key size of the given key")
}
keyType := strings.TrimSpace(sshKeygenOutput[len(sshKeygenOutput)-1])
if minimumKeySize := MinimumKeySize[keyType]; minimumKeySize == 0 {
return false, errors.New("Sorry, unrecognized public key type")
return false, errors.New("sorry, unrecognized public key type")
} else if keySize < minimumKeySize {
return false, fmt.Errorf("The minimum accepted size of a public key %s is %d", keyType, minimumKeySize)
return false, fmt.Errorf("the minimum accepted size of a public key %s is %d", keyType, minimumKeySize)
}
return true, nil
@@ -203,9 +212,12 @@ func AddPublicKey(key *PublicKey) (err error) {
if err != nil {
return errors.New("ssh-keygen -l -f: " + stderr)
} else if len(stdout) < 2 {
return errors.New("Not enough output for calculating fingerprint")
return errors.New("not enough output for calculating fingerprint: " + stdout)
}
key.Fingerprint = strings.Split(stdout, " ")[1]
if has, err := x.Get(&PublicKey{Fingerprint: key.Fingerprint}); err == nil && has {
return ErrKeyAlreadyExist
}
// Save SSH key.
if _, err = x.Insert(key); err != nil {
@@ -233,10 +245,10 @@ func GetPublicKeyById(keyId int64) (*PublicKey, error) {
return key, nil
}
// ListPublicKey returns a list of all public keys that user has.
func ListPublicKey(uid int64) ([]*PublicKey, error) {
// ListPublicKeys returns a list of public keys belongs to given user.
func ListPublicKeys(uid int64) ([]*PublicKey, error) {
keys := make([]*PublicKey, 0, 5)
err := x.Find(&keys, &PublicKey{OwnerId: uid})
err := x.Where("owner_id=?", uid).Find(&keys)
if err != nil {
return nil, err
}

View File

@@ -23,6 +23,7 @@ import (
"github.com/Unknwon/cae/zip"
"github.com/Unknwon/com"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process"
@@ -48,7 +49,7 @@ var (
)
var (
DescriptionPattern = regexp.MustCompile(`https?://\S+`)
DescPattern = regexp.MustCompile(`https?://\S+`)
)
func LoadRepoConfig() {
@@ -132,14 +133,15 @@ func NewRepoContext() {
// Repository represents a git repository.
type Repository struct {
Id int64
OwnerId int64 `xorm:"UNIQUE(s)"`
Owner *User `xorm:"-"`
ForkId int64
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
Name string `xorm:"INDEX NOT NULL"`
Description string
Website string
Id int64
OwnerId int64 `xorm:"UNIQUE(s)"`
Owner *User `xorm:"-"`
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
Name string `xorm:"INDEX NOT NULL"`
Description string
Website string
DefaultBranch string
NumWatches int
NumStars int
NumForks int
@@ -153,19 +155,26 @@ type Repository struct {
NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"`
NumOpenMilestones int `xorm:"-"`
NumTags int `xorm:"-"`
IsPrivate bool
IsMirror bool
*Mirror `xorm:"-"`
IsFork bool `xorm:"NOT NULL DEFAULT false"`
IsBare bool
IsGoget bool
DefaultBranch string
Created time.Time `xorm:"CREATED"`
Updated time.Time `xorm:"UPDATED"`
IsPrivate bool
IsBare bool
IsGoget bool
IsMirror bool
*Mirror `xorm:"-"`
IsFork bool `xorm:"NOT NULL DEFAULT false"`
ForkId int64
ForkRepo *Repository `xorm:"-"`
Created time.Time `xorm:"CREATED"`
Updated time.Time `xorm:"UPDATED"`
}
func (repo *Repository) GetOwner() (err error) {
repo.Owner, err = GetUserById(repo.OwnerId)
if repo.Owner == nil {
repo.Owner, err = GetUserById(repo.OwnerId)
}
return err
}
@@ -174,6 +183,41 @@ func (repo *Repository) GetMirror() (err error) {
return err
}
func (repo *Repository) GetForkRepo() (err error) {
if !repo.IsFork {
return nil
}
repo.ForkRepo, err = GetRepositoryById(repo.ForkId)
return err
}
func (repo *Repository) RepoPath() (string, error) {
if err := repo.GetOwner(); err != nil {
return "", err
}
return RepoPath(repo.Owner.Name, repo.Name), nil
}
func (repo *Repository) RepoLink() (string, error) {
if err := repo.GetOwner(); err != nil {
return "", err
}
return setting.AppSubUrl + "/" + repo.Owner.Name + "/" + repo.Name, nil
}
func (repo *Repository) IsOwnedBy(u *User) bool {
return repo.OwnerId == u.Id
}
func (repo *Repository) HasAccess(uname string) bool {
if err := repo.GetOwner(); err != nil {
return false
}
has, _ := HasAccess(uname, path.Join(repo.Owner.Name, repo.Name), READABLE)
return has
}
// DescriptionHtml does special handles to description and return HTML string.
func (repo *Repository) DescriptionHtml() template.HTML {
sanitize := func(s string) string {
@@ -181,7 +225,7 @@ func (repo *Repository) DescriptionHtml() template.HTML {
ss := html.EscapeString(s)
return fmt.Sprintf(`<a href="%s" target="_blank">%s</a>`, ss, ss)
}
return template.HTML(DescriptionPattern.ReplaceAllStringFunc(repo.Description, sanitize))
return template.HTML(DescPattern.ReplaceAllStringFunc(base.XSSString(repo.Description), sanitize))
}
// IsRepositoryExist returns true if the repository with given name under user has already existed.
@@ -197,9 +241,30 @@ func IsRepositoryExist(u *User, repoName string) (bool, error) {
return com.IsDir(RepoPath(u.Name, repoName)), nil
}
// CloneLink represents different types of clone URLs of repository.
type CloneLink struct {
SSH string
HTTPS string
Git string
}
// CloneLink returns clone URLs of repository.
func (repo *Repository) CloneLink() (cl CloneLink, err error) {
if err = repo.GetOwner(); err != nil {
return cl, err
}
if setting.SshPort != 22 {
cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.Domain, setting.SshPort, repo.Owner.LowerName, repo.LowerName)
} else {
cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, repo.Owner.LowerName, repo.LowerName)
}
cl.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, repo.Owner.LowerName, repo.LowerName)
return cl, nil
}
var (
illegalEquals = []string{"debug", "raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new"}
illegalSuffixs = []string{".git"}
illegalSuffixs = []string{".git", ".keys"}
)
// IsLegalName returns false if name contains illegal characters.
@@ -264,28 +329,6 @@ func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) er
return nil
}
// MirrorUpdate checks and updates mirror repositories.
func MirrorUpdate() {
if err := x.Iterate(new(Mirror), func(idx int, bean interface{}) error {
m := bean.(*Mirror)
if m.NextUpdate.After(time.Now()) {
return nil
}
repoPath := filepath.Join(setting.RepoRootPath, m.RepoName+".git")
if _, stderr, err := process.ExecDir(10*time.Minute,
repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath),
"git", "remote", "update"); err != nil {
return errors.New("git remote update: " + stderr)
}
m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour)
return UpdateMirror(m)
}); err != nil {
log.Error(4, "repo.MirrorUpdate: %v", err)
}
}
// MigrateRepository migrates a existing repository from other project hosting.
func MigrateRepository(u *User, name, desc string, private, mirror bool, url string) (*Repository, error) {
repo, err := CreateRepository(u, name, desc, "", "", private, mirror, false)
@@ -652,7 +695,7 @@ func RepoPath(userName, repoName string) string {
func TransferOwnership(u *User, newOwner string, repo *Repository) error {
newUser, err := GetUserByName(newOwner)
if err != nil {
return err
return fmt.Errorf("fail to get new owner(%s): %v", newOwner, err)
}
// Check if new owner has repository with same name.
@@ -669,17 +712,34 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
return err
}
if _, err = sess.Where("repo_name = ?", u.LowerName+"/"+repo.LowerName).
And("user_name = ?", u.LowerName).Update(&Access{UserName: newUser.LowerName}); err != nil {
sess.Rollback()
return err
owner := repo.Owner
oldRepoLink := path.Join(owner.LowerName, repo.LowerName)
// Delete all access first if current owner is an organization.
if owner.IsOrganization() {
if _, err = sess.Where("repo_name=?", oldRepoLink).Delete(new(Access)); err != nil {
sess.Rollback()
return fmt.Errorf("fail to delete current accesses: %v", err)
}
} else {
// Delete current owner access.
if _, err = sess.Where("repo_name=?", oldRepoLink).And("user_name=?", owner.LowerName).
Delete(new(Access)); err != nil {
sess.Rollback()
return fmt.Errorf("fail to delete access(owner): %v", err)
}
// In case new owner has access.
if _, err = sess.Where("repo_name=?", oldRepoLink).And("user_name=?", newUser.LowerName).
Delete(new(Access)); err != nil {
sess.Rollback()
return fmt.Errorf("fail to delete access(new user): %v", err)
}
}
if _, err = sess.Where("repo_name = ?", u.LowerName+"/"+repo.LowerName).Update(&Access{
RepoName: newUser.LowerName + "/" + repo.LowerName,
}); err != nil {
// Change accesses to new repository path.
if _, err = sess.Where("repo_name=?", oldRepoLink).
Update(&Access{RepoName: path.Join(newUser.LowerName, repo.LowerName)}); err != nil {
sess.Rollback()
return err
return fmt.Errorf("fail to update access(change reponame): %v", err)
}
// Update repository.
@@ -695,17 +755,17 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
return err
}
if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", u.Id); err != nil {
if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", owner.Id); err != nil {
sess.Rollback()
return err
}
mode := WRITABLE
if repo.IsMirror {
mode = READABLE
}
// New owner is organization.
if newUser.IsOrganization() {
mode := WRITABLE
if repo.IsMirror {
mode = READABLE
}
access := &Access{
RepoName: path.Join(newUser.LowerName, repo.LowerName),
Mode: mode,
@@ -737,10 +797,20 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
sess.Rollback()
return err
}
} else {
access := &Access{
RepoName: path.Join(newUser.LowerName, repo.LowerName),
UserName: newUser.LowerName,
Mode: mode,
}
if _, err = sess.Insert(access); err != nil {
sess.Rollback()
return fmt.Errorf("fail to insert access: %v", err)
}
}
// Change repository directory name.
if err = os.Rename(RepoPath(u.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil {
if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil {
sess.Rollback()
return err
}
@@ -749,14 +819,8 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
return err
}
// Add watch of new owner to repository.
if !newUser.IsOrganization() {
if err = WatchRepo(newUser.Id, repo.Id, true); err != nil {
log.Error(4, "WatchRepo", err)
}
}
if err = WatchRepo(u.Id, repo.Id, false); err != nil {
log.Error(4, "WatchRepo2", err)
if err = WatchRepo(newUser.Id, repo.Id, true); err != nil {
log.Error(4, "WatchRepo", err)
}
if err = TransferRepoAction(u, newUser, repo); err != nil {
@@ -768,6 +832,7 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
func ChangeRepositoryName(userName, oldRepoName, newRepoName string) (err error) {
newRepoName = strings.ToLower(newRepoName)
if !IsLegalName(newRepoName) {
return ErrRepoNameIllegal
}
@@ -813,7 +878,7 @@ func UpdateRepository(repo *Repository) error {
return err
}
// DeleteRepository deletes a repository for a user or orgnaztion.
// DeleteRepository deletes a repository for a user or organization.
func DeleteRepository(uid, repoId int64, userName string) error {
repo := &Repository{Id: repoId, OwnerId: uid}
has, err := x.Get(repo)
@@ -908,13 +973,25 @@ func DeleteRepository(uid, repoId int64, userName string) error {
return err
}
if repo.IsFork {
if _, err = sess.Exec("UPDATE `repository` SET num_forks = num_forks - 1 WHERE id = ?", repo.ForkId); err != nil {
sess.Rollback()
return err
}
}
if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", uid); err != nil {
sess.Rollback()
return err
}
// Remove repository files.
if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil {
sess.Rollback()
return err
desc := fmt.Sprintf("Fail to delete repository files(%s/%s): %v", userName, repo.Name, err)
log.Warn(desc)
if err = CreateRepositoryNotice(desc); err != nil {
log.Error(4, "Fail to add notice: %v", err)
}
}
return sess.Commit()
}
@@ -1003,15 +1080,21 @@ func GetCollaboratorNames(repoName string) ([]string, error) {
return names, nil
}
// CollaborativeRepository represents a repository with collaborative information.
type CollaborativeRepository struct {
*Repository
CanPush bool
}
// GetCollaborativeRepos returns a list of repositories that user is collaborator.
func GetCollaborativeRepos(uname string) ([]*Repository, error) {
func GetCollaborativeRepos(uname string) ([]*CollaborativeRepository, error) {
uname = strings.ToLower(uname)
accesses := make([]*Access, 0, 10)
if err := x.Find(&accesses, &Access{UserName: uname}); err != nil {
return nil, err
}
repos := make([]*Repository, 0, 10)
repos := make([]*CollaborativeRepository, 0, 10)
for _, access := range accesses {
infos := strings.Split(access.RepoName, "/")
if infos[0] == uname {
@@ -1028,7 +1111,7 @@ func GetCollaborativeRepos(uname string) ([]*Repository, error) {
return nil, err
}
repo.Owner = u
repos = append(repos, repo)
repos = append(repos, &CollaborativeRepository{repo, access.Mode == WRITABLE})
}
return repos, nil
}
@@ -1054,17 +1137,11 @@ type SearchOption struct {
Keyword string
Uid int64
Limit int
Private bool
}
// SearchRepositoryByName returns given number of repositories whose name contains keyword.
func SearchRepositoryByName(opt SearchOption) (repos []*Repository, err error) {
// Prevent SQL inject.
opt.Keyword = strings.TrimSpace(opt.Keyword)
if len(opt.Keyword) == 0 {
return repos, nil
}
opt.Keyword = strings.Split(opt.Keyword, " ")[0]
if len(opt.Keyword) == 0 {
return repos, nil
}
@@ -1077,43 +1154,150 @@ func SearchRepositoryByName(opt SearchOption) (repos []*Repository, err error) {
if opt.Uid > 0 {
sess.Where("owner_id=?", opt.Uid)
}
sess.And("lower_name like '%" + opt.Keyword + "%'").Find(&repos)
if !opt.Private {
sess.And("is_private=false")
}
sess.And("lower_name like ?", "%"+opt.Keyword+"%").Find(&repos)
return repos, err
}
// Watch is connection request for receiving repository notifycation.
// DeleteRepositoryArchives deletes all repositories' archives.
func DeleteRepositoryArchives() error {
return x.Where("id > 0").Iterate(new(Repository),
func(idx int, bean interface{}) error {
repo := bean.(*Repository)
if err := repo.GetOwner(); err != nil {
return err
}
return os.RemoveAll(filepath.Join(RepoPath(repo.Owner.Name, repo.Name), "archives"))
})
}
var (
// Prevent duplicate tasks.
isMirrorUpdating = false
isGitFscking = false
)
// MirrorUpdate checks and updates mirror repositories.
func MirrorUpdate() {
if isMirrorUpdating {
return
}
isMirrorUpdating = true
defer func() { isMirrorUpdating = false }()
if err := x.Iterate(new(Mirror), func(idx int, bean interface{}) error {
m := bean.(*Mirror)
if m.NextUpdate.After(time.Now()) {
return nil
}
repoPath := filepath.Join(setting.RepoRootPath, m.RepoName+".git")
if _, stderr, err := process.ExecDir(10*time.Minute,
repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath),
"git", "remote", "update"); err != nil {
return errors.New("git remote update: " + stderr)
}
m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour)
return UpdateMirror(m)
}); err != nil {
log.Error(4, "repo.MirrorUpdate: %v", err)
}
}
// GitFsck calls 'git fsck' to check repository health.
func GitFsck() {
if isGitFscking {
return
}
isGitFscking = true
defer func() { isGitFscking = false }()
args := append([]string{"fsck"}, setting.GitFsckArgs...)
if err := x.Where("id > 0").Iterate(new(Repository),
func(idx int, bean interface{}) error {
repo := bean.(*Repository)
if err := repo.GetOwner(); err != nil {
return err
}
repoPath := RepoPath(repo.Owner.Name, repo.Name)
_, _, err := process.ExecDir(-1, repoPath, "Repository health check", "git", args...)
if err != nil {
desc := fmt.Sprintf("Fail to health check repository(%s)", repoPath)
log.Warn(desc)
if err = CreateRepositoryNotice(desc); err != nil {
log.Error(4, "Fail to add notice: %v", err)
}
}
return nil
}); err != nil {
log.Error(4, "repo.Fsck: %v", err)
}
}
func GitGcRepos() error {
args := append([]string{"gc"}, setting.GitGcArgs...)
return x.Where("id > 0").Iterate(new(Repository),
func(idx int, bean interface{}) error {
repo := bean.(*Repository)
if err := repo.GetOwner(); err != nil {
return err
}
_, stderr, err := process.ExecDir(-1, RepoPath(repo.Owner.Name, repo.Name), "Repository garbage collection", "git", args...)
if err != nil {
return fmt.Errorf("%v: %v", err, stderr)
}
return nil
})
}
// __ __ __ .__
// / \ / \_____ _/ |_ ____ | |__
// \ \/\/ /\__ \\ __\/ ___\| | \
// \ / / __ \| | \ \___| Y \
// \__/\ / (____ /__| \___ >___| /
// \/ \/ \/ \/
// Watch is connection request for receiving repository notification.
type Watch struct {
Id int64
UserId int64 `xorm:"UNIQUE(watch)"`
RepoId int64 `xorm:"UNIQUE(watch)"`
}
// Watch or unwatch repository.
func WatchRepo(uid, repoId int64, watch bool) (err error) {
// IsWatching checks if user has watched given repository.
func IsWatching(uid, repoId int64) bool {
has, _ := x.Get(&Watch{0, uid, repoId})
return has
}
func watchRepoWithEngine(e Engine, uid, repoId int64, watch bool) (err error) {
if watch {
if IsWatching(uid, repoId) {
return nil
}
if _, err = x.Insert(&Watch{RepoId: repoId, UserId: uid}); err != nil {
if _, err = e.Insert(&Watch{RepoId: repoId, UserId: uid}); err != nil {
return err
}
_, err = x.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", repoId)
_, err = e.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", repoId)
} else {
if !IsWatching(uid, repoId) {
return nil
}
if _, err = x.Delete(&Watch{0, uid, repoId}); err != nil {
if _, err = e.Delete(&Watch{0, uid, repoId}); err != nil {
return err
}
_, err = x.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repoId)
_, err = e.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repoId)
}
return err
}
// IsWatching checks if user has watched given repository.
func IsWatching(uid, rid int64) bool {
has, _ := x.Get(&Watch{0, uid, rid})
return has
// Watch or unwatch repository.
func WatchRepo(uid, repoId int64, watch bool) (err error) {
return watchRepoWithEngine(x, uid, repoId, watch)
}
// GetWatchers returns all watchers of given repository.
@@ -1151,6 +1335,13 @@ func NotifyWatchers(act *Action) error {
return nil
}
// _________ __
// / _____// |______ _______
// \_____ \\ __\__ \\_ __ \
// / \| | / __ \| | \/
// /_______ /|__| (____ /__|
// \/ \/
type Star struct {
Id int64
Uid int64 `xorm:"UNIQUE(s)"`
@@ -1165,16 +1356,20 @@ func StarRepo(uid, repoId int64, star bool) (err error) {
}
if _, err = x.Insert(&Star{Uid: uid, RepoId: repoId}); err != nil {
return err
} else if _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repoId); err != nil {
return err
}
_, err = x.Exec("UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repoId)
_, err = x.Exec("UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", uid)
} else {
if !IsStaring(uid, repoId) {
return nil
}
if _, err = x.Delete(&Star{0, uid, repoId}); err != nil {
return err
} else if _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repoId); err != nil {
return err
}
_, err = x.Exec("UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repoId)
_, err = x.Exec("UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?", uid)
}
return err
}
@@ -1185,6 +1380,157 @@ func IsStaring(uid, repoId int64) bool {
return has
}
func ForkRepository(repoName string, uid int64) {
// ___________ __
// \_ _____/__________| | __
// | __)/ _ \_ __ \ |/ /
// | \( <_> ) | \/ <
// \___ / \____/|__| |__|_ \
// \/ \/
func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repository, error) {
isExist, err := IsRepositoryExist(u, name)
if err != nil {
return nil, err
} else if isExist {
return nil, ErrRepoAlreadyExist
}
// In case the old repository is a fork.
if oldRepo.IsFork {
oldRepo, err = GetRepositoryById(oldRepo.ForkId)
if err != nil {
return nil, err
}
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return nil, err
}
repo := &Repository{
OwnerId: u.Id,
Owner: u,
Name: name,
LowerName: strings.ToLower(name),
Description: desc,
IsPrivate: oldRepo.IsPrivate,
IsFork: true,
ForkId: oldRepo.Id,
}
if _, err = sess.Insert(repo); err != nil {
sess.Rollback()
return nil, err
}
var t *Team // Owner team.
mode := WRITABLE
access := &Access{
UserName: u.LowerName,
RepoName: path.Join(u.LowerName, repo.LowerName),
Mode: mode,
}
// Give access to all members in owner team.
if u.IsOrganization() {
t, err = u.GetOwnerTeam()
if err != nil {
sess.Rollback()
return nil, err
}
if err = t.GetMembers(); err != nil {
sess.Rollback()
return nil, err
}
for _, u := range t.Members {
access.Id = 0
access.UserName = u.LowerName
if _, err = sess.Insert(access); err != nil {
sess.Rollback()
return nil, err
}
}
} else {
if _, err = sess.Insert(access); err != nil {
sess.Rollback()
return nil, err
}
}
if _, err = sess.Exec(
"UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil {
sess.Rollback()
return nil, err
}
// Update owner team info and count.
if u.IsOrganization() {
t.RepoIds += "$" + com.ToStr(repo.Id) + "|"
t.NumRepos++
if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
sess.Rollback()
return nil, err
}
}
if u.IsOrganization() {
t, err := u.GetOwnerTeam()
if err != nil {
log.Error(4, "GetOwnerTeam: %v", err)
} else {
if err = t.GetMembers(); err != nil {
log.Error(4, "GetMembers: %v", err)
} else {
for _, u := range t.Members {
if err = watchRepoWithEngine(sess, u.Id, repo.Id, true); err != nil {
log.Error(4, "WatchRepo2: %v", err)
}
}
}
}
} else {
if err = watchRepoWithEngine(sess, u.Id, repo.Id, true); err != nil {
log.Error(4, "WatchRepo3: %v", err)
}
}
if err = NewRepoAction(u, repo); err != nil {
log.Error(4, "NewRepoAction: %v", err)
}
if _, err = sess.Exec(
"UPDATE `repository` SET num_forks = num_forks + 1 WHERE id = ?", oldRepo.Id); err != nil {
sess.Rollback()
return nil, err
}
oldRepoPath, err := oldRepo.RepoPath()
if err != nil {
sess.Rollback()
return nil, fmt.Errorf("fail to get repo path(%s): %v", oldRepo.Name, err)
}
if err = sess.Commit(); err != nil {
return nil, err
}
repoPath := RepoPath(u.Name, repo.Name)
_, stderr, err := process.ExecTimeout(10*time.Minute,
fmt.Sprintf("ForkRepository(git clone): %s/%s", u.Name, repo.Name),
"git", "clone", "--bare", oldRepoPath, repoPath)
if err != nil {
return nil, errors.New("ForkRepository(git clone): " + stderr)
}
_, stderr, err = process.ExecDir(-1,
repoPath, fmt.Sprintf("ForkRepository(git update-server-info): %s", repoPath),
"git", "update-server-info")
if err != nil {
return nil, errors.New("ForkRepository(git update-server-info): " + stderr)
}
return repo, nil
}

View File

@@ -16,8 +16,6 @@ const (
)
type Slack struct {
Domain string `json:"domain"`
Token string `json:"token"`
Channel string `json:"channel"`
}
@@ -36,14 +34,6 @@ type SlackAttachment struct {
Text string `json:"text"`
}
func GetSlackURL(domain string, token string) string {
return fmt.Sprintf(
"https://%s.slack.com/services/hooks/incoming-webhook?token=%s",
domain,
token,
)
}
func (p SlackPayload) GetJSONPayload() ([]byte, error) {
data, err := json.Marshal(p)
if err != nil {

69
models/token.go Normal file
View File

@@ -0,0 +1,69 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"errors"
"time"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/uuid"
)
var (
ErrAccessTokenNotExist = errors.New("Access token does not exist")
)
// AccessToken represents a personal access token.
type AccessToken struct {
Id int64
Uid int64
Name string
Sha1 string `xorm:"UNIQUE VARCHAR(40)"`
Created time.Time `xorm:"CREATED"`
Updated time.Time
HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"`
}
// NewAccessToken creates new access token.
func NewAccessToken(t *AccessToken) error {
t.Sha1 = base.EncodeSha1(uuid.NewV4().String())
_, err := x.Insert(t)
return err
}
// GetAccessTokenBySha returns access token by given sha1.
func GetAccessTokenBySha(sha string) (*AccessToken, error) {
t := &AccessToken{Sha1: sha}
has, err := x.Get(t)
if err != nil {
return nil, err
} else if !has {
return nil, ErrAccessTokenNotExist
}
return t, nil
}
// ListAccessTokens returns a list of access tokens belongs to given user.
func ListAccessTokens(uid int64) ([]*AccessToken, error) {
tokens := make([]*AccessToken, 0, 5)
err := x.Where("uid=?", uid).Desc("id").Find(&tokens)
if err != nil {
return nil, err
}
for _, t := range tokens {
t.HasUsed = t.Updated.After(t.Created)
t.HasRecentActivity = t.Updated.Add(7 * 24 * time.Hour).After(time.Now())
}
return tokens, nil
}
// DeleteAccessTokenById deletes access token by given ID.
func DeleteAccessTokenById(id int64) error {
_, err := x.Id(id).Delete(new(AccessToken))
return err
}

View File

@@ -23,6 +23,10 @@ type UpdateTask struct {
NewCommitId string
}
const (
MAX_COMMITS int = 5
)
func AddUpdateTask(task *UpdateTask) error {
_, err := x.Insert(task)
return err
@@ -102,7 +106,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
if err = CommitRepoAction(userId, ru.Id, userName, actEmail,
repos.Id, repoUserName, repoName, refName, commit, oldCommitId, newCommitId); err != nil {
log.GitLogger.Fatal(4, "runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
log.GitLogger.Fatal(4, "CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
}
return err
}
@@ -112,8 +116,8 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
return fmt.Errorf("runUpdate GetCommit of newCommitId: %v", err)
}
// Push new branch.
var l *list.List
// if a new branch
if isNew {
l, err = newCommit.CommitsBefore()
if err != nil {
@@ -130,9 +134,8 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
return fmt.Errorf("runUpdate.Commit repoId: %v", err)
}
// if commits push
// Push commits.
commits := make([]*base.PushCommit, 0)
var maxCommits = 2
var actEmail string
for e := l.Front(); e != nil; e = e.Next() {
commit := e.Value.(*git.Commit)
@@ -145,14 +148,13 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
commit.Message(),
commit.Author.Email,
commit.Author.Name})
if len(commits) >= maxCommits {
if len(commits) >= MAX_COMMITS {
break
}
}
//commits = append(commits, []string{lastCommit.Id().String(), lastCommit.Message()})
if err = CommitRepoAction(userId, ru.Id, userName, actEmail,
repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits}, oldCommitId, newCommitId); err != nil {
repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits, ""}, oldCommitId, newCommitId); err != nil {
return fmt.Errorf("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
}
return nil

View File

@@ -5,17 +5,23 @@
package models
import (
"bytes"
"container/list"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"image"
"image/jpeg"
"os"
"path/filepath"
"strings"
"time"
"github.com/Unknwon/com"
"github.com/nfnt/resize"
"github.com/gogits/gogs/modules/avatar"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/log"
@@ -44,32 +50,40 @@ var (
// User represents the object of individual and member of organization.
type User struct {
Id int64
LowerName string `xorm:"UNIQUE NOT NULL"`
Name string `xorm:"UNIQUE NOT NULL"`
FullName string
Email string `xorm:"UNIQUE NOT NULL"`
Passwd string `xorm:"NOT NULL"`
LoginType LoginType
LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
LoginName string
Type UserType
Orgs []*User `xorm:"-"`
Repos []*Repository `xorm:"-"`
Id int64
LowerName string `xorm:"UNIQUE NOT NULL"`
Name string `xorm:"UNIQUE NOT NULL"`
FullName string
Email string `xorm:"UNIQUE(s) NOT NULL"`
Passwd string `xorm:"NOT NULL"`
LoginType LoginType
LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
LoginName string
Type UserType `xorm:"UNIQUE(s)"`
Orgs []*User `xorm:"-"`
Repos []*Repository `xorm:"-"`
Location string
Website string
Rands string `xorm:"VARCHAR(10)"`
Salt string `xorm:"VARCHAR(10)"`
Created time.Time `xorm:"CREATED"`
Updated time.Time `xorm:"UPDATED"`
// Permissions.
IsActive bool
IsAdmin bool
AllowGitHook bool
// Avatar.
Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
AvatarEmail string `xorm:"NOT NULL"`
UseCustomAvatar bool
// Counters.
NumFollowers int
NumFollowings int
NumStars int
NumRepos int
Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
AvatarEmail string `xorm:"NOT NULL"`
Location string
Website string
IsActive bool
IsAdmin bool
Rands string `xorm:"VARCHAR(10)"`
Salt string `xorm:"VARCHAR(10)"`
Created time.Time `xorm:"CREATED"`
Updated time.Time `xorm:"UPDATED"`
// For organization.
Description string
@@ -82,24 +96,27 @@ type User struct {
// DashboardLink returns the user dashboard page link.
func (u *User) DashboardLink() string {
if u.IsOrganization() {
return "/org/" + u.Name + "/dashboard/"
return setting.AppSubUrl + "/org/" + u.Name + "/dashboard/"
}
return "/"
return setting.AppSubUrl + "/"
}
// HomeLink returns the user home page link.
func (u *User) HomeLink() string {
return "/user/" + u.Name
return setting.AppSubUrl + "/" + u.Name
}
// AvatarLink returns user gravatar link.
func (u *User) AvatarLink() string {
if setting.DisableGravatar {
return "/img/avatar_default.jpg"
} else if setting.Service.EnableCacheAvatar {
return "/avatar/" + u.Avatar
switch {
case u.UseCustomAvatar:
return setting.AppSubUrl + "/avatars/" + com.ToStr(u.Id)
case setting.DisableGravatar:
return setting.AppSubUrl + "/img/avatar_default.jpg"
case setting.Service.EnableCacheAvatar:
return setting.AppSubUrl + "/avatar/" + u.Avatar
}
return "//1.gravatar.com/avatar/" + u.Avatar
return setting.GravatarSource + u.Avatar
}
// NewGitSig generates and returns the signature of given user.
@@ -124,6 +141,48 @@ func (u *User) ValidtePassword(passwd string) bool {
return u.Passwd == newUser.Passwd
}
// CustomAvatarPath returns user custom avatar file path.
func (u *User) CustomAvatarPath() string {
return filepath.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
}
// UploadAvatar saves custom avatar for user.
// FIXME: split uploads to different subdirs in case we have massive users.
func (u *User) UploadAvatar(data []byte) error {
u.UseCustomAvatar = true
img, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return err
}
m := resize.Resize(200, 200, img, resize.NearestNeighbor)
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Id(u.Id).AllCols().Update(u); err != nil {
sess.Rollback()
return err
}
os.MkdirAll(setting.AvatarUploadPath, os.ModePerm)
fw, err := os.Create(u.CustomAvatarPath())
if err != nil {
sess.Rollback()
return err
}
defer fw.Close()
if err = jpeg.Encode(fw, m, nil); err != nil {
sess.Rollback()
return err
}
return sess.Commit()
}
// IsOrganization returns true if user is actually a organization.
func (u *User) IsOrganization() bool {
return u.Type == ORGANIZATION
@@ -192,7 +251,7 @@ func IsEmailUsed(email string) (bool, error) {
return x.Get(&User{Email: email})
}
// GetUserSalt returns a user salt token
// GetUserSalt returns a ramdom user salt token.
func GetUserSalt() string {
return base.GetRandomString(10)
}
@@ -218,8 +277,8 @@ func CreateUser(u *User) error {
}
u.LowerName = strings.ToLower(u.Name)
u.Avatar = base.EncodeMd5(u.Email)
u.AvatarEmail = u.Email
u.Avatar = avatar.HashEmail(u.AvatarEmail)
u.Rands = GetUserSalt()
u.Salt = GetUserSalt()
u.EncodePasswd()
@@ -359,6 +418,13 @@ func ChangeUserName(u *User, newUserName string) (err error) {
// UpdateUser updates user's information.
func UpdateUser(u *User) error {
has, err := x.Where("id != ?", u.Id).And("email = ?", u.Email).Get(new(User))
if err != nil {
return err
} else if has {
return ErrEmailAlreadyUsed
}
u.LowerName = strings.ToLower(u.Name)
if len(u.Location) > 255 {
@@ -371,11 +437,16 @@ func UpdateUser(u *User) error {
u.Description = u.Description[:255]
}
_, err := x.Id(u.Id).AllCols().Update(u)
if u.AvatarEmail == "" {
u.AvatarEmail = u.Email
}
u.Avatar = avatar.HashEmail(u.AvatarEmail)
_, err = x.Id(u.Id).AllCols().Update(u)
return err
}
// TODO: need some kind of mechanism to record failure.
// FIXME: need some kind of mechanism to record failure. HINT: system notice
// DeleteUser completely and permanently deletes everything of user.
func DeleteUser(u *User) error {
// Check ownership of repository.
@@ -389,13 +460,13 @@ func DeleteUser(u *User) error {
// Check membership of organization.
count, err = u.GetOrganizationCount()
if err != nil {
return errors.New("modesl.GetRepositories(GetOrganizationCount): " + err.Error())
return errors.New("GetOrganizationCount: " + err.Error())
} else if count > 0 {
return ErrUserHasOrgs
}
// TODO: check issues, other repos' commits
// TODO: roll backable in some point.
// FIXME: check issues, other repos' commits
// FIXME: roll backable in some point.
// Delete all followers.
if _, err = x.Delete(&Follow{FollowId: u.Id}); err != nil {
@@ -472,22 +543,22 @@ func GetUserById(id int64) (*User, error) {
return u, nil
}
// GetUserByName returns the user object by given name if exists.
// GetUserByName returns user by given name.
func GetUserByName(name string) (*User, error) {
if len(name) == 0 {
return nil, ErrUserNotExist
}
user := &User{LowerName: strings.ToLower(name)}
has, err := x.Get(user)
u := &User{LowerName: strings.ToLower(name)}
has, err := x.Get(u)
if err != nil {
return nil, err
} else if !has {
return nil, ErrUserNotExist
}
return user, nil
return u, nil
}
// GetUserEmailsByNames returns a slice of e-mails corresponds to names.
// GetUserEmailsByNames returns a list of e-mails corresponds to names.
func GetUserEmailsByNames(names []string) []string {
mails := make([]string, 0, len(names))
for _, name := range names {
@@ -513,6 +584,46 @@ func GetUserIdsByNames(names []string) []int64 {
return ids
}
// UserCommit represents a commit with validation of user.
type UserCommit struct {
User *User
*git.Commit
}
// ValidateCommitWithEmail chceck if author's e-mail of commit is corresponsind to a user.
func ValidateCommitWithEmail(c *git.Commit) *User {
u, err := GetUserByEmail(c.Author.Email)
if err != nil {
return nil
}
return u
}
// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
emails := map[string]*User{}
newCommits := list.New()
e := oldCommits.Front()
for e != nil {
c := e.Value.(*git.Commit)
var u *User
if v, ok := emails[c.Author.Email]; !ok {
u, _ = GetUserByEmail(c.Author.Email)
emails[c.Author.Email] = u
} else {
u = v
}
newCommits.PushBack(UserCommit{
User: u,
Commit: c,
})
e = e.Next()
}
return newCommits
}
// GetUserByEmail returns the user object by given e-mail if exists.
func GetUserByEmail(email string) (*User, error) {
if len(email) == 0 {
@@ -530,24 +641,17 @@ func GetUserByEmail(email string) (*User, error) {
// SearchUserByName returns given number of users whose name contains keyword.
func SearchUserByName(opt SearchOption) (us []*User, err error) {
// Prevent SQL inject.
opt.Keyword = strings.TrimSpace(opt.Keyword)
if len(opt.Keyword) == 0 {
return us, nil
}
opt.Keyword = strings.Split(opt.Keyword, " ")[0]
if len(opt.Keyword) == 0 {
return us, nil
}
opt.Keyword = strings.ToLower(opt.Keyword)
us = make([]*User, 0, opt.Limit)
err = x.Limit(opt.Limit).Where("type=0").And("lower_name like '%" + opt.Keyword + "%'").Find(&us)
err = x.Limit(opt.Limit).Where("type=0").And("lower_name like ?", "%"+opt.Keyword+"%").Find(&us)
return us, err
}
// Follow is connection request for receiving user notifycation.
// Follow is connection request for receiving user notification.
type Follow struct {
Id int64
UserId int64 `xorm:"unique(follow)"`
@@ -556,27 +660,27 @@ type Follow struct {
// FollowUser marks someone be another's follower.
func FollowUser(userId int64, followId int64) (err error) {
session := x.NewSession()
defer session.Close()
session.Begin()
sess := x.NewSession()
defer sess.Close()
sess.Begin()
if _, err = session.Insert(&Follow{UserId: userId, FollowId: followId}); err != nil {
session.Rollback()
if _, err = sess.Insert(&Follow{UserId: userId, FollowId: followId}); err != nil {
sess.Rollback()
return err
}
rawSql := "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?"
if _, err = session.Exec(rawSql, followId); err != nil {
session.Rollback()
if _, err = sess.Exec(rawSql, followId); err != nil {
sess.Rollback()
return err
}
rawSql = "UPDATE `user` SET num_followings = num_followings + 1 WHERE id = ?"
if _, err = session.Exec(rawSql, userId); err != nil {
session.Rollback()
if _, err = sess.Exec(rawSql, userId); err != nil {
sess.Rollback()
return err
}
return session.Commit()
return sess.Commit()
}
// UnFollowUser unmarks someone be another's follower.

View File

@@ -27,6 +27,32 @@ const (
FORM
)
var hookContentTypes = map[string]HookContentType{
"json": JSON,
"form": FORM,
}
// ToHookContentType returns HookContentType by given name.
func ToHookContentType(name string) HookContentType {
return hookContentTypes[name]
}
func (t HookContentType) Name() string {
switch t {
case JSON:
return "json"
case FORM:
return "form"
}
return ""
}
// IsValidHookContentType returns true if given name is a valid hook content type.
func IsValidHookContentType(name string) bool {
_, ok := hookContentTypes[name]
return ok
}
// HookEvent represents events that will delivery hook.
type HookEvent struct {
PushOnly bool `json:"push_only"`
@@ -46,6 +72,8 @@ type Webhook struct {
HookTaskType HookTaskType
Meta string `xorm:"TEXT"` // store hook-specific attributes
OrgId int64
Created time.Time `xorm:"CREATED"`
Updated time.Time `xorm:"UPDATED"`
}
// GetEvent handles conversion from Events to HookEvent.
@@ -71,7 +99,7 @@ func (w *Webhook) UpdateEvent() error {
return err
}
// HasPushEvent returns true if hook enbaled push event.
// HasPushEvent returns true if hook enabled push event.
func (w *Webhook) HasPushEvent() bool {
if w.PushOnly {
return true
@@ -147,12 +175,39 @@ const (
SLACK
)
var hookTaskTypes = map[string]HookTaskType{
"gogs": GOGS,
"slack": SLACK,
}
// ToHookTaskType returns HookTaskType by given name.
func ToHookTaskType(name string) HookTaskType {
return hookTaskTypes[name]
}
func (t HookTaskType) Name() string {
switch t {
case GOGS:
return "gogs"
case SLACK:
return "slack"
}
return ""
}
// IsValidHookTaskType returns true if given name is a valid hook task type.
func IsValidHookTaskType(name string) bool {
_, ok := hookTaskTypes[name]
return ok
}
type HookEventType string
const (
PUSH HookEventType = "push"
)
// FIXME: just use go-gogs-client structs maybe?
type PayloadAuthor struct {
Name string `json:"name"`
Email string `json:"email"`
@@ -235,8 +290,22 @@ func UpdateHookTask(t *HookTask) error {
return err
}
var (
// Prevent duplicate deliveries.
// This happens with massive hook tasks cannot finish delivering
// before next shooting starts.
isShooting = false
)
// DeliverHooks checks and delivers undelivered hooks.
// FIXME: maybe can use goroutine to shoot a number of them at same time?
func DeliverHooks() {
if isShooting {
return
}
isShooting = true
defer func() { isShooting = false }()
tasks := make([]*HookTask, 0, 10)
timeout := time.Duration(setting.WebhookDeliverTimeout) * time.Second
x.Where("is_delivered=?", false).Iterate(new(HookTask),
@@ -255,7 +324,7 @@ func DeliverHooks() {
t.IsDelivered = true
// TODO: record response.
// FIXME: record response.
switch t.Type {
case GOGS:
{

View File

@@ -6,22 +6,22 @@ package auth
import (
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/i18n"
"github.com/gogits/gogs/modules/middleware/binding"
"github.com/macaron-contrib/binding"
)
type AdminEditUserForm struct {
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
Passwd string `form:"password"`
Website string `form:"website" binding:"MaxSize(50)"`
Location string `form:"location" binding:"MaxSize(50)"`
Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
Active bool `form:"active"`
Admin bool `form:"admin"`
LoginType int `form:"login_type"`
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
Passwd string `form:"password"`
Website string `form:"website" binding:"MaxSize(50)"`
Location string `form:"location" binding:"MaxSize(50)"`
Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
Active bool `form:"active"`
Admin bool `form:"admin"`
AllowGitHook bool `form:"allow_git_hook"`
LoginType int `form:"login_type"`
}
func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

View File

@@ -8,31 +8,24 @@ import (
"reflect"
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/i18n"
"github.com/macaron-contrib/binding"
"github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/middleware/binding"
)
type MarkdownForm struct {
Text string `form:"text" binding:"Required"`
Text string `form:"text"`
Mode string `form:"mode"`
Context string `form:"context"`
}
func (f *MarkdownForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validateApiReq(errs, ctx.Data, f, l)
func (f *MarkdownForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validateApiReq(errs, ctx.Data, f)
}
func validateApiReq(errs *binding.Errors, data map[string]interface{}, f interface{}, l i18n.Locale) {
if errs.Count() == 0 {
return
} else if len(errs.Overall) > 0 {
for _, err := range errs.Overall {
log.Error(4, "%s: %v", reflect.TypeOf(f), err)
}
return
func validateApiReq(errs binding.Errors, data map[string]interface{}, f auth.Form) binding.Errors {
if errs.Len() == 0 {
return errs
}
data["HasError"] = true
@@ -54,26 +47,27 @@ func validateApiReq(errs *binding.Errors, data map[string]interface{}, f interfa
continue
}
if err, ok := errs.Fields[field.Name]; ok {
switch err {
case binding.BindingRequireError:
if errs[0].FieldNames[0] == field.Name {
switch errs[0].Classification {
case binding.RequiredError:
data["ErrorMsg"] = fieldName + " cannot be empty"
case binding.BindingAlphaDashError:
case binding.AlphaDashError:
data["ErrorMsg"] = fieldName + " must be valid alpha or numeric or dash(-_) characters"
case binding.BindingAlphaDashDotError:
case binding.AlphaDashDotError:
data["ErrorMsg"] = fieldName + " must be valid alpha or numeric or dash(-_) or dot characters"
case binding.BindingMinSizeError:
case binding.MinSizeError:
data["ErrorMsg"] = fieldName + " must contain at least " + auth.GetMinSize(field) + " characters"
case binding.BindingMaxSizeError:
case binding.MaxSizeError:
data["ErrorMsg"] = fieldName + " must contain at most " + auth.GetMaxSize(field) + " characters"
case binding.BindingEmailError:
case binding.EmailError:
data["ErrorMsg"] = fieldName + " is not a valid e-mail address"
case binding.BindingUrlError:
case binding.UrlError:
data["ErrorMsg"] = fieldName + " is not a valid URL"
default:
data["ErrorMsg"] = "Unknown error: " + err
data["ErrorMsg"] = "Unknown error: " + errs[0].Classification
}
return
return errs
}
}
return errs
}

View File

@@ -9,32 +9,38 @@ import (
"reflect"
"strings"
"github.com/macaron-contrib/i18n"
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/binding"
"github.com/macaron-contrib/session"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/middleware/binding"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/uuid"
)
// SignedInId returns the id of signed in user.
func SignedInId(header http.Header, sess session.Store) int64 {
func SignedInId(req *http.Request, sess session.Store) int64 {
if !models.HasEngine {
return 0
}
if setting.Service.EnableReverseProxyAuth {
webAuthUser := header.Get(setting.ReverseProxyAuthUser)
if len(webAuthUser) > 0 {
u, err := models.GetUserByName(webAuthUser)
if err != nil {
if err != models.ErrUserNotExist {
log.Error(4, "GetUserByName: %v", err)
// API calls need to check access token.
if strings.HasPrefix(req.URL.Path, "/api/") {
auHead := req.Header.Get("Authorization")
if len(auHead) > 0 {
auths := strings.Fields(auHead)
if len(auths) == 2 && auths[0] == "token" {
t, err := models.GetAccessTokenBySha(auths[1])
if err != nil {
if err != models.ErrAccessTokenNotExist {
log.Error(4, "GetAccessTokenBySha: %v", err)
}
return 0
}
return 0
return t.Uid
}
return u.Id
}
}
@@ -55,18 +61,78 @@ func SignedInId(header http.Header, sess session.Store) int64 {
}
// SignedInUser returns the user object of signed user.
func SignedInUser(header http.Header, sess session.Store) *models.User {
uid := SignedInId(header, sess)
// It returns a bool value to indicate whether user uses basic auth or not.
func SignedInUser(req *http.Request, sess session.Store) (*models.User, bool) {
if !models.HasEngine {
return nil, false
}
uid := SignedInId(req, sess)
if uid <= 0 {
return nil
if setting.Service.EnableReverseProxyAuth {
webAuthUser := req.Header.Get(setting.ReverseProxyAuthUser)
if len(webAuthUser) > 0 {
u, err := models.GetUserByName(webAuthUser)
if err != nil {
if err != models.ErrUserNotExist {
log.Error(4, "GetUserByName: %v", err)
return nil, false
}
// Check if enabled auto-registration.
if setting.Service.EnableReverseProxyAutoRegister {
u := &models.User{
Name: webAuthUser,
Email: uuid.NewV4().String() + "@localhost",
Passwd: webAuthUser,
IsActive: true,
}
if err = models.CreateUser(u); err != nil {
// FIXME: should I create a system notice?
log.Error(4, "CreateUser: %v", err)
return nil, false
} else {
return u, false
}
}
}
return u, false
}
}
// Check with basic auth.
baHead := req.Header.Get("Authorization")
if len(baHead) > 0 {
auths := strings.Fields(baHead)
if len(auths) == 2 && auths[0] == "Basic" {
uname, passwd, _ := base.BasicAuthDecode(auths[1])
u, err := models.GetUserByName(uname)
if err != nil {
if err != models.ErrUserNotExist {
log.Error(4, "GetUserByName: %v", err)
}
return nil, false
}
if u.ValidtePassword(passwd) {
return u, true
}
}
}
return nil, false
}
u, err := models.GetUserById(uid)
if err != nil {
log.Error(4, "GetUserById: %v", err)
return nil
return nil, false
}
return u
return u, false
}
type Form interface {
binding.Validator
}
// AssignForm assign form values back to the template data.
@@ -109,14 +175,9 @@ func GetMaxSize(field reflect.StructField) string {
return getSize(field, "MaxSize(")
}
func validate(errs *binding.Errors, data map[string]interface{}, f interface{}, l i18n.Locale) {
if errs.Count() == 0 {
return
} else if len(errs.Overall) > 0 {
for _, err := range errs.Overall {
log.Error(4, "%s: %v", reflect.TypeOf(f), err)
}
return
func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaron.Locale) binding.Errors {
if errs.Len() == 0 {
return errs
}
data["HasError"] = true
@@ -139,28 +200,29 @@ func validate(errs *binding.Errors, data map[string]interface{}, f interface{},
continue
}
if err, ok := errs.Fields[field.Name]; ok {
if errs[0].FieldNames[0] == field.Name {
data["Err_"+field.Name] = true
trName := l.Tr("form." + field.Name)
switch err {
case binding.BindingRequireError:
switch errs[0].Classification {
case binding.RequiredError:
data["ErrorMsg"] = trName + l.Tr("form.require_error")
case binding.BindingAlphaDashError:
case binding.AlphaDashError:
data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_error")
case binding.BindingAlphaDashDotError:
case binding.AlphaDashDotError:
data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_error")
case binding.BindingMinSizeError:
case binding.MinSizeError:
data["ErrorMsg"] = trName + l.Tr("form.min_size_error", GetMinSize(field))
case binding.BindingMaxSizeError:
case binding.MaxSizeError:
data["ErrorMsg"] = trName + l.Tr("form.max_size_error", GetMaxSize(field))
case binding.BindingEmailError:
case binding.EmailError:
data["ErrorMsg"] = trName + l.Tr("form.email_error")
case binding.BindingUrlError:
case binding.UrlError:
data["ErrorMsg"] = trName + l.Tr("form.url_error")
default:
data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + err
data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification
}
return
return errs
}
}
return errs
}

View File

@@ -6,9 +6,7 @@ package auth
import (
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/i18n"
"github.com/gogits/gogs/modules/middleware/binding"
"github.com/macaron-contrib/binding"
)
type AuthenticationForm struct {
@@ -31,6 +29,6 @@ type AuthenticationForm struct {
AllowAutoRegister bool `form:"allowautoregister"`
}
func (f *AuthenticationForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *AuthenticationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

View File

@@ -20,7 +20,7 @@ type Ldapsource struct {
Port int // port number
UseSSL bool // Use SSL
BaseDN string // Base DN
Attributes string // Attribut to search
Attributes string // Attribute to search
Filter string // Query filter to validate entry
MsAdSAFormat string // in the case of MS AD Simple Authen, the format to use (see: http://msdn.microsoft.com/en-us/library/cc223499.aspx)
Enabled bool // if this source is disabled
@@ -37,7 +37,7 @@ func AddSource(name string, host string, port int, usessl bool, basedn string, a
Authensource = append(Authensource, ldaphost)
}
//LoginUser : try to login an user to LDAP sources, return requested (attribut,true) if ok, ("",false) other wise
//LoginUser : try to login an user to LDAP sources, return requested (attribute,true) if ok, ("",false) other wise
//First match wins
//Returns first attribute if exists
func LoginUser(name, passwd string) (a string, r bool) {

View File

@@ -6,9 +6,7 @@ package auth
import (
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/i18n"
"github.com/gogits/gogs/modules/middleware/binding"
"github.com/macaron-contrib/binding"
)
// ________ .__ __ .__
@@ -23,8 +21,8 @@ type CreateOrgForm struct {
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
}
func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
type UpdateOrgSettingForm struct {
@@ -37,8 +35,8 @@ type UpdateOrgSettingForm struct {
Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
}
func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
// ___________
@@ -54,6 +52,6 @@ type CreateTeamForm struct {
Permission string `form:"permission"`
}
func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

View File

@@ -1,21 +0,0 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package auth
import (
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/i18n"
"github.com/gogits/gogs/modules/middleware/binding"
)
type AddSSHKeyForm struct {
SSHTitle string `form:"title" binding:"Required"`
Content string `form:"content" binding:"Required"`
}
func (f *AddSSHKeyForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
}

View File

@@ -6,9 +6,7 @@ package auth
import (
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/i18n"
"github.com/gogits/gogs/modules/middleware/binding"
"github.com/macaron-contrib/binding"
)
// _______________________________________ _________.______________________ _______________.___.
@@ -23,13 +21,13 @@ type CreateRepoForm struct {
RepoName string `form:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
Private bool `form:"private"`
Description string `form:"desc" binding:"MaxSize(255)"`
AutoInit bool `form:"auto_init"`
Gitignore string `form:"gitignore"`
License string `form:"license"`
InitReadme bool `form:"init_readme"`
}
func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
type MigrateRepoForm struct {
@@ -43,8 +41,8 @@ type MigrateRepoForm struct {
Description string `form:"desc" binding:"MaxSize(255)"`
}
func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
type RepoSettingForm struct {
@@ -57,8 +55,8 @@ type RepoSettingForm struct {
GoGet bool `form:"goget"`
}
func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
// __ __ ___. .__ .__ __
@@ -77,21 +75,20 @@ type NewWebhookForm struct {
Active bool `form:"active"`
}
func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
type NewSlackHookForm struct {
HookTaskType string `form:"hook_type" binding:"Required"`
Domain string `form:"domain" binding:"Required`
Token string `form:"token" binding:"Required"`
PayloadUrl string `form:"payload_url" binding:"Required`
Channel string `form:"channel" binding:"Required"`
PushOnly bool `form:"push_only"`
Active bool `form:"active"`
}
func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
// .___
@@ -102,15 +99,15 @@ func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs *binding.Errors,
// \/ \/ \/
type CreateIssueForm struct {
IssueName string `form:"title" binding:"Required;MaxSize(50)"`
IssueName string `form:"title" binding:"Required;MaxSize(255)"`
MilestoneId int64 `form:"milestoneid"`
AssigneeId int64 `form:"assigneeid"`
Labels string `form:"labels"`
Content string `form:"content"`
}
func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
// _____ .__.__ __
@@ -126,8 +123,8 @@ type CreateMilestoneForm struct {
Deadline string `form:"due_date"`
}
func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
// .____ ___. .__
@@ -142,8 +139,8 @@ type CreateLabelForm struct {
Color string `form:"color" binding:"Required;Size(7)"`
}
func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
// __________ .__
@@ -162,18 +159,17 @@ type NewReleaseForm struct {
Prerelease bool `form:"prerelease"`
}
func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
type EditReleaseForm struct {
Target string `form:"tag_target" binding:"Required"`
Title string `form:"title" binding:"Required"`
Content string `form:"content" binding:"Required"`
Draft string `form:"draft"`
Prerelease bool `form:"prerelease"`
}
func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

View File

@@ -5,10 +5,10 @@
package auth
import (
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/i18n"
"mime/multipart"
"github.com/gogits/gogs/modules/middleware/binding"
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/binding"
)
type InstallForm struct {
@@ -34,8 +34,8 @@ type InstallForm struct {
AdminEmail string `form:"admin_email" binding:"Required;Email;MaxSize(50)"`
}
func (f *InstallForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
// _____ ____ _________________ ___
@@ -54,8 +54,8 @@ type RegisterForm struct {
LoginName string `form:"loginname"`
}
func (f *RegisterForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
type SignInForm struct {
@@ -64,8 +64,8 @@ type SignInForm struct {
Remember bool `form:"remember"`
}
func (f *SignInForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *SignInForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
// __________________________________________.___ _______ ________ _________
@@ -84,8 +84,17 @@ type UpdateProfileForm struct {
Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
}
func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
type UploadAvatarForm struct {
Enable bool `form:"enable"`
Avatar *multipart.FileHeader `form:"avatar"`
}
func (f *UploadAvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
type ChangePasswordForm struct {
@@ -94,6 +103,23 @@ type ChangePasswordForm struct {
Retype string `form:"retype"`
}
func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
validate(errs, ctx.Data, f, l)
func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
type AddSSHKeyForm struct {
SSHTitle string `form:"title" binding:"Required"`
Content string `form:"content" binding:"Required"`
}
func (f *AddSSHKeyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
type NewAccessTokenForm struct {
Name string `form:"name" binding:"Required"`
}
func (f *NewAccessTokenForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

View File

@@ -33,17 +33,27 @@ import (
"github.com/nfnt/resize"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
var (
gravatar = "http://www.gravatar.com/avatar"
)
var gravatarSource string
func init() {
gravatarSource = setting.GravatarSource
if !strings.HasPrefix(gravatarSource, "http:") {
gravatarSource = "http:" + gravatarSource
}
}
// hash email to md5 string
// keep this func in order to make this package indenpent
// keep this func in order to make this package independent
func HashEmail(email string) string {
// https://en.gravatar.com/site/implement/hash/
email = strings.TrimSpace(email)
email = strings.ToLower(email)
h := md5.New()
h.Write([]byte(strings.ToLower(email)))
h.Write([]byte(email))
return hex.EncodeToString(h.Sum(nil))
}
@@ -115,13 +125,13 @@ func (this *Avatar) Encode(wr io.Writer, size int) (err error) {
if img, err = decodeImageFile(imgPath); err != nil {
return
}
m := resize.Resize(uint(size), 0, img, resize.Lanczos3)
m := resize.Resize(uint(size), 0, img, resize.NearestNeighbor)
return jpeg.Encode(wr, m, nil)
}
// get image from gravatar.com
func (this *Avatar) Update() {
thunder.Fetch(gravatar+"/"+this.Hash+"?"+this.reqParams,
thunder.Fetch(gravatarSource+this.Hash+"?"+this.reqParams,
this.imagePath)
}
@@ -129,7 +139,7 @@ func (this *Avatar) UpdateTimeout(timeout time.Duration) (err error) {
select {
case <-time.After(timeout):
err = fmt.Errorf("get gravatar image %s timeout", this.Hash)
case err = <-thunder.GoFetch(gravatar+"/"+this.Hash+"?"+this.reqParams,
case err = <-thunder.GoFetch(gravatarSource+this.Hash+"?"+this.reqParams,
this.imagePath):
}
return err

View File

@@ -4,12 +4,14 @@
package base
const DOC_URL = "http://gogs.io/docs"
type (
TplName string
ApiJsonErr struct {
Message string `json:"message"`
DocUrl string `json:"documentation_url"`
DocUrl string `json:"url"`
}
)

View File

@@ -13,7 +13,9 @@ import (
"regexp"
"strings"
"github.com/gogits/gfm"
"github.com/russross/blackfriday"
"github.com/gogits/gogs/modules/setting"
)
func isletter(c byte) bool {
@@ -73,7 +75,7 @@ func IsReadmeFile(name string) bool {
}
type CustomRender struct {
gfm.Renderer
blackfriday.Renderer
urlPrefix string
}
@@ -89,11 +91,20 @@ func (options *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte,
options.Renderer.Link(out, link, title, content)
}
func (options *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
if len(link) > 0 && !isLink(link) {
link = []byte(path.Join(strings.Replace(options.urlPrefix, "/src/", "/raw/", 1), string(link)))
}
options.Renderer.Image(out, link, title, alt)
}
var (
MentionPattern = regexp.MustCompile(`@[0-9a-zA-Z_]{1,}`)
commitPattern = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)
issueFullPattern = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
issueIndexPattern = regexp.MustCompile(`#[0-9]+`)
MentionPattern = regexp.MustCompile(`@[0-9a-zA-Z_]{1,}`)
commitPattern = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)
issueFullPattern = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
issueIndexPattern = regexp.MustCompile(`#[0-9]+`)
sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`)
)
func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
@@ -112,7 +123,7 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
ms := MentionPattern.FindAll(line, -1)
for _, m := range ms {
line = bytes.Replace(line, m,
[]byte(fmt.Sprintf(`<a href="/user/%s">%s</a>`, m[1:], m)), -1)
[]byte(fmt.Sprintf(`<a href="%s/user/%s">%s</a>`, setting.AppSubUrl, m[1:], m)), -1)
}
}
@@ -143,7 +154,22 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
` <a href="%s">#%s</a>`, m, ShortSha(string(m[i+7:j])))), -1)
}
ms = issueIndexPattern.FindAll(rawBytes, -1)
rawBytes = RenderIssueIndexPattern(rawBytes, urlPrefix)
rawBytes = RenderSha1CurrentPattern(rawBytes, urlPrefix)
return rawBytes
}
func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte {
ms := sha1CurrentPattern.FindAll(rawBytes, -1)
for _, m := range ms {
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
`<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, ShortSha(string(m)))), -1)
}
return rawBytes
}
func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string) []byte {
ms := issueIndexPattern.FindAll(rawBytes, -1)
for _, m := range ms {
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
`<a href="%s/issues/%s">%s</a>`, urlPrefix, m[1:], m)), -1)
@@ -153,39 +179,40 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
func RenderRawMarkdown(body []byte, urlPrefix string) []byte {
htmlFlags := 0
// htmlFlags |= gfm.HTML_USE_XHTML
// htmlFlags |= gfm.HTML_USE_SMARTYPANTS
// htmlFlags |= gfm.HTML_SMARTYPANTS_FRACTIONS
// htmlFlags |= gfm.HTML_SMARTYPANTS_LATEX_DASHES
// htmlFlags |= gfm.HTML_SKIP_HTML
htmlFlags |= gfm.HTML_SKIP_STYLE
htmlFlags |= gfm.HTML_SKIP_SCRIPT
htmlFlags |= gfm.HTML_GITHUB_BLOCKCODE
htmlFlags |= gfm.HTML_OMIT_CONTENTS
// htmlFlags |= gfm.HTML_COMPLETE_PAGE
// htmlFlags |= blackfriday.HTML_USE_XHTML
// htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
// htmlFlags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS
// htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
// htmlFlags |= blackfriday.HTML_SKIP_HTML
htmlFlags |= blackfriday.HTML_SKIP_STYLE
// htmlFlags |= blackfriday.HTML_SKIP_SCRIPT
// htmlFlags |= blackfriday.HTML_GITHUB_BLOCKCODE
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
// htmlFlags |= blackfriday.HTML_COMPLETE_PAGE
renderer := &CustomRender{
Renderer: gfm.HtmlRenderer(htmlFlags, "", ""),
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
urlPrefix: urlPrefix,
}
// set up the parser
extensions := 0
extensions |= gfm.EXTENSION_NO_INTRA_EMPHASIS
extensions |= gfm.EXTENSION_TABLES
extensions |= gfm.EXTENSION_FENCED_CODE
extensions |= gfm.EXTENSION_AUTOLINK
extensions |= gfm.EXTENSION_STRIKETHROUGH
extensions |= gfm.EXTENSION_HARD_LINE_BREAK
extensions |= gfm.EXTENSION_SPACE_HEADERS
extensions |= gfm.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
extensions |= blackfriday.EXTENSION_TABLES
extensions |= blackfriday.EXTENSION_FENCED_CODE
extensions |= blackfriday.EXTENSION_AUTOLINK
extensions |= blackfriday.EXTENSION_STRIKETHROUGH
extensions |= blackfriday.EXTENSION_HARD_LINE_BREAK
extensions |= blackfriday.EXTENSION_SPACE_HEADERS
extensions |= blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
body = gfm.Markdown(body, renderer, extensions)
body = blackfriday.Markdown(body, renderer, extensions)
return body
}
func RenderMarkdown(rawBytes []byte, urlPrefix string) []byte {
body := RenderSpecialLink(rawBytes, urlPrefix)
body = RenderRawMarkdown(body, urlPrefix)
body = XSS(body)
return body
}

View File

@@ -5,7 +5,6 @@
package base
import (
"bytes"
"container/list"
"encoding/json"
"errors"
@@ -48,18 +47,23 @@ func ShortSha(sha1 string) string {
return sha1
}
func ToUtf8WithErr(content []byte) (error, string) {
func DetectEncoding(content []byte) (string, error) {
detector := chardet.NewTextDetector()
result, err := detector.DetectBest(content)
return result.Charset, err
}
func ToUtf8WithErr(content []byte) (error, string) {
charset, err := DetectEncoding(content)
if err != nil {
return err, ""
}
if result.Charset == "utf8" {
if charset == "utf8" {
return nil, string(content)
}
decoder := mahonia.NewDecoder(result.Charset)
decoder := mahonia.NewDecoder(charset)
if decoder != nil {
return nil, decoder.ConvertString(string(content))
}
@@ -82,6 +86,9 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
"AppName": func() string {
return setting.AppName
},
"AppSubUrl": func() string {
return setting.AppSubUrl
},
"AppVer": func() string {
return setting.AppVer
},
@@ -104,12 +111,11 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
return a + b
},
"ActionIcon": ActionIcon,
"ActionDesc": ActionDesc,
"DateFormat": DateFormat,
"List": List,
"Mail2Domain": func(mail string) string {
if !strings.Contains(mail, "@") {
return "try.gogits.org"
return "try.gogs.io"
}
suffix := strings.SplitN(mail, "@", 2)[1]
@@ -120,7 +126,17 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
return domain
},
"SubStr": func(str string, start, length int) string {
return str[start : start+length]
if len(str) == 0 {
return ""
}
end := start + length
if length == -1 {
end = len(str)
}
if len(str) < end {
return str
}
return str[start:end]
},
"DiffTypeToStr": DiffTypeToStr,
"DiffLineTypeToStr": DiffLineTypeToStr,
@@ -130,6 +146,9 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
"Oauth2Icon": Oauth2Icon,
"Oauth2Name": Oauth2Name,
"ToUtf8": ToUtf8,
"EscapePound": func(str string) string {
return strings.Replace(str, "#", "%23", -1)
},
}
type Actioner interface {
@@ -146,14 +165,12 @@ type Actioner interface {
// and returns a icon class name.
func ActionIcon(opType int) string {
switch opType {
case 1: // Create repository.
case 1, 8: // Create, transfer repository.
return "repo"
case 5, 9: // Commit repository.
return "git-commit"
case 6: // Create issue.
return "issue-opened"
case 8: // Transfer repository.
return "share"
case 10: // Comment issue.
return "comment"
default:
@@ -161,19 +178,6 @@ func ActionIcon(opType int) string {
}
}
// TODO: Legacy
const (
TPL_CREATE_REPO = `<a href="/user/%s">%s</a> created repository <a href="/%s">%s</a>`
TPL_COMMIT_REPO = `<a href="/user/%s">%s</a> pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a>%s`
TPL_COMMIT_REPO_LI = `<div><img src="%s?s=16" alt="user-avatar"/> <a href="/%s/commit/%s" rel="nofollow">%s</a> %s</div>`
TPL_CREATE_ISSUE = `<a href="/user/%s">%s</a> opened issue <a href="/%s/issues/%s">%s#%s</a>
<div><img src="%s?s=16" alt="user-avatar"/> %s</div>`
TPL_TRANSFER_REPO = `<a href="/user/%s">%s</a> transfered repository <code>%s</code> to <a href="/%s">%s</a>`
TPL_PUSH_TAG = `<a href="/user/%s">%s</a> pushed tag <a href="/%s/src/%s" rel="nofollow">%s</a> at <a href="/%s">%s</a>`
TPL_COMMENT_ISSUE = `<a href="/user/%s">%s</a> commented on issue <a href="/%s/issues/%s">%s#%s</a>
<div><img src="%s?s=16" alt="user-avatar"/> %s</div>`
)
type PushCommit struct {
Sha1 string
Message string
@@ -182,8 +186,9 @@ type PushCommit struct {
}
type PushCommits struct {
Len int
Commits []*PushCommit
Len int
Commits []*PushCommit
CompareUrl string
}
func ActionContent2Commits(act Actioner) *PushCommits {
@@ -194,52 +199,6 @@ func ActionContent2Commits(act Actioner) *PushCommits {
return push
}
// TODO: Legacy
// ActionDesc accepts int that represents action operation type
// and returns the description.
func ActionDesc(act Actioner) string {
actUserName := act.GetActUserName()
email := act.GetActEmail()
repoUserName := act.GetRepoUserName()
repoName := act.GetRepoName()
repoLink := repoUserName + "/" + repoName
branch := act.GetBranch()
content := act.GetContent()
switch act.GetOpType() {
case 1: // Create repository.
return fmt.Sprintf(TPL_CREATE_REPO, actUserName, actUserName, repoLink, repoName)
case 5: // Commit repository.
var push *PushCommits
if err := json.Unmarshal([]byte(content), &push); err != nil {
return err.Error()
}
buf := bytes.NewBuffer([]byte("\n"))
for _, commit := range push.Commits {
buf.WriteString(fmt.Sprintf(TPL_COMMIT_REPO_LI, AvatarLink(commit.AuthorEmail), repoLink, commit.Sha1, commit.Sha1[:7], commit.Message) + "\n")
}
if push.Len > 3 {
buf.WriteString(fmt.Sprintf(`<div><a href="/%s/%s/commits/%s" rel="nofollow">%d other commits >></a></div>`, actUserName, repoName, branch, push.Len))
}
return fmt.Sprintf(TPL_COMMIT_REPO, actUserName, actUserName, repoLink, branch, branch, repoLink, repoLink,
buf.String())
case 6: // Create issue.
infos := strings.SplitN(content, "|", 2)
return fmt.Sprintf(TPL_CREATE_ISSUE, actUserName, actUserName, repoLink, infos[0], repoLink, infos[0],
AvatarLink(email), infos[1])
case 8: // Transfer repository.
newRepoLink := content + "/" + repoName
return fmt.Sprintf(TPL_TRANSFER_REPO, actUserName, actUserName, repoLink, newRepoLink, newRepoLink)
case 9: // Push tag.
return fmt.Sprintf(TPL_PUSH_TAG, actUserName, actUserName, repoLink, branch, branch, repoLink, repoLink)
case 10: // Comment issue.
infos := strings.SplitN(content, "|", 2)
return fmt.Sprintf(TPL_COMMENT_ISSUE, actUserName, actUserName, repoLink, infos[0], repoLink, infos[0],
AvatarLink(email), infos[1])
default:
return "invalid type"
}
}
func DiffTypeToStr(diffType int) string {
diffTypes := map[int]string{
1: "add", 2: "modify", 3: "del",

View File

@@ -9,27 +9,51 @@ import (
"crypto/md5"
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"encoding/hex"
"fmt"
"hash"
"html/template"
"math"
"regexp"
"strings"
"time"
"github.com/Unknwon/com"
"github.com/Unknwon/i18n"
"github.com/gogits/gogs/modules/avatar"
"github.com/gogits/gogs/modules/setting"
)
// Encode string to md5 hex value
// Encode string to md5 hex value.
func EncodeMd5(str string) string {
m := md5.New()
m.Write([]byte(str))
return hex.EncodeToString(m.Sum(nil))
}
// Encode string to sha1 hex value.
func EncodeSha1(str string) string {
h := sha1.New()
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
func BasicAuthDecode(encoded string) (string, string, error) {
s, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
return "", "", err
}
auth := strings.SplitN(string(s), ":", 2)
return auth[0], auth[1], nil
}
func BasicAuthEncode(username, password string) string {
return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
}
// GetRandomString generate random string by specify chars.
func GetRandomString(n int, alphabets ...byte) string {
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
@@ -146,11 +170,14 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string
// AvatarLink returns avatar link by given e-mail.
func AvatarLink(email string) string {
if setting.DisableGravatar {
return "/img/avatar_default.jpg"
} else if setting.Service.EnableCacheAvatar {
return "/avatar/" + EncodeMd5(email)
return setting.AppSubUrl + "/img/avatar_default.jpg"
}
return "//1.gravatar.com/avatar/" + EncodeMd5(email)
gravatarHash := avatar.HashEmail(email)
if setting.Service.EnableCacheAvatar {
return setting.AppSubUrl + "/avatar/" + gravatarHash
}
return setting.GravatarSource + gravatarHash
}
// Seconds-based time units
@@ -446,3 +473,29 @@ func DateFormat(t time.Time, format string) string {
format = replacer.Replace(format)
return t.Format(format)
}
type xssFilter struct {
reg *regexp.Regexp
repl []byte
}
var (
whiteSpace = []byte(" ")
xssFilters = []xssFilter{
{regexp.MustCompile(`\ [ONon]\w*=["]*`), whiteSpace},
{regexp.MustCompile(`<[SCRIPTscript]{6}`), whiteSpace},
{regexp.MustCompile(`=[` + "`" + `'"]*[JAVASCRIPTjavascript \t\0&#x0D;]*:`), whiteSpace},
}
)
// XSS goes through all the XSS filters to make user input content as safe as possible.
func XSS(in []byte) []byte {
for _, filter := range xssFilters {
in = filter.reg.ReplaceAll(in, filter.repl)
}
return in
}
func XSSString(in string) string {
return string(XSS([]byte(in)))
}

View File

@@ -7,9 +7,9 @@ Callers may register Funcs to be invoked on a given schedule. Cron will run
them in their own goroutines.
c := cron.New()
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("@hourly", func() { fmt.Println("Every hour") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.AddFunc("Every hour on the half hour","0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("Every hour","@hourly", func() { fmt.Println("Every hour") })
c.AddFunc("Every hour and a half","@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.Start()
..
// Funcs are invoked in their own goroutine, asynchronously.

View File

@@ -16,6 +16,7 @@ var c = New()
func NewCronContext() {
c.AddFunc("Update mirrors", "@every 1h", models.MirrorUpdate)
c.AddFunc("Deliver hooks", fmt.Sprintf("@every %dm", setting.WebhookTaskInterval), models.DeliverHooks)
c.AddFunc("Repository health check", "@every 1h", models.GitFsck)
c.Start()
}

View File

@@ -5,6 +5,7 @@
package git
import (
"bufio"
"container/list"
"strings"
)
@@ -17,7 +18,8 @@ type Commit struct {
Committer *Signature
CommitMessage string
parents []sha1 // sha1 strings
parents []sha1 // sha1 strings
submodules map[string]*SubModule
}
// Return the commit message. Same as retrieving CommitMessage directly.
@@ -84,3 +86,49 @@ func (c *Commit) CommitsByRange(page int) (*list.List, error) {
func (c *Commit) GetCommitOfRelPath(relPath string) (*Commit, error) {
return c.repo.getCommitOfRelPath(c.Id, relPath)
}
func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
moduels, err := c.GetSubModules()
if err != nil {
return nil, err
}
return moduels[entryname], nil
}
func (c *Commit) GetSubModules() (map[string]*SubModule, error) {
if c.submodules != nil {
return c.submodules, nil
}
entry, err := c.GetTreeEntryByPath(".gitmodules")
if err != nil {
return nil, err
}
rd, err := entry.Blob().Data()
if err != nil {
return nil, err
}
scanner := bufio.NewScanner(rd)
c.submodules = make(map[string]*SubModule)
var ismodule bool
var path string
for scanner.Scan() {
if strings.HasPrefix(scanner.Text(), "[submodule") {
ismodule = true
continue
}
if ismodule {
fields := strings.Split(scanner.Text(), "=")
k := strings.TrimSpace(fields[0])
if k == "path" {
path = strings.TrimSpace(fields[1])
} else if k == "url" {
c.submodules[path] = &SubModule{path, strings.TrimSpace(fields[1])}
ismodule = false
}
}
}
return c.submodules, nil
}

111
modules/git/hooks.go Normal file
View File

@@ -0,0 +1,111 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package git
import (
"errors"
"io/ioutil"
"os"
"path"
"strings"
)
// hookNames is a list of Git hooks' name that are supported.
var hookNames = []string{
"pre-applypatch",
"applypatch-msg",
"prepare-commit-msg",
"commit-msg",
"pre-commit",
"pre-rebase",
"post-commit",
"post-receive",
"post-update",
}
var (
ErrNotValidHook = errors.New("not a valid Git hook")
)
// IsValidHookName returns true if given name is a valid Git hook.
func IsValidHookName(name string) bool {
for _, hn := range hookNames {
if hn == name {
return true
}
}
return false
}
// Hook represents a Git hook.
type Hook struct {
name string
IsActive bool // Indicates whether repository has this hook.
Content string // Content of hook if it's active.
Sample string // Sample content from Git.
path string // Hook file path.
}
// GetHook returns a Git hook by given name and repository.
func GetHook(repoPath, name string) (*Hook, error) {
if !IsValidHookName(name) {
return nil, ErrNotValidHook
}
h := &Hook{
name: name,
path: path.Join(repoPath, "hooks", name),
}
if isFile(h.path) {
data, err := ioutil.ReadFile(h.path)
if err != nil {
return nil, err
}
h.IsActive = true
h.Content = string(data)
} else if isFile(h.path + ".sample") {
data, err := ioutil.ReadFile(h.path + ".sample")
if err != nil {
return nil, err
}
h.Sample = string(data)
}
return h, nil
}
func (h *Hook) Name() string {
return h.name
}
// Update updates hook settings.
func (h *Hook) Update() error {
if len(strings.TrimSpace(h.Content)) == 0 {
return os.Remove(h.path)
}
return ioutil.WriteFile(h.path, []byte(strings.Replace(h.Content, "\r", "", -1)), os.ModePerm)
}
// ListHooks returns a list of Git hooks of given repository.
func ListHooks(repoPath string) (_ []*Hook, err error) {
if !isDir(path.Join(repoPath, "hooks")) {
return nil, errors.New("hooks path does not exist")
}
hooks := make([]*Hook, len(hookNames))
for i, name := range hookNames {
hooks[i], err = GetHook(repoPath, name)
if err != nil {
return nil, err
}
}
return hooks, nil
}
func (repo *Repository) GetHook(name string) (*Hook, error) {
return GetHook(repo.Path, name)
}
func (repo *Repository) Hooks() ([]*Hook, error) {
return ListHooks(repo.Path)
}

View File

@@ -40,11 +40,11 @@ func (repo *Repository) GetCommitIdOfTag(tagName string) (string, error) {
}
func (repo *Repository) GetCommitOfTag(tagName string) (*Commit, error) {
commitId, err := repo.GetCommitIdOfTag(tagName)
tag, err := repo.GetTag(tagName)
if err != nil {
return nil, err
}
return repo.GetCommit(commitId)
return tag.Commit()
}
// Parse commit information from the (uncompressed) raw

View File

@@ -22,6 +22,9 @@ func (repo *Repository) IsTagExist(tagName string) bool {
// GetTags returns all tags of given repository.
func (repo *Repository) GetTags() ([]string, error) {
if gitVer.AtLeast(MustParseVersion("2.0.0")) {
return repo.getTagsReversed()
}
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l")
if err != nil {
return nil, errors.New(stderr)
@@ -30,6 +33,15 @@ func (repo *Repository) GetTags() ([]string, error) {
return tags[:len(tags)-1], nil
}
func (repo *Repository) getTagsReversed() ([]string, error) {
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l", "--sort=-v:refname")
if err != nil {
return nil, errors.New(stderr)
}
tags := strings.Split(stdout, "\n")
return tags[:len(tags)-1], nil
}
func (repo *Repository) CreateTag(tagName, idStr string) error {
_, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", tagName, idStr)
if err != nil {
@@ -52,6 +64,7 @@ func (repo *Repository) getTag(id sha1) (*Tag, error) {
if err != nil {
return nil, errors.New(stderr)
}
tp = strings.TrimSpace(tp)
// Tag is a commit.
if ObjectType(tp) == COMMIT {

58
modules/git/submodule.go Normal file
View File

@@ -0,0 +1,58 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package git
import (
"strings"
)
type SubModule struct {
Name string
Url string
}
// SubModuleFile represents a file with submodule type.
type SubModuleFile struct {
*Commit
refUrl string
refId string
}
func NewSubModuleFile(c *Commit, refUrl, refId string) *SubModuleFile {
return &SubModuleFile{
Commit: c,
refUrl: refUrl,
refId: refId,
}
}
// RefUrl guesses and returns reference URL.
func (sf *SubModuleFile) RefUrl() string {
url := strings.TrimSuffix(sf.refUrl, ".git")
// git://xxx/user/repo
if strings.HasPrefix(url, "git://") {
return "http://" + strings.TrimPrefix(url, "git://")
}
// http[s]://xxx/user/repo
if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
return url
}
// sysuser@xxx:user/repo
i := strings.Index(url, "@")
j := strings.LastIndex(url, ":")
if i > -1 && j > -1 {
return "http://" + url[i+1:j] + "/" + url[j+1:]
}
return url
}
// RefId returns reference ID.
func (sf *SubModuleFile) RefId() string {
return sf.refId
}

View File

@@ -51,6 +51,8 @@ func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) {
case "160000":
entry.mode = ModeCommit
entry.Type = COMMIT
step = 8
case "040000":
entry.mode = ModeTree
entry.Type = TREE
@@ -107,9 +109,12 @@ func (t *Tree) ListEntries(relpath string) (Entries, error) {
}
t.entriesParsed = true
stdout, _, err := com.ExecCmdDirBytes(t.repo.Path,
stdout, stderr, err := com.ExecCmdDirBytes(t.repo.Path,
"git", "ls-tree", t.Id.String())
if err != nil {
if strings.Contains(err.Error(), "exit status 128") {
return nil, errors.New(strings.TrimSpace(string(stderr)))
}
return nil, err
}
t.entries, err = parseTreeData(t, stdout)

View File

@@ -61,6 +61,10 @@ func (te *TreeEntry) Size() int64 {
return te.size
}
func (te *TreeEntry) IsSubModule() bool {
return te.mode == ModeCommit
}
func (te *TreeEntry) IsDir() bool {
return te.mode == ModeTree
}
@@ -80,7 +84,7 @@ type Entries []*TreeEntry
var sorter = []func(t1, t2 *TreeEntry) bool{
func(t1, t2 *TreeEntry) bool {
return t1.IsDir() && !t2.IsDir()
return (t1.IsDir() || t1.IsSubModule()) && !t2.IsDir() && !t2.IsSubModule()
},
func(t1, t2 *TreeEntry) bool {
return t1.name < t2.name

View File

@@ -7,6 +7,7 @@ package git
import (
"bytes"
"container/list"
"os"
"path/filepath"
"strings"
)
@@ -46,3 +47,23 @@ func RefEndName(refStr string) string {
func filepathFromSHA1(rootdir, sha1 string) string {
return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:])
}
// isDir returns true if given path is a directory,
// or returns false when it's a file or does not exist.
func isDir(dir string) bool {
f, e := os.Stat(dir)
if e != nil {
return false
}
return f.IsDir()
}
// isFile returns true if given path is a file,
// or returns false when it's a directory or does not exist.
func isFile(filePath string) bool {
f, e := os.Stat(filePath)
if e != nil {
return false
}
return !f.IsDir()
}

View File

@@ -74,6 +74,10 @@ func (v *Version) LessThan(that *Version) bool {
return v.Compare(that) < 0
}
func (v *Version) AtLeast(that *Version) bool {
return v.Compare(that) >= 0
}
// GetVersion returns current Git version installed.
func GetVersion() (*Version, error) {
if gitVer != nil {

View File

@@ -5,7 +5,7 @@
package httplib
// NOTE: last sync c07b1d8 on Aug 23, 2014.
// NOTE: last sync 57e62e5 on Oct 29, 2014.
import (
"bytes"
@@ -14,6 +14,7 @@ import (
"encoding/xml"
"io"
"io/ioutil"
"log"
"mime/multipart"
"net"
"net/http"
@@ -252,35 +253,36 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
} else {
b.url = b.url + "?" + paramBody
}
} else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 {
} else if b.req.Method == "POST" && b.req.Body == nil {
if len(b.files) > 0 {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
for formname, filename := range b.files {
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
if err != nil {
return nil, err
pr, pw := io.Pipe()
bodyWriter := multipart.NewWriter(pw)
go func() {
for formname, filename := range b.files {
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
if err != nil {
log.Fatal(err)
}
fh, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
//iocopy
_, err = io.Copy(fileWriter, fh)
fh.Close()
if err != nil {
log.Fatal(err)
}
}
fh, err := os.Open(filename)
if err != nil {
return nil, err
for k, v := range b.params {
bodyWriter.WriteField(k, v)
}
//iocopy
_, err = io.Copy(fileWriter, fh)
fh.Close()
if err != nil {
return nil, err
}
}
for k, v := range b.params {
bodyWriter.WriteField(k, v)
}
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
b.Header("Content-Type", contentType)
b.req.Body = ioutil.NopCloser(bodyBuf)
b.req.ContentLength = int64(bodyBuf.Len())
} else {
bodyWriter.Close()
pw.Close()
}()
b.Header("Content-Type", bodyWriter.FormDataContentType())
b.req.Body = ioutil.NopCloser(pr)
} else if len(paramBody) > 0 {
b.Header("Content-Type", "application/x-www-form-urlencoded")
b.Body(paramBody)
}
@@ -332,7 +334,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
Jar: jar,
}
if b.setting.UserAgent != "" {
if len(b.setting.UserAgent) > 0 && len(b.req.Header.Get("User-Agent")) == 0 {
b.req.Header.Set("User-Agent", b.setting.UserAgent)
}

View File

@@ -57,23 +57,23 @@ func TestSimplePost(t *testing.T) {
}
}
func TestPostFile(t *testing.T) {
v := "smallfish"
req := Post("http://httpbin.org/post")
req.Param("username", v)
req.PostFile("uploadfile", "httplib_test.go")
// func TestPostFile(t *testing.T) {
// v := "smallfish"
// req := Post("http://httpbin.org/post")
// req.Param("username", v)
// req.PostFile("uploadfile", "httplib_test.go")
str, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
// str, err := req.String()
// if err != nil {
// t.Fatal(err)
// }
// t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in post")
}
}
// n := strings.Index(str, v)
// if n == -1 {
// t.Fatal(v + " not found in post")
// }
// }
func TestSimplePut(t *testing.T) {
str, err := Put("http://httpbin.org/put").String()
@@ -194,3 +194,13 @@ func TestToFile(t *testing.T) {
t.Fatal(err)
}
}
func TestHeader(t *testing.T) {
req := Get("http://httpbin.org/headers")
req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36")
str, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/gogits/gogs/modules/asn1-ber"
)
// debbuging type
// debugging type
// - has a Printf method to write the debug output
type debugging bool

View File

@@ -87,6 +87,12 @@ func Fatal(skip int, format string, v ...interface{}) {
os.Exit(1)
}
func Close() {
for _, l := range loggers {
l.Close()
}
}
// .___ __ _____
// | | _____/ |_ ____________/ ____\____ ____ ____
// | |/ \ __\/ __ \_ __ \ __\\__ \ _/ ___\/ __ \
@@ -156,7 +162,7 @@ func newLogger(buffer int64) *Logger {
return l
}
// SetLogger sets new logger instanse with given logger adapter and config.
// SetLogger sets new logger instance with given logger adapter and config.
func (l *Logger) SetLogger(adapter string, config string) error {
l.lock.Lock()
defer l.lock.Unlock()

View File

@@ -32,7 +32,7 @@ const (
)
// A Decoder is a function that decodes a character set, one character at a time.
// It works much like utf8.DecodeRune, but has an aditional status return value.
// It works much like utf8.DecodeRune, but has an additional status return value.
type Decoder func(p []byte) (c rune, size int, status Status)
// An Encoder is a function that encodes a character set, one character at a time.

View File

@@ -5,7 +5,9 @@
package mailer
import (
"crypto/tls"
"fmt"
"net"
"net/smtp"
"strings"
@@ -33,7 +35,7 @@ func (m Message) Content() string {
}
// create mail content
content := "From: " + m.From + "<" + m.User +
content := "From: \"" + m.From + "\" <" + m.User +
">\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body
return content
}
@@ -64,6 +66,53 @@ func processMailQueue() {
}
}
// sendMail allows mail with self-signed certificates.
func sendMail(hostAddressWithPort string, auth smtp.Auth, from string, recipients []string, msgContent []byte) error {
client, err := smtp.Dial(hostAddressWithPort)
if err != nil {
return err
}
host, _, _ := net.SplitHostPort(hostAddressWithPort)
tlsConn := &tls.Config{
InsecureSkipVerify: true,
ServerName: host,
}
if err = client.StartTLS(tlsConn); err != nil {
return err
}
if ok, _ := client.Extension("AUTH"); ok && auth != nil {
if err = client.Auth(auth); err != nil {
return err
}
}
if err = client.Mail(from); err != nil {
return err
}
for _, rec := range recipients {
if err = client.Rcpt(rec); err != nil {
return err
}
}
w, err := client.Data()
if err != nil {
return err
}
if _, err = w.Write([]byte(msgContent)); err != nil {
return err
}
if err = w.Close(); err != nil {
return err
}
return client.Quit()
}
// Direct Send mail message
func Send(msg *Message) (int, error) {
log.Trace("Sending mails to: %s", strings.Join(msg.To, "; "))
@@ -78,14 +127,17 @@ func Send(msg *Message) (int, error) {
return 0, fmt.Errorf("empty email body")
}
auth := smtp.PlainAuth("", setting.MailService.User, setting.MailService.Passwd, host[0])
var auth smtp.Auth
if len(setting.MailService.Passwd) > 0 {
auth = smtp.PlainAuth("", setting.MailService.User, setting.MailService.Passwd, host[0])
}
if msg.Massive {
// send mail to multiple emails one by one
num := 0
for _, to := range msg.To {
body := []byte("To: " + to + "\r\n" + content)
err := smtp.SendMail(setting.MailService.Host, auth, msg.From, []string{to}, body)
err := sendMail(setting.MailService.Host, auth, msg.From, []string{to}, body)
if err != nil {
return num, err
}
@@ -96,7 +148,7 @@ func Send(msg *Message) (int, error) {
body := []byte("To: " + strings.Join(msg.To, ";") + "\r\n" + content)
// send to multiple emails in one message
err := smtp.SendMail(setting.MailService.Host, auth, msg.From, msg.To, body)
err := sendMail(setting.MailService.Host, auth, msg.From, msg.To, body)
if err != nil {
return 0, err
} else {

View File

@@ -25,13 +25,19 @@ func Toggle(options *ToggleOptions) macaron.Handler {
return func(ctx *Context) {
// Cannot view any page before installation.
if !setting.InstallLock {
ctx.Redirect("/install")
ctx.Redirect(setting.AppSubUrl + "/install")
return
}
// Checking non-logged users landing page.
if !ctx.IsSigned && ctx.Req.RequestURI == "/" && setting.LandingPageUrl != setting.LANDING_PAGE_HOME {
ctx.Redirect(string(setting.LandingPageUrl))
return
}
// Redirect to dashboard if user tries to visit any non-login page.
if options.SignOutRequire && ctx.IsSigned && ctx.Req.RequestURI != "/" {
ctx.Redirect("/")
ctx.Redirect(setting.AppSubUrl + "/")
return
}
@@ -48,8 +54,8 @@ func Toggle(options *ToggleOptions) macaron.Handler {
if strings.HasSuffix(ctx.Req.RequestURI, "watch") {
return
}
ctx.SetCookie("redirect_to", "/"+url.QueryEscape(ctx.Req.RequestURI))
ctx.Redirect("/user/login")
ctx.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
ctx.Redirect(setting.AppSubUrl + "/user/login")
return
} else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm {
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
@@ -67,3 +73,21 @@ func Toggle(options *ToggleOptions) macaron.Handler {
}
}
}
func ApiReqToken() macaron.Handler {
return func(ctx *Context) {
if !ctx.IsSigned {
ctx.Error(403)
return
}
}
}
func ApiReqBasicAuth() macaron.Handler {
return func(ctx *Context) {
if !ctx.IsBasicAuth {
ctx.Error(403)
return
}
}
}

View File

@@ -1,472 +0,0 @@
// Copyright 2013 The Martini Contrib Authors. All rights reserved.
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package binding
import (
"encoding/json"
"fmt"
"io"
"net/http"
"reflect"
"regexp"
"strconv"
"strings"
"unicode/utf8"
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/i18n"
)
/*
To the land of Middle-ware Earth:
One func to rule them all,
One func to find them,
One func to bring them all,
And in this package BIND them.
*/
// Bind accepts a copy of an empty struct and populates it with
// values from the request (if deserialization is successful). It
// wraps up the functionality of the Form and Json middleware
// according to the Content-Type of the request, and it guesses
// if no Content-Type is specified. Bind invokes the ErrorHandler
// middleware to bail out if errors occurred. If you want to perform
// your own error handling, use Form or Json middleware directly.
// An interface pointer can be added as a second argument in order
// to map the struct to a specific interface.
func Bind(obj interface{}, ifacePtr ...interface{}) macaron.Handler {
return func(ctx *macaron.Context) {
contentType := ctx.Req.Header.Get("Content-Type")
if strings.Contains(contentType, "form-urlencoded") {
ctx.Invoke(Form(obj, ifacePtr...))
} else if strings.Contains(contentType, "multipart/form-data") {
ctx.Invoke(MultipartForm(obj, ifacePtr...))
} else if strings.Contains(contentType, "json") {
ctx.Invoke(Json(obj, ifacePtr...))
} else {
ctx.Invoke(Json(obj, ifacePtr...))
if getErrors(ctx).Count() > 0 {
ctx.Invoke(Form(obj, ifacePtr...))
}
}
ctx.Invoke(ErrorHandler)
}
}
// BindIgnErr will do the exactly same thing as Bind but without any
// error handling, which user has freedom to deal with them.
// This allows user take advantages of validation.
func BindIgnErr(obj interface{}, ifacePtr ...interface{}) macaron.Handler {
return func(ctx *macaron.Context, req *http.Request) {
contentType := req.Header.Get("Content-Type")
if strings.Contains(contentType, "form-urlencoded") {
ctx.Invoke(Form(obj, ifacePtr...))
} else if strings.Contains(contentType, "multipart/form-data") {
ctx.Invoke(MultipartForm(obj, ifacePtr...))
} else if strings.Contains(contentType, "json") {
ctx.Invoke(Json(obj, ifacePtr...))
} else {
ctx.Invoke(Json(obj, ifacePtr...))
if getErrors(ctx).Count() > 0 {
ctx.Invoke(Form(obj, ifacePtr...))
}
}
}
}
// Form is middleware to deserialize form-urlencoded data from the request.
// It gets data from the form-urlencoded body, if present, or from the
// query string. It uses the http.Request.ParseForm() method
// to perform deserialization, then reflection is used to map each field
// into the struct with the proper type. Structs with primitive slice types
// (bool, float, int, string) can support deserialization of repeated form
// keys, for example: key=val1&key=val2&key=val3
// An interface pointer can be added as a second argument in order
// to map the struct to a specific interface.
func Form(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
return func(ctx *macaron.Context) {
ensureNotPointer(formStruct)
formStruct := reflect.New(reflect.TypeOf(formStruct))
errors := newErrors()
parseErr := ctx.Req.ParseForm()
// Format validation of the request body or the URL would add considerable overhead,
// and ParseForm does not complain when URL encoding is off.
// Because an empty request body or url can also mean absence of all needed values,
// it is not in all cases a bad request, so let's return 422.
if parseErr != nil {
errors.Overall[BindingDeserializationError] = parseErr.Error()
}
mapForm(formStruct, ctx.Req.Form, errors)
validateAndMap(formStruct, ctx, errors, ifacePtr...)
}
}
func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
return func(ctx *macaron.Context) {
ensureNotPointer(formStruct)
formStruct := reflect.New(reflect.TypeOf(formStruct))
errors := newErrors()
// Workaround for multipart forms returning nil instead of an error
// when content is not multipart
// https://code.google.com/p/go/issues/detail?id=6334
multipartReader, err := ctx.Req.MultipartReader()
if err != nil {
errors.Overall[BindingDeserializationError] = err.Error()
} else {
form, parseErr := multipartReader.ReadForm(MaxMemory)
if parseErr != nil {
errors.Overall[BindingDeserializationError] = parseErr.Error()
}
ctx.Req.MultipartForm = form
}
mapForm(formStruct, ctx.Req.MultipartForm.Value, errors)
validateAndMap(formStruct, ctx, errors, ifacePtr...)
}
}
// Json is middleware to deserialize a JSON payload from the request
// into the struct that is passed in. The resulting struct is then
// validated, but no error handling is actually performed here.
// An interface pointer can be added as a second argument in order
// to map the struct to a specific interface.
func Json(jsonStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
return func(ctx *macaron.Context) {
ensureNotPointer(jsonStruct)
jsonStruct := reflect.New(reflect.TypeOf(jsonStruct))
errors := newErrors()
if ctx.Req.Body != nil {
defer ctx.Req.Body.Close()
}
if err := json.NewDecoder(ctx.Req.Body).Decode(jsonStruct.Interface()); err != nil && err != io.EOF {
errors.Overall[BindingDeserializationError] = err.Error()
}
validateAndMap(jsonStruct, ctx, errors, ifacePtr...)
}
}
// Validate is middleware to enforce required fields. If the struct
// passed in is a Validator, then the user-defined Validate method
// is executed, and its errors are mapped to the context. This middleware
// performs no error handling: it merely detects them and maps them.
func Validate(obj interface{}) macaron.Handler {
return func(ctx *macaron.Context, l i18n.Locale) {
errors := newErrors()
validateStruct(errors, obj)
if validator, ok := obj.(Validator); ok {
validator.Validate(ctx, errors, l)
}
ctx.Map(*errors)
}
}
var (
alphaDashPattern = regexp.MustCompile("[^\\d\\w-_]")
alphaDashDotPattern = regexp.MustCompile("[^\\d\\w-_\\.]")
emailPattern = regexp.MustCompile("[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[a-zA-Z0-9](?:[\\w-]*[\\w])?")
urlPattern = regexp.MustCompile(`(http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?`)
)
func validateStruct(errors *Errors, obj interface{}) {
typ := reflect.TypeOf(obj)
val := reflect.ValueOf(obj)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
// Allow ignored fields in the struct
if field.Tag.Get("form") == "-" {
continue
}
fieldValue := val.Field(i).Interface()
if field.Type.Kind() == reflect.Struct {
validateStruct(errors, fieldValue)
continue
}
zero := reflect.Zero(field.Type).Interface()
// Match rules.
for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
if len(rule) == 0 {
continue
}
switch {
case rule == "Required":
if reflect.DeepEqual(zero, fieldValue) {
errors.Fields[field.Name] = BindingRequireError
break
}
case rule == "AlphaDash":
if alphaDashPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Fields[field.Name] = BindingAlphaDashError
break
}
case rule == "AlphaDashDot":
if alphaDashDotPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Fields[field.Name] = BindingAlphaDashDotError
break
}
case strings.HasPrefix(rule, "MinSize("):
min, err := strconv.Atoi(rule[8 : len(rule)-1])
if err != nil {
errors.Overall["MinSize"] = err.Error()
break
}
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) < min {
errors.Fields[field.Name] = BindingMinSizeError
break
}
v := reflect.ValueOf(fieldValue)
if v.Kind() == reflect.Slice && v.Len() < min {
errors.Fields[field.Name] = BindingMinSizeError
break
}
case strings.HasPrefix(rule, "MaxSize("):
max, err := strconv.Atoi(rule[8 : len(rule)-1])
if err != nil {
errors.Overall["MaxSize"] = err.Error()
break
}
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) > max {
errors.Fields[field.Name] = BindingMaxSizeError
break
}
v := reflect.ValueOf(fieldValue)
if v.Kind() == reflect.Slice && v.Len() > max {
errors.Fields[field.Name] = BindingMinSizeError
break
}
case rule == "Email":
if !emailPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Fields[field.Name] = BindingEmailError
break
}
case rule == "Url":
str := fmt.Sprintf("%v", fieldValue)
if len(str) == 0 {
continue
} else if !urlPattern.MatchString(str) {
errors.Fields[field.Name] = BindingUrlError
break
}
}
}
}
}
func mapForm(formStruct reflect.Value, form map[string][]string, errors *Errors) {
typ := formStruct.Elem().Type()
for i := 0; i < typ.NumField(); i++ {
typeField := typ.Field(i)
if inputFieldName := typeField.Tag.Get("form"); inputFieldName != "" {
structField := formStruct.Elem().Field(i)
if !structField.CanSet() {
continue
}
inputValue, exists := form[inputFieldName]
if !exists {
continue
}
numElems := len(inputValue)
if structField.Kind() == reflect.Slice && numElems > 0 {
sliceOf := structField.Type().Elem().Kind()
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
for i := 0; i < numElems; i++ {
setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors)
}
formStruct.Elem().Field(i).Set(slice)
} else {
setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors)
}
}
}
}
// ErrorHandler simply counts the number of errors in the
// context and, if more than 0, writes a 400 Bad Request
// response and a JSON payload describing the errors with
// the "Content-Type" set to "application/json".
// Middleware remaining on the stack will not even see the request
// if, by this point, there are any errors.
// This is a "default" handler, of sorts, and you are
// welcome to use your own instead. The Bind middleware
// invokes this automatically for convenience.
func ErrorHandler(errs Errors, resp http.ResponseWriter) {
if errs.Count() > 0 {
resp.Header().Set("Content-Type", "application/json; charset=utf-8")
if _, ok := errs.Overall[BindingDeserializationError]; ok {
resp.WriteHeader(http.StatusBadRequest)
} else {
resp.WriteHeader(422)
}
errOutput, _ := json.Marshal(errs)
resp.Write(errOutput)
return
}
}
// This sets the value in a struct of an indeterminate type to the
// matching value from the request (via Form middleware) in the
// same type, so that not all deserialized values have to be strings.
// Supported types are string, int, float, and bool.
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value, nameInTag string, errors *Errors) {
switch valueKind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if val == "" {
val = "0"
}
intVal, err := strconv.ParseInt(val, 10, 64)
if err != nil {
errors.Fields[nameInTag] = BindingIntegerTypeError
} else {
structField.SetInt(intVal)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if val == "" {
val = "0"
}
uintVal, err := strconv.ParseUint(val, 10, 64)
if err != nil {
errors.Fields[nameInTag] = BindingIntegerTypeError
} else {
structField.SetUint(uintVal)
}
case reflect.Bool:
structField.SetBool(val == "on")
case reflect.Float32:
if val == "" {
val = "0.0"
}
floatVal, err := strconv.ParseFloat(val, 32)
if err != nil {
errors.Fields[nameInTag] = BindingFloatTypeError
} else {
structField.SetFloat(floatVal)
}
case reflect.Float64:
if val == "" {
val = "0.0"
}
floatVal, err := strconv.ParseFloat(val, 64)
if err != nil {
errors.Fields[nameInTag] = BindingFloatTypeError
} else {
structField.SetFloat(floatVal)
}
case reflect.String:
structField.SetString(val)
}
}
// Don't pass in pointers to bind to. Can lead to bugs.
func ensureNotPointer(obj interface{}) {
if reflect.TypeOf(obj).Kind() == reflect.Ptr {
panic("Pointers are not accepted as binding models")
}
}
// Performs validation and combines errors from validation
// with errors from deserialization, then maps both the
// resulting struct and the errors to the context.
func validateAndMap(obj reflect.Value, ctx *macaron.Context, errors *Errors, ifacePtr ...interface{}) {
ctx.Invoke(Validate(obj.Interface()))
errors.Combine(getErrors(ctx))
ctx.Map(*errors)
ctx.Map(obj.Elem().Interface())
if len(ifacePtr) > 0 {
ctx.MapTo(obj.Elem().Interface(), ifacePtr[0])
}
}
func newErrors() *Errors {
return &Errors{make(map[string]string), make(map[string]string)}
}
func getErrors(ctx *macaron.Context) Errors {
return ctx.GetVal(reflect.TypeOf(Errors{})).Interface().(Errors)
}
type (
// Implement the Validator interface to define your own input
// validation before the request even gets to your application.
// The Validate method will be executed during the validation phase.
Validator interface {
Validate(*macaron.Context, *Errors, i18n.Locale)
}
)
var (
// Maximum amount of memory to use when parsing a multipart form.
// Set this to whatever value you prefer; default is 10 MB.
MaxMemory = int64(1024 * 1024 * 10)
)
// Errors represents the contract of the response body when the
// binding step fails before getting to the application.
type Errors struct {
Overall map[string]string `json:"overall"`
Fields map[string]string `json:"fields"`
}
// Total errors is the sum of errors with the request overall
// and errors on individual fields.
func (err Errors) Count() int {
return len(err.Overall) + len(err.Fields)
}
func (this *Errors) Combine(other Errors) {
for key, val := range other.Fields {
if _, exists := this.Fields[key]; !exists {
this.Fields[key] = val
}
}
for key, val := range other.Overall {
if _, exists := this.Overall[key]; !exists {
this.Overall[key] = val
}
}
}
const (
BindingRequireError string = "Required"
BindingAlphaDashError string = "AlphaDash"
BindingAlphaDashDotError string = "AlphaDashDot"
BindingMinSizeError string = "MinSize"
BindingMaxSizeError string = "MaxSize"
BindingEmailError string = "Email"
BindingUrlError string = "Url"
BindingDeserializationError string = "DeserializationError"
BindingIntegerTypeError string = "IntegerTypeError"
BindingBooleanTypeError string = "BooleanTypeError"
BindingFloatTypeError string = "FloatTypeError"
)

View File

@@ -9,7 +9,6 @@ import (
"html/template"
"io"
"net/http"
"path"
"strings"
"time"
@@ -30,38 +29,35 @@ import (
// Context represents context of a request.
type Context struct {
*macaron.Context
i18n.Locale
Cache cache.Cache
csrf csrf.CSRF
Flash *session.Flash
Session session.Store
User *models.User
IsSigned bool
User *models.User
IsSigned bool
IsBasicAuth bool
Repo struct {
IsOwner bool
IsTrueOwner bool
IsWatching bool
IsBranch bool
IsTag bool
IsCommit bool
IsAdmin bool // Current user is admin level.
HasAccess bool
Repository *models.Repository
Owner *models.User
Commit *git.Commit
Tag *git.Tag
GitRepo *git.Repository
BranchName string
TagName string
CommitId string
RepoLink string
CloneLink struct {
SSH string
HTTPS string
Git string
}
IsOwner bool
IsTrueOwner bool
IsWatching bool
IsBranch bool
IsTag bool
IsCommit bool
IsAdmin bool // Current user is admin level.
HasAccess bool
Repository *models.Repository
Owner *models.User
Commit *git.Commit
Tag *git.Tag
GitRepo *git.Repository
BranchName string
TagName string
TreeName string
CommitId string
RepoLink string
CloneLink models.CloneLink
CommitsCount int
Mirror *models.Mirror
}
@@ -77,12 +73,6 @@ type Context struct {
}
}
// Query querys form parameter.
func (ctx *Context) Query(name string) string {
ctx.Req.ParseForm()
return ctx.Req.Form.Get(name)
}
// HasError returns true if error occurs in form validation.
func (ctx *Context) HasApiError() bool {
hasErr, ok := ctx.Data["HasError"]
@@ -140,23 +130,6 @@ func (ctx *Context) Handle(status int, title string, err error) {
ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status)))
}
func (ctx *Context) ServeFile(file string, names ...string) {
var name string
if len(names) > 0 {
name = names[0]
} else {
name = path.Base(file)
}
ctx.Resp.Header().Set("Content-Description", "File Transfer")
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name)
ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
ctx.Resp.Header().Set("Expires", "0")
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
ctx.Resp.Header().Set("Pragma", "public")
http.ServeFile(ctx.Resp, ctx.Req, file)
}
func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
modtime := time.Now()
for _, p := range params {
@@ -172,7 +145,7 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa
ctx.Resp.Header().Set("Expires", "0")
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
ctx.Resp.Header().Set("Pragma", "public")
http.ServeContent(ctx.Resp, ctx.Req, name, modtime, r)
http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r)
}
// Contexter initializes a classic context for a request.
@@ -180,14 +153,13 @@ func Contexter() macaron.Handler {
return func(c *macaron.Context, l i18n.Locale, cache cache.Cache, sess session.Store, f *session.Flash, x csrf.CSRF) {
ctx := &Context{
Context: c,
Locale: l,
Cache: cache,
csrf: x,
Flash: f,
Session: sess,
}
// Compute current URL for real-time change language.
link := ctx.Req.RequestURI
link := setting.AppSubUrl + ctx.Req.RequestURI
i := strings.Index(link, "?")
if i > -1 {
link = link[:i]
@@ -197,12 +169,16 @@ func Contexter() macaron.Handler {
ctx.Data["PageStartTime"] = time.Now()
// Get user from session if logined.
ctx.User = auth.SignedInUser(ctx.Req.Header, ctx.Session)
ctx.User, ctx.IsBasicAuth = auth.SignedInUser(ctx.Req.Request, ctx.Session)
if ctx.User != nil {
ctx.IsSigned = true
ctx.Data["IsSigned"] = ctx.IsSigned
ctx.Data["SignedUser"] = ctx.User
ctx.Data["SignedUserName"] = ctx.User.Name
ctx.Data["IsAdmin"] = ctx.User.IsAdmin
} else {
ctx.Data["SignedUserName"] = ""
}
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.

View File

@@ -9,6 +9,7 @@ import (
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
@@ -37,7 +38,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
ctx.Handle(404, "GetUserByName", err)
} else if redirect {
log.Error(4, "GetUserByName", err)
ctx.Redirect("/")
ctx.Redirect(setting.AppSubUrl + "/")
} else {
ctx.Handle(500, "GetUserByName", err)
}
@@ -47,7 +48,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
ctx.Data["Org"] = org
if ctx.IsSigned {
ctx.Org.IsOwner = org.IsOrgOwner(ctx.User.Id)
ctx.Org.IsOwner = org.IsOwnedBy(ctx.User.Id)
if ctx.Org.IsOwner {
ctx.Org.IsMember = true
ctx.Org.IsAdminTeam = true
@@ -67,7 +68,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
}
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
ctx.Org.OrgLink = "/org/" + org.Name
ctx.Org.OrgLink = setting.AppSubUrl + "/org/" + org.Name
ctx.Data["OrgLink"] = ctx.Org.OrgLink
// Team.
@@ -79,7 +80,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
ctx.Handle(404, "GetTeam", err)
} else if redirect {
log.Error(4, "GetTeam", err)
ctx.Redirect("/")
ctx.Redirect(setting.AppSubUrl + "/")
} else {
ctx.Handle(500, "GetTeam", err)
}

View File

@@ -13,22 +13,221 @@ import (
"github.com/Unknwon/macaron"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
func ApiRepoAssignment() macaron.Handler {
return func(ctx *Context) {
userName := ctx.Params(":username")
repoName := ctx.Params(":reponame")
var (
u *models.User
err error
)
// Collaborators who have write access can be seen as owners.
if ctx.IsSigned {
ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE)
if err != nil {
ctx.JSON(500, &base.ApiJsonErr{"HasAccess: " + err.Error(), base.DOC_URL})
return
}
ctx.Repo.IsTrueOwner = ctx.User.LowerName == strings.ToLower(userName)
}
if !ctx.Repo.IsTrueOwner {
u, err = models.GetUserByName(userName)
if err != nil {
if err == models.ErrUserNotExist {
ctx.Error(404)
} else {
ctx.JSON(500, &base.ApiJsonErr{"GetUserByName: " + err.Error(), base.DOC_URL})
}
return
}
} else {
u = ctx.User
}
ctx.Repo.Owner = u
// Organization owner team members are true owners as well.
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
ctx.Repo.IsTrueOwner = true
}
// Get repository.
repo, err := models.GetRepositoryByName(u.Id, repoName)
if err != nil {
if err == models.ErrRepoNotExist {
ctx.Error(404)
return
}
ctx.JSON(500, &base.ApiJsonErr{"GetRepositoryByName: " + err.Error(), base.DOC_URL})
return
} else if err = repo.GetOwner(); err != nil {
ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL})
return
}
// Check if the mirror repository owner(mirror repository doesn't have access).
if ctx.IsSigned && !ctx.Repo.IsOwner {
if repo.OwnerId == ctx.User.Id {
ctx.Repo.IsOwner = true
}
// Check if current user has admin permission to repository.
if u.IsOrganization() {
auth, err := models.GetHighestAuthorize(u.Id, ctx.User.Id, repo.Id, 0)
if err != nil {
ctx.JSON(500, &base.ApiJsonErr{"GetHighestAuthorize: " + err.Error(), base.DOC_URL})
return
}
if auth == models.ORG_ADMIN {
ctx.Repo.IsOwner = true
ctx.Repo.IsAdmin = true
}
}
}
// Check access.
if repo.IsPrivate && !ctx.Repo.IsOwner {
if ctx.User == nil {
ctx.Error(404)
return
}
hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE)
if err != nil {
ctx.JSON(500, &base.ApiJsonErr{"HasAccess: " + err.Error(), base.DOC_URL})
return
} else if !hasAccess {
ctx.Error(404)
return
}
}
ctx.Repo.HasAccess = true
ctx.Repo.Repository = repo
}
}
// RepoRef handles repository reference name including those contain `/`.
func RepoRef() macaron.Handler {
return func(ctx *Context) {
var (
refName string
err error
)
// For API calls.
if ctx.Repo.GitRepo == nil {
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
gitRepo, err := git.OpenRepository(repoPath)
if err != nil {
ctx.Handle(500, "RepoRef Invalid repo "+repoPath, err)
return
}
ctx.Repo.GitRepo = gitRepo
}
// Get default branch.
if len(ctx.Params("*")) == 0 {
refName = ctx.Repo.Repository.DefaultBranch
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
brs, err := ctx.Repo.GitRepo.GetBranches()
if err != nil {
ctx.Handle(500, "GetBranches", err)
return
}
refName = brs[0]
}
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommitOfBranch(refName)
if err != nil {
ctx.Handle(500, "GetCommitOfBranch", err)
return
}
ctx.Repo.CommitId = ctx.Repo.Commit.Id.String()
ctx.Repo.IsBranch = true
} else {
hasMatched := false
parts := strings.Split(ctx.Params("*"), "/")
for i, part := range parts {
refName = strings.TrimPrefix(refName+"/"+part, "/")
if ctx.Repo.GitRepo.IsBranchExist(refName) ||
ctx.Repo.GitRepo.IsTagExist(refName) {
if i < len(parts)-1 {
ctx.Repo.TreeName = strings.Join(parts[i+1:], "/")
}
hasMatched = true
break
}
}
if !hasMatched && len(parts[0]) == 40 {
refName = parts[0]
ctx.Repo.TreeName = strings.Join(parts[1:], "/")
}
if ctx.Repo.GitRepo.IsBranchExist(refName) {
ctx.Repo.IsBranch = true
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommitOfBranch(refName)
if err != nil {
ctx.Handle(500, "GetCommitOfBranch", err)
return
}
ctx.Repo.CommitId = ctx.Repo.Commit.Id.String()
} else if ctx.Repo.GitRepo.IsTagExist(refName) {
ctx.Repo.IsTag = true
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommitOfTag(refName)
if err != nil {
ctx.Handle(500, "GetCommitOfTag", err)
return
}
ctx.Repo.CommitId = ctx.Repo.Commit.Id.String()
} else if len(refName) == 40 {
ctx.Repo.IsCommit = true
ctx.Repo.CommitId = refName
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
if err != nil {
ctx.Handle(404, "GetCommit", nil)
return
}
} else {
ctx.Handle(404, "RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
return
}
}
ctx.Repo.BranchName = refName
ctx.Data["BranchName"] = ctx.Repo.BranchName
ctx.Data["CommitId"] = ctx.Repo.CommitId
ctx.Data["IsBranch"] = ctx.Repo.IsBranch
ctx.Data["IsTag"] = ctx.Repo.IsTag
ctx.Data["IsCommit"] = ctx.Repo.IsCommit
ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
if err != nil {
ctx.Handle(500, "CommitsCount", err)
return
}
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
}
}
func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
return func(ctx *Context) {
var (
validBranch bool // To valid brach name.
displayBare bool // To display bare page if it is a bare repo.
)
if len(args) >= 1 {
validBranch = args[0]
}
if len(args) >= 2 {
displayBare = args[1]
displayBare = args[0]
}
var (
@@ -60,7 +259,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
ctx.Handle(404, "GetUserByName", err)
} else if redirect {
log.Error(4, "GetUserByName", err)
ctx.Redirect("/")
ctx.Redirect(setting.AppSubUrl + "/")
} else {
ctx.Handle(500, "GetUserByName", err)
}
@@ -72,7 +271,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
if u == nil {
if redirect {
ctx.Redirect("/")
ctx.Redirect(setting.AppSubUrl + "/")
return
}
ctx.Handle(404, "RepoAssignment", errors.New("invliad user account for single repository"))
@@ -81,7 +280,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
ctx.Repo.Owner = u
// Organization owner team members are true owners as well.
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgOwner(ctx.User.Id) {
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
ctx.Repo.IsTrueOwner = true
}
@@ -92,7 +291,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
ctx.Handle(404, "GetRepositoryByName", err)
return
} else if redirect {
ctx.Redirect("/")
ctx.Redirect(setting.AppSubUrl + "/")
return
}
ctx.Handle(500, "GetRepositoryByName", err)
@@ -160,7 +359,11 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
return
}
ctx.Repo.GitRepo = gitRepo
ctx.Repo.RepoLink = "/" + u.Name + "/" + repo.Name
ctx.Repo.RepoLink, err = repo.RepoLink()
if err != nil {
ctx.Handle(500, "RepoLink", err)
return
}
ctx.Data["RepoLink"] = ctx.Repo.RepoLink
tags, err := ctx.Repo.GitRepo.GetTags()
@@ -168,20 +371,26 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
ctx.Handle(500, "GetTags", err)
return
}
ctx.Data["Tags"] = tags
ctx.Repo.Repository.NumTags = len(tags)
// Non-fork repository will not return error in this method.
if err = repo.GetForkRepo(); err != nil {
ctx.Handle(500, "GetForkRepo", err)
return
}
ctx.Data["Title"] = u.Name + "/" + repo.Name
ctx.Data["Repository"] = repo
ctx.Data["Owner"] = ctx.Repo.Repository.Owner
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner
ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner
if setting.SshPort != 22 {
ctx.Repo.CloneLink.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.Domain, setting.SshPort, u.LowerName, repo.LowerName)
} else {
ctx.Repo.CloneLink.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, u.LowerName, repo.LowerName)
ctx.Repo.CloneLink, err = repo.CloneLink()
if err != nil {
ctx.Handle(500, "CloneLink", err)
return
}
ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, u.LowerName, repo.LowerName)
ctx.Data["CloneLink"] = ctx.Repo.CloneLink
if ctx.Repo.Repository.IsGoget {
@@ -189,77 +398,11 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
ctx.Data["GoGetImport"] = fmt.Sprintf("%s/%s/%s", setting.Domain, u.LowerName, repo.LowerName)
}
// when repo is bare, not valid branch
if !ctx.Repo.Repository.IsBare && validBranch {
detect:
if len(refName) > 0 {
if gitRepo.IsBranchExist(refName) {
ctx.Repo.IsBranch = true
ctx.Repo.BranchName = refName
ctx.Repo.Commit, err = gitRepo.GetCommitOfBranch(refName)
if err != nil {
ctx.Handle(404, "RepoAssignment invalid branch", nil)
return
}
ctx.Repo.CommitId = ctx.Repo.Commit.Id.String()
} else if gitRepo.IsTagExist(refName) {
ctx.Repo.IsTag = true
ctx.Repo.BranchName = refName
ctx.Repo.Tag, err = gitRepo.GetTag(refName)
if err != nil {
ctx.Handle(404, "RepoAssignment invalid tag", nil)
return
}
ctx.Repo.Commit, _ = ctx.Repo.Tag.Commit()
ctx.Repo.CommitId = ctx.Repo.Commit.Id.String()
} else if len(refName) == 40 {
ctx.Repo.IsCommit = true
ctx.Repo.CommitId = refName
ctx.Repo.BranchName = refName
ctx.Repo.Commit, err = gitRepo.GetCommit(refName)
if err != nil {
ctx.Handle(404, "RepoAssignment invalid commit", nil)
return
}
} else {
ctx.Handle(404, "RepoAssignment invalid repo", errors.New("branch or tag not exist"))
return
}
} else {
if len(refName) == 0 {
if gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
refName = ctx.Repo.Repository.DefaultBranch
} else {
brs, err := gitRepo.GetBranches()
if err != nil {
ctx.Handle(500, "GetBranches", err)
return
}
refName = brs[0]
}
}
goto detect
}
ctx.Data["IsBranch"] = ctx.Repo.IsBranch
ctx.Data["IsCommit"] = ctx.Repo.IsCommit
ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
if err != nil {
ctx.Handle(500, "CommitsCount", err)
return
}
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
}
// repo is bare and display enable
if ctx.Repo.Repository.IsBare {
log.Debug("Bare repository: %s", ctx.Repo.RepoLink)
// NOTE: to prevent templating error
ctx.Data["BranchName"] = ""
if displayBare {
ctx.HTML(200, "repo/bare")
}
@@ -274,7 +417,8 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
ctx.Data["TagName"] = ctx.Repo.TagName
brs, err := ctx.Repo.GitRepo.GetBranches()
if err != nil {
log.Error(4, "GetBranches: %v", err)
ctx.Handle(500, "GetBranches", err)
return
}
ctx.Data["Branches"] = brs
ctx.Data["BrancheCount"] = len(brs)
@@ -298,8 +442,8 @@ func RequireTrueOwner() macaron.Handler {
return func(ctx *Context) {
if !ctx.Repo.IsTrueOwner && !ctx.Repo.IsAdmin {
if !ctx.IsSigned {
ctx.SetCookie("redirect_to", "/"+url.QueryEscape(ctx.Req.RequestURI))
ctx.Redirect("/user/login")
ctx.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
ctx.Redirect(setting.AppSubUrl + "/user/login")
return
}
ctx.Handle(404, ctx.Req.RequestURI, nil)
@@ -307,3 +451,13 @@ func RequireTrueOwner() macaron.Handler {
}
}
}
// GitHookService checks if repository Git hooks service has been enabled.
func GitHookService() macaron.Handler {
return func(ctx *Context) {
if !ctx.User.AllowGitHook && !ctx.User.IsAdmin {
ctx.Handle(404, "GitHookService", nil)
return
}
}
}

View File

@@ -6,6 +6,7 @@ package setting
import (
"fmt"
"net/url"
"os"
"os/exec"
"path"
@@ -16,10 +17,11 @@ import (
"github.com/Unknwon/com"
"github.com/Unknwon/goconfig"
"github.com/macaron-contrib/oauth2"
"github.com/macaron-contrib/session"
"github.com/gogits/gogs/modules/log"
// "github.com/gogits/gogs-ng/modules/ssh"
// "github.com/gogits/gogs/modules/ssh"
)
type Scheme string
@@ -27,13 +29,22 @@ type Scheme string
const (
HTTP Scheme = "http"
HTTPS Scheme = "https"
FCGI Scheme = "fcgi"
)
type LandingPage string
const (
LANDING_PAGE_HOME LandingPage = "/"
LANDING_PAGE_EXPLORE LandingPage = "/explore"
)
var (
// App settings.
AppVer string
AppName string
AppUrl string
AppVer string
AppName string
AppUrl string
AppSubUrl string
// Server settings.
Protocol Scheme
@@ -45,6 +56,7 @@ var (
CertFile, KeyFile string
StaticRootPath string
EnableGzip bool
LandingPageUrl LandingPage
// Security settings.
InstallLock bool
@@ -63,8 +75,10 @@ var (
ScriptType string
// Picture settings.
PictureService string
DisableGravatar bool
PictureService string
AvatarUploadPath string
GravatarSource string
DisableGravatar bool
// Log settings.
LogRootPath string
@@ -95,6 +109,8 @@ var (
// Git settings.
MaxGitDiffLines int
GitFsckArgs []string
GitGcArgs []string
// I18n settings.
Langs, Names []string
@@ -106,6 +122,7 @@ var (
ProdMode bool
RunUser string
IsWindows bool
HasRobotsTxt bool
)
func init() {
@@ -165,12 +182,22 @@ func NewConfigContext() {
AppUrl += "/"
}
// Check if has app suburl.
url, err := url.Parse(AppUrl)
if err != nil {
log.Fatal(4, "Invalid ROOT_URL(%s): %s", AppUrl, err)
}
AppSubUrl = strings.TrimSuffix(url.Path, "/")
Protocol = HTTP
if Cfg.MustValue("server", "PROTOCOL") == "https" {
Protocol = HTTPS
CertFile = Cfg.MustValue("server", "CERT_FILE")
KeyFile = Cfg.MustValue("server", "KEY_FILE")
}
if Cfg.MustValue("server", "PROTOCOL") == "fcgi" {
Protocol = FCGI
}
Domain = Cfg.MustValue("server", "DOMAIN", "localhost")
HttpAddr = Cfg.MustValue("server", "HTTP_ADDR", "0.0.0.0")
HttpPort = Cfg.MustValue("server", "HTTP_PORT", "3000")
@@ -181,6 +208,13 @@ func NewConfigContext() {
LogRootPath = Cfg.MustValue("log", "ROOT_PATH", path.Join(workDir, "log"))
EnableGzip = Cfg.MustBool("server", "ENABLE_GZIP")
switch Cfg.MustValue("server", "LANDING_PAGE", "home") {
case "explore":
LandingPageUrl = LANDING_PAGE_EXPLORE
default:
LandingPageUrl = LANDING_PAGE_HOME
}
InstallLock = Cfg.MustBool("security", "INSTALL_LOCK")
SecretKey = Cfg.MustValue("security", "SECRET_KEY")
LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS")
@@ -226,7 +260,7 @@ func NewConfigContext() {
log.Fatal(4, "Expect user(%s) but current user is: %s", RunUser, curUser)
}
// Determine and create root git reposiroty path.
// Determine and create root git repository path.
homeDir, err := com.HomeDir()
if err != nil {
log.Fatal(4, "Fail to get home directory: %v", err)
@@ -243,26 +277,38 @@ func NewConfigContext() {
}
ScriptType = Cfg.MustValue("repository", "SCRIPT_TYPE", "bash")
PictureService = Cfg.MustValueRange("picture", "SERVICE", "server",
[]string{"server"})
PictureService = Cfg.MustValueRange("picture", "SERVICE", "server", []string{"server"})
AvatarUploadPath = Cfg.MustValue("picture", "AVATAR_UPLOAD_PATH", "data/avatars")
os.MkdirAll(AvatarUploadPath, os.ModePerm)
switch Cfg.MustValue("picture", "GRAVATAR_SOURCE", "gravatar") {
case "duoshuo":
GravatarSource = "http://gravatar.duoshuo.com/avatar/"
default:
GravatarSource = "//1.gravatar.com/avatar/"
}
DisableGravatar = Cfg.MustBool("picture", "DISABLE_GRAVATAR")
MaxGitDiffLines = Cfg.MustInt("git", "MAX_GITDIFF_LINES", 10000)
GitFsckArgs = Cfg.MustValueArray("git", "FSCK_ARGS", " ")
GitGcArgs = Cfg.MustValueArray("git", "GC_ARGS", " ")
Langs = Cfg.MustValueArray("i18n", "LANGS", ",")
Names = Cfg.MustValueArray("i18n", "NAMES", ",")
HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
}
var Service struct {
RegisterEmailConfirm bool
DisableRegistration bool
RequireSignInView bool
EnableCacheAvatar bool
EnableNotifyMail bool
EnableReverseProxyAuth bool
LdapAuth bool
ActiveCodeLives int
ResetPwdCodeLives int
RegisterEmailConfirm bool
DisableRegistration bool
RequireSignInView bool
EnableCacheAvatar bool
EnableNotifyMail bool
EnableReverseProxyAuth bool
EnableReverseProxyAutoRegister bool
ActiveCodeLives int
ResetPwdCodeLives int
}
func newService() {
@@ -272,6 +318,7 @@ func newService() {
Service.RequireSignInView = Cfg.MustBool("service", "REQUIRE_SIGNIN_VIEW")
Service.EnableCacheAvatar = Cfg.MustBool("service", "ENABLE_CACHE_AVATAR")
Service.EnableReverseProxyAuth = Cfg.MustBool("service", "ENABLE_REVERSE_PROXY_AUTHENTICATION")
Service.EnableReverseProxyAutoRegister = Cfg.MustBool("service", "ENABLE_REVERSE_PROXY_AUTO_REGISTERATION")
}
var logLevels = map[string]string{
@@ -371,13 +418,11 @@ func newSessionService() {
SessionConfig = new(session.Config)
SessionConfig.ProviderConfig = strings.Trim(Cfg.MustValue("session", "PROVIDER_CONFIG"), "\" ")
SessionConfig.CookieName = Cfg.MustValue("session", "COOKIE_NAME", "i_like_gogits")
SessionConfig.CookiePath = AppSubUrl
SessionConfig.Secure = Cfg.MustBool("session", "COOKIE_SECURE")
SessionConfig.EnableSetCookie = Cfg.MustBool("session", "ENABLE_SET_COOKIE", true)
SessionConfig.Gclifetime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400)
SessionConfig.Maxlifetime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400)
SessionConfig.SessionIDHashFunc = Cfg.MustValueRange("session", "SESSION_ID_HASHFUNC",
"sha1", []string{"sha1", "sha256", "md5"})
SessionConfig.SessionIDHashKey = Cfg.MustValue("session", "SESSION_ID_HASHKEY", string(com.RandomCreateBytes(16)))
if SessionProvider == "file" {
os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm)
@@ -395,9 +440,8 @@ type Mailer struct {
}
type OauthInfo struct {
ClientId, ClientSecret string
Scopes string
AuthUrl, TokenUrl string
oauth2.Options
AuthUrl, TokenUrl string
}
// Oauther represents oauth service.
@@ -464,5 +508,5 @@ func NewServices() {
newRegisterMailService()
newNotifyMailService()
newWebhookService()
// ssh.Listen("2022")
// ssh.Listen("2222")
}

View File

@@ -7,12 +7,12 @@ package social
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
oauth "github.com/gogits/oauth2"
"github.com/macaron-contrib/oauth2"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/log"
@@ -27,16 +27,11 @@ type BasicUserInfo struct {
type SocialConnector interface {
Type() int
SetRedirectUrl(string)
UserInfo(*oauth.Token, *url.URL) (*BasicUserInfo, error)
AuthCodeURL(string) string
Exchange(string) (*oauth.Token, error)
UserInfo(*oauth2.Token, *url.URL) (*BasicUserInfo, error)
}
var (
SocialBaseUrl = "/user/login"
SocialMap = make(map[string]SocialConnector)
SocialMap = make(map[string]SocialConnector)
)
func NewOauthService() {
@@ -44,27 +39,34 @@ func NewOauthService() {
return
}
oauth2.AppSubUrl = setting.AppSubUrl
setting.OauthService = &setting.Oauther{}
setting.OauthService.OauthInfos = make(map[string]*setting.OauthInfo)
socialConfigs := make(map[string]*oauth.Config)
socialConfigs := make(map[string]*oauth2.Options)
allOauthes := []string{"github", "google", "qq", "twitter", "weibo"}
// Load all OAuth config data.
for _, name := range allOauthes {
setting.OauthService.OauthInfos[name] = &setting.OauthInfo{
ClientId: setting.Cfg.MustValue("oauth."+name, "CLIENT_ID"),
ClientSecret: setting.Cfg.MustValue("oauth."+name, "CLIENT_SECRET"),
Scopes: setting.Cfg.MustValue("oauth."+name, "SCOPES"),
AuthUrl: setting.Cfg.MustValue("oauth."+name, "AUTH_URL"),
TokenUrl: setting.Cfg.MustValue("oauth."+name, "TOKEN_URL"),
if !setting.Cfg.MustBool("oauth."+name, "ENABLED") {
continue
}
socialConfigs[name] = &oauth.Config{
ClientId: setting.OauthService.OauthInfos[name].ClientId,
setting.OauthService.OauthInfos[name] = &setting.OauthInfo{
Options: oauth2.Options{
ClientID: setting.Cfg.MustValue("oauth."+name, "CLIENT_ID"),
ClientSecret: setting.Cfg.MustValue("oauth."+name, "CLIENT_SECRET"),
Scopes: setting.Cfg.MustValueArray("oauth."+name, "SCOPES", " "),
PathLogin: "/user/login/oauth2/" + name,
PathCallback: setting.AppSubUrl + "/user/login/" + name,
RedirectURL: setting.AppUrl + "user/login/" + name,
},
AuthUrl: setting.Cfg.MustValue("oauth."+name, "AUTH_URL"),
TokenUrl: setting.Cfg.MustValue("oauth."+name, "TOKEN_URL"),
}
socialConfigs[name] = &oauth2.Options{
ClientID: setting.OauthService.OauthInfos[name].ClientID,
ClientSecret: setting.OauthService.OauthInfos[name].ClientSecret,
RedirectURL: strings.TrimSuffix(setting.AppUrl, "/") + SocialBaseUrl + name,
Scope: setting.OauthService.OauthInfos[name].Scopes,
AuthURL: setting.OauthService.OauthInfos[name].AuthUrl,
TokenURL: setting.OauthService.OauthInfos[name].TokenUrl,
Scopes: setting.OauthService.OauthInfos[name].Scopes,
}
}
enabledOauths := make([]string, 0, 10)
@@ -91,11 +93,11 @@ func NewOauthService() {
}
// Twitter.
if setting.Cfg.MustBool("oauth.twitter", "ENABLED") {
setting.OauthService.Twitter = true
newTwitterOauth(socialConfigs["twitter"])
enabledOauths = append(enabledOauths, "Twitter")
}
// if setting.Cfg.MustBool("oauth.twitter", "ENABLED") {
// setting.OauthService.Twitter = true
// newTwitterOauth(socialConfigs["twitter"])
// enabledOauths = append(enabledOauths, "Twitter")
// }
// Weibo.
if setting.Cfg.MustBool("oauth.weibo", "ENABLED") {
@@ -115,38 +117,25 @@ func NewOauthService() {
// \/ \/ \/
type SocialGithub struct {
Token *oauth.Token
*oauth.Transport
opts *oauth2.Options
}
func newGitHubOauth(opts *oauth2.Options) {
SocialMap["github"] = &SocialGithub{opts}
}
func (s *SocialGithub) Type() int {
return int(models.GITHUB)
}
func newGitHubOauth(config *oauth.Config) {
SocialMap["github"] = &SocialGithub{
Transport: &oauth.Transport{
Config: config,
Transport: http.DefaultTransport,
},
}
}
func (s *SocialGithub) SetRedirectUrl(url string) {
s.Transport.Config.RedirectURL = url
}
func (s *SocialGithub) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
transport := &oauth.Transport{
Token: token,
}
func (s *SocialGithub) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
transport := s.opts.NewTransportFromToken(token)
var data struct {
Id int `json:"id"`
Name string `json:"login"`
Email string `json:"email"`
}
var err error
r, err := transport.Client().Get(s.Transport.Scope)
r, err := transport.Client().Get("https://api.github.com/user")
if err != nil {
return nil, err
}
@@ -169,38 +158,25 @@ func (s *SocialGithub) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo,
// \/ /_____/ \/
type SocialGoogle struct {
Token *oauth.Token
*oauth.Transport
opts *oauth2.Options
}
func (s *SocialGoogle) Type() int {
return int(models.GOOGLE)
}
func newGoogleOauth(config *oauth.Config) {
SocialMap["google"] = &SocialGoogle{
Transport: &oauth.Transport{
Config: config,
Transport: http.DefaultTransport,
},
}
func newGoogleOauth(opts *oauth2.Options) {
SocialMap["google"] = &SocialGoogle{opts}
}
func (s *SocialGoogle) SetRedirectUrl(url string) {
s.Transport.Config.RedirectURL = url
}
func (s *SocialGoogle) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
transport := &oauth.Transport{Token: token}
func (s *SocialGoogle) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
transport := s.opts.NewTransportFromToken(token)
var data struct {
Id string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
var err error
reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
r, err := transport.Client().Get(reqUrl)
r, err := transport.Client().Get("https://www.googleapis.com/userinfo/v2/me")
if err != nil {
return nil, err
}
@@ -223,62 +199,35 @@ func (s *SocialGoogle) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo,
// \__> \__>
type SocialTencent struct {
Token *oauth.Token
*oauth.Transport
reqUrl string
opts *oauth2.Options
}
func newTencentOauth(opts *oauth2.Options) {
SocialMap["qq"] = &SocialTencent{opts}
}
func (s *SocialTencent) Type() int {
return int(models.QQ)
}
func newTencentOauth(config *oauth.Config) {
SocialMap["qq"] = &SocialTencent{
reqUrl: "https://open.t.qq.com/api/user/info",
Transport: &oauth.Transport{
Config: config,
Transport: http.DefaultTransport,
},
}
}
func (s *SocialTencent) SetRedirectUrl(url string) {
s.Transport.Config.RedirectURL = url
}
func (s *SocialTencent) UserInfo(token *oauth.Token, URL *url.URL) (*BasicUserInfo, error) {
var data struct {
Data struct {
Id string `json:"openid"`
Name string `json:"name"`
Email string `json:"email"`
} `json:"data"`
}
var err error
// https://open.t.qq.com/api/user/info?
//oauth_consumer_key=APP_KEY&
//access_token=ACCESSTOKEN&openid=openid
//clientip=CLIENTIP&oauth_version=2.a
//scope=all
var urls = url.Values{
"oauth_consumer_key": {s.Transport.Config.ClientId},
"access_token": {token.AccessToken},
"openid": URL.Query()["openid"],
"oauth_version": {"2.a"},
"scope": {"all"},
}
r, err := http.Get(s.reqUrl + "?" + urls.Encode())
func (s *SocialTencent) UserInfo(token *oauth2.Token, URL *url.URL) (*BasicUserInfo, error) {
r, err := http.Get("https://graph.z.qq.com/moc2/me?access_token=" + url.QueryEscape(token.AccessToken))
if err != nil {
return nil, err
}
defer r.Body.Close()
if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
vals, err := url.ParseQuery(string(body))
if err != nil {
return nil, err
}
return &BasicUserInfo{
Identity: data.Data.Id,
Name: data.Data.Name,
Email: data.Data.Email,
Identity: vals.Get("openid"),
}, nil
}
@@ -289,54 +238,54 @@ func (s *SocialTencent) UserInfo(token *oauth.Token, URL *url.URL) (*BasicUserIn
// |____| \/\_/ |__||__| |__| \___ >__|
// \/
type SocialTwitter struct {
Token *oauth.Token
*oauth.Transport
}
// type SocialTwitter struct {
// Token *oauth2.Token
// *oauth2.Transport
// }
func (s *SocialTwitter) Type() int {
return int(models.TWITTER)
}
// func (s *SocialTwitter) Type() int {
// return int(models.TWITTER)
// }
func newTwitterOauth(config *oauth.Config) {
SocialMap["twitter"] = &SocialTwitter{
Transport: &oauth.Transport{
Config: config,
Transport: http.DefaultTransport,
},
}
}
// func newTwitterOauth(config *oauth2.Config) {
// SocialMap["twitter"] = &SocialTwitter{
// Transport: &oauth.Transport{
// Config: config,
// Transport: http.DefaultTransport,
// },
// }
// }
func (s *SocialTwitter) SetRedirectUrl(url string) {
s.Transport.Config.RedirectURL = url
}
// func (s *SocialTwitter) SetRedirectUrl(url string) {
// s.Transport.Config.RedirectURL = url
// }
//https://github.com/mrjones/oauth
func (s *SocialTwitter) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
// transport := &oauth.Transport{Token: token}
// var data struct {
// Id string `json:"id"`
// Name string `json:"name"`
// Email string `json:"email"`
// }
// var err error
// //https://github.com/mrjones/oauth
// func (s *SocialTwitter) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
// // transport := &oauth.Transport{Token: token}
// // var data struct {
// // Id string `json:"id"`
// // Name string `json:"name"`
// // Email string `json:"email"`
// // }
// // var err error
// reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
// r, err := transport.Client().Get(reqUrl)
// if err != nil {
// return nil, err
// }
// defer r.Body.Close()
// if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
// return nil, err
// }
// return &BasicUserInfo{
// Identity: data.Id,
// Name: data.Name,
// Email: data.Email,
// }, nil
return nil, nil
}
// // reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
// // r, err := transport.Client().Get(reqUrl)
// // if err != nil {
// // return nil, err
// // }
// // defer r.Body.Close()
// // if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
// // return nil, err
// // }
// // return &BasicUserInfo{
// // Identity: data.Id,
// // Name: data.Name,
// // Email: data.Email,
// // }, nil
// return nil, nil
// }
// __ __ ._____.
// / \ / \ ____ |__\_ |__ ____
@@ -346,37 +295,25 @@ func (s *SocialTwitter) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo
// \/ \/ \/
type SocialWeibo struct {
Token *oauth.Token
*oauth.Transport
opts *oauth2.Options
}
func newWeiboOauth(opts *oauth2.Options) {
SocialMap["weibo"] = &SocialWeibo{opts}
}
func (s *SocialWeibo) Type() int {
return int(models.WEIBO)
}
func newWeiboOauth(config *oauth.Config) {
SocialMap["weibo"] = &SocialWeibo{
Transport: &oauth.Transport{
Config: config,
Transport: http.DefaultTransport,
},
}
}
func (s *SocialWeibo) SetRedirectUrl(url string) {
s.Transport.Config.RedirectURL = url
}
func (s *SocialWeibo) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
transport := &oauth.Transport{Token: token}
func (s *SocialWeibo) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
transport := s.opts.NewTransportFromToken(token)
var data struct {
Name string `json:"name"`
}
var err error
var urls = url.Values{
"access_token": {token.AccessToken},
"uid": {token.Extra["id_token"]},
"uid": {token.Extra("uid")},
}
reqUrl := "https://api.weibo.com/2/users/show.json"
r, err := transport.Client().Get(reqUrl + "?" + urls.Encode())
@@ -384,11 +321,12 @@ func (s *SocialWeibo) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo,
return nil, err
}
defer r.Body.Close()
if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
return nil, err
}
return &BasicUserInfo{
Identity: token.Extra["id_token"],
Identity: token.Extra("uid"),
Name: data.Name,
}, nil
}

View File

@@ -13,9 +13,8 @@ import (
"os/exec"
"strings"
"code.google.com/p/go.crypto/ssh"
"github.com/Unknwon/com"
"golang.org/x/crypto/ssh"
"github.com/gogits/gogs/modules/log"
)
@@ -35,8 +34,11 @@ func handleServerConn(keyId string, chans <-chan ssh.NewChannel) {
go func(in <-chan *ssh.Request) {
defer channel.Close()
for req := range in {
ok, payload := false, strings.TrimLeft(string(req.Payload), "\x00")
ok, payload := false, strings.TrimLeft(string(req.Payload), "\x00&")
fmt.Println("Request:", req.Type, req.WantReply, payload)
if req.WantReply {
fmt.Println(req.Reply(true, nil))
}
switch req.Type {
case "env":
args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v")
@@ -54,7 +56,7 @@ func handleServerConn(keyId string, chans <-chan ssh.NewChannel) {
case "exec":
os.Setenv("SSH_ORIGINAL_COMMAND", strings.TrimLeft(payload, "'("))
log.Info("Payload: %v", strings.TrimLeft(payload, "'("))
cmd := exec.Command("/Users/jiahuachen/Applications/Go/src/github.com/gogits/gogs-ng/gogs-ng", "serv", "key-"+keyId)
cmd := exec.Command("/Users/jiahuachen/Applications/Go/src/github.com/gogits/gogs/gogs", "serv", "key-"+keyId)
cmd.Stdout = channel
cmd.Stdin = channel
cmd.Stderr = channel.Stderr()
@@ -65,7 +67,6 @@ func handleServerConn(keyId string, chans <-chan ssh.NewChannel) {
}
}
fmt.Println("Done:", ok)
req.Reply(ok, nil) // BUG: Git on Mac seems not know this reply and hang?
}
fmt.Println("Done!!!")
}(requests)
@@ -101,7 +102,7 @@ func Listen(port string) {
config := &ssh.ServerConfig{
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
// keyCache[string(ssh.MarshalAuthorizedKey(key))] = 2
return &ssh.Permissions{Extensions: map[string]string{"key-id": "2"}}, nil
return &ssh.Permissions{Extensions: map[string]string{"key-id": "1"}}, nil
},
}

View File

@@ -2,18 +2,6 @@
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <postinst> `abort-remove'
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
APP_NAME="gogs"
CLI="${APP_NAME}"
APP_USER=$(${CLI} config:get APP_USER)
@@ -21,28 +9,27 @@ APP_GROUP=$(${CLI} config:get APP_GROUP)
APP_CONFIG="/etc/${APP_NAME}/conf/app.ini"
case "$1" in
abort-upgrade|abort-remove|abort-deconfigure)
exit 0
;;
configure)
configure|*)
mkdir -p $(dirname ${APP_CONFIG})
chown ${APP_USER}.${APP_GROUP} $(dirname ${APP_CONFIG})
[ -f ${APP_CONFIG} ] || ${CLI} run cp conf/app.ini ${APP_CONFIG}
${CLI} config:set USER=${APP_USER}
${CLI} config:set GOGS_CUSTOM="/etc/${APP_NAME}"
PORT=$(${CLI} config:get PORT || echo "6000")
sed -i "s|HTTP_PORT = 3000|HTTP_PORT = ${PORT}|" ${APP_CONFIG}
sed -i "s|RUN_USER = git|RUN_USER = ${APP_USER}|" ${APP_CONFIG}
sed -i "s|RUN_MODE = dev|RUN_MODE = prod|" ${APP_CONFIG}
# setup symlink towards custom conf
mkdir -p /opt/${APP_NAME}/custom/conf
chown -R ${APP_USER}.${APP_GROUP} /opt/${APP_NAME}/custom
ln -f -s ${APP_CONFIG} /opt/${APP_NAME}/custom/conf/app.ini
# scale
${CLI} scale web=1 || true
;;
abort-upgrade|abort-remove|abort-deconfigure)
exit 0
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac

View File

@@ -303,6 +303,9 @@ var Gogits = {};
// api working
Gogits.getUsers = function (val, $target) {
var notEmpty = function (str) {
return str && str.length > 0;
}
$.ajax({
url: '/api/v1/users/search?q=' + val,
dataType: "json",
@@ -310,7 +313,11 @@ var Gogits = {};
if (json.ok && json.data.length) {
var html = '';
$.each(json.data, function (i, item) {
html += '<li><img src="' + item.avatar + '">' + item.username + '</li>';
html += '<li><img src="' + item.avatar + '">' + item.username;
if (notEmpty(item.full_name)) {
html += ' (' + item.full_name + ')';
}
html += '</li>';
});
$target.toggleShow();
$target.find('ul').html(html);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,368 @@
/* Magnific Popup CSS */
.mfp-bg {
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1042;
overflow: hidden;
position: fixed;
background: #0b0b0b;
opacity: 0.8;
filter: alpha(opacity=80); }
.mfp-wrap {
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1043;
position: fixed;
outline: none !important;
-webkit-backface-visibility: hidden; }
.mfp-container {
text-align: center;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
padding: 0 8px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box; }
.mfp-container:before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle; }
.mfp-align-top .mfp-container:before {
display: none; }
.mfp-content {
position: relative;
display: inline-block;
vertical-align: middle;
margin: 0 auto;
text-align: left;
z-index: 1045; }
.mfp-inline-holder .mfp-content, .mfp-ajax-holder .mfp-content {
width: 100%;
cursor: auto; }
.mfp-ajax-cur {
cursor: progress; }
.mfp-zoom-out-cur, .mfp-zoom-out-cur .mfp-image-holder .mfp-close {
cursor: -moz-zoom-out;
cursor: -webkit-zoom-out;
cursor: zoom-out; }
.mfp-zoom {
cursor: pointer;
cursor: -webkit-zoom-in;
cursor: -moz-zoom-in;
cursor: zoom-in; }
.mfp-auto-cursor .mfp-content {
cursor: auto; }
.mfp-close, .mfp-arrow, .mfp-preloader, .mfp-counter {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none; }
.mfp-loading.mfp-figure {
display: none; }
.mfp-hide {
display: none !important; }
.mfp-preloader {
color: #cccccc;
position: absolute;
top: 50%;
width: auto;
text-align: center;
margin-top: -0.8em;
left: 8px;
right: 8px;
z-index: 1044; }
.mfp-preloader a {
color: #cccccc; }
.mfp-preloader a:hover {
color: white; }
.mfp-s-ready .mfp-preloader {
display: none; }
.mfp-s-error .mfp-content {
display: none; }
button.mfp-close, button.mfp-arrow {
overflow: visible;
cursor: pointer;
background: transparent;
border: 0;
-webkit-appearance: none;
display: block;
outline: none;
padding: 0;
z-index: 1046;
-webkit-box-shadow: none;
box-shadow: none; }
button::-moz-focus-inner {
padding: 0;
border: 0; }
.mfp-close {
width: 44px;
height: 44px;
line-height: 44px;
position: absolute;
right: 0;
top: 0;
text-decoration: none;
text-align: center;
opacity: 0.65;
filter: alpha(opacity=65);
padding: 0 0 18px 10px;
color: white;
font-style: normal;
font-size: 28px;
font-family: Arial, Baskerville, monospace; }
.mfp-close:hover, .mfp-close:focus {
opacity: 1;
filter: alpha(opacity=100); }
.mfp-close:active {
top: 1px; }
.mfp-close-btn-in .mfp-close {
color: #333333; }
.mfp-image-holder .mfp-close, .mfp-iframe-holder .mfp-close {
color: white;
right: -6px;
text-align: right;
padding-right: 6px;
width: 100%; }
.mfp-counter {
position: absolute;
top: 0;
right: 0;
color: #cccccc;
font-size: 12px;
line-height: 18px; }
.mfp-arrow {
position: absolute;
opacity: 0.65;
filter: alpha(opacity=65);
margin: 0;
top: 50%;
margin-top: -55px;
padding: 0;
width: 90px;
height: 110px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
.mfp-arrow:active {
margin-top: -54px; }
.mfp-arrow:hover, .mfp-arrow:focus {
opacity: 1;
filter: alpha(opacity=100); }
.mfp-arrow:before, .mfp-arrow:after, .mfp-arrow .mfp-b, .mfp-arrow .mfp-a {
content: '';
display: block;
width: 0;
height: 0;
position: absolute;
left: 0;
top: 0;
margin-top: 35px;
margin-left: 35px;
border: medium inset transparent; }
.mfp-arrow:after, .mfp-arrow .mfp-a {
border-top-width: 13px;
border-bottom-width: 13px;
top: 8px; }
.mfp-arrow:before, .mfp-arrow .mfp-b {
border-top-width: 21px;
border-bottom-width: 21px;
opacity: 0.7; }
.mfp-arrow-left {
left: 0; }
.mfp-arrow-left:after, .mfp-arrow-left .mfp-a {
border-right: 17px solid white;
margin-left: 31px; }
.mfp-arrow-left:before, .mfp-arrow-left .mfp-b {
margin-left: 25px;
border-right: 27px solid #3f3f3f; }
.mfp-arrow-right {
right: 0; }
.mfp-arrow-right:after, .mfp-arrow-right .mfp-a {
border-left: 17px solid white;
margin-left: 39px; }
.mfp-arrow-right:before, .mfp-arrow-right .mfp-b {
border-left: 27px solid #3f3f3f; }
.mfp-iframe-holder {
padding-top: 40px;
padding-bottom: 40px; }
.mfp-iframe-holder .mfp-content {
line-height: 0;
width: 100%;
max-width: 900px; }
.mfp-iframe-holder .mfp-close {
top: -40px; }
.mfp-iframe-scaler {
width: 100%;
height: 0;
overflow: hidden;
padding-top: 56.25%; }
.mfp-iframe-scaler iframe {
position: absolute;
display: block;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
background: black; }
/* Main image in popup */
img.mfp-img {
width: auto;
max-width: 100%;
height: auto;
display: block;
line-height: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 40px 0 40px;
margin: 0 auto; }
/* The shadow behind the image */
.mfp-figure {
line-height: 0; }
.mfp-figure:after {
content: '';
position: absolute;
left: 0;
top: 40px;
bottom: 40px;
display: block;
right: 0;
width: auto;
height: auto;
z-index: -1;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
background: #444444; }
.mfp-figure small {
color: #bdbdbd;
display: block;
font-size: 12px;
line-height: 14px; }
.mfp-figure figure {
margin: 0; }
.mfp-bottom-bar {
margin-top: -36px;
position: absolute;
top: 100%;
left: 0;
width: 100%;
cursor: auto; }
.mfp-title {
text-align: left;
line-height: 18px;
color: #f3f3f3;
word-wrap: break-word;
padding-right: 36px; }
.mfp-image-holder .mfp-content {
max-width: 100%; }
.mfp-gallery .mfp-image-holder .mfp-figure {
cursor: pointer; }
@media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px) {
/**
* Remove all paddings around the image on small screen
*/
.mfp-img-mobile .mfp-image-holder {
padding-left: 0;
padding-right: 0; }
.mfp-img-mobile img.mfp-img {
padding: 0; }
.mfp-img-mobile .mfp-figure:after {
top: 0;
bottom: 0; }
.mfp-img-mobile .mfp-figure small {
display: inline;
margin-left: 5px; }
.mfp-img-mobile .mfp-bottom-bar {
background: rgba(0, 0, 0, 0.6);
bottom: 0;
margin: 0;
top: auto;
padding: 3px 5px;
position: fixed;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box; }
.mfp-img-mobile .mfp-bottom-bar:empty {
padding: 0; }
.mfp-img-mobile .mfp-counter {
right: 5px;
top: 3px; }
.mfp-img-mobile .mfp-close {
top: 0;
right: 0;
width: 35px;
height: 35px;
line-height: 35px;
background: rgba(0, 0, 0, 0.6);
position: fixed;
text-align: center;
padding: 0; } }
@media all and (max-width: 900px) {
.mfp-arrow {
-webkit-transform: scale(0.75);
transform: scale(0.75); }
.mfp-arrow-left {
-webkit-transform-origin: 0;
transform-origin: 0; }
.mfp-arrow-right {
-webkit-transform-origin: 100%;
transform-origin: 100%; }
.mfp-container {
padding-left: 6px;
padding-right: 6px; } }
.mfp-ie7 .mfp-img {
padding: 0; }
.mfp-ie7 .mfp-bottom-bar {
width: 600px;
left: 50%;
margin-left: -300px;
margin-top: 5px;
padding-bottom: 5px; }
.mfp-ie7 .mfp-container {
padding: 0; }
.mfp-ie7 .mfp-content {
padding-top: 44px; }
.mfp-ie7 .mfp-close {
top: 0;
right: 0;
padding-top: 0; }

25
public/ng/css/tipsy.css Normal file
View File

@@ -0,0 +1,25 @@
.tipsy { font-size: 10px; position: absolute; padding: 5px; z-index: 100000; }
.tipsy-inner { background-color: #000; color: #FFF; max-width: 200px; padding: 5px 8px 4px 8px; text-align: center; }
/* Rounded corners */
.tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; }
/* Uncomment for shadow */
/*.tipsy-inner { box-shadow: 0 0 5px #000000; -webkit-box-shadow: 0 0 5px #000000; -moz-box-shadow: 0 0 5px #000000; }*/
.tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #000; }
/* Rules to colour arrows */
.tipsy-arrow-n { border-bottom-color: #000; }
.tipsy-arrow-s { border-top-color: #000; }
.tipsy-arrow-e { border-left-color: #000; }
.tipsy-arrow-w { border-right-color: #000; }
.tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
.tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
.tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; }
.tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; }

View File

@@ -4,7 +4,7 @@
}
html {
font-size: 13px;
font-family: Helvetica, "Microsoft Yahei", Menlo, Monaco, Consolas, "Courier New", monospace;
font-family: Helvetica, "Microsoft Yahei", Arial, sans-serif;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
line-height: 24px;
@@ -16,7 +16,7 @@ textarea,
select,
option,
button {
font-family: Helvetica, "Microsoft Yahei", Menlo, Monaco, Consolas, "Courier New", monospace;
font-family: Helvetica, "Microsoft Yahei", Arial, sans-serif;
}
article,
aside,
@@ -151,7 +151,7 @@ code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-family: monospace;
font-size: 1em;
}
.text-left {
@@ -277,9 +277,6 @@ hr {
border-bottom: 1px solid #dddddd;
margin-bottom: .75em;
}
p code {
color: #b63b2c;
}
.radius {
border-radius: .25em;
}
@@ -369,6 +366,9 @@ dt {
.grid-4-5 {
width: 80%;
}
.btn {
white-space: nowrap;
}
.btn-small {
font-size: 10.8px;
padding: .4em .9em;
@@ -379,6 +379,7 @@ dt {
}
.btn-large {
font-size: 14.4px;
padding: .4em .9em;
}
.btn-green {
background-color: #65ad4e;
@@ -460,6 +461,9 @@ dt {
box-sizing: content-box;
text-align: center;
}
.btn-comb {
margin-left: -1px;
}
.btn-disabled {
opacity: .6;
cursor: not-allowed;
@@ -483,6 +487,10 @@ dt {
.ipt-large {
font-size: 14.4px;
}
.ipt-textarea {
height: auto !important;
width: auto;
}
.ipt-disabled,
input[disabled] {
background-color: #f2f2f2 !important;
@@ -712,6 +720,14 @@ ul.menu-radius > li:last-child > a {
border-bottom-left-radius: .3em;
border-bottom-right-radius: .3em;
}
.panel.panel-info {
border-color: #85c5e5;
}
.panel.panel-info > .panel-header {
color: #31708f;
background-color: #d9edf7;
border-color: #85c5e5;
}
.panel.panel-warning {
border-color: #F0C36D;
}
@@ -720,7 +736,7 @@ ul.menu-radius > li:last-child > a {
border-color: #F0C36D;
}
.label {
padding: 2px 6px;
padding: .3em .6em .2em .6em;
color: #ffffff;
}
.label-red {
@@ -735,6 +751,10 @@ ul.menu-radius > li:last-child > a {
.label-green {
background-color: #65ad4e;
}
.label-green:hover {
background-color: #71bf57;
color: #FFF;
}
.label-orange {
background-color: #df7514;
}
@@ -795,6 +815,17 @@ ul.menu-radius > li:last-child > a {
border: 1px solid #b05c10;
background-color: #fcecdd;
}
.white-popup-block {
background: #FFF;
padding: 20px 30px;
text-align: left;
max-width: 650px;
margin: 40px auto;
position: relative;
}
.white-popup-block p {
font-size: 14px;
}
table th,
table td {
padding: .3em .6em;
@@ -851,3 +882,30 @@ table td {
.table-radius tbody tr:last-child > td:last-child {
border-bottom-right-radius: .3em;
}
.pager .page {
padding: .3em .5em .2em .5em;
margin: 0 .3em;
background-color: #cccccc;
color: #444444;
}
.pager .page:hover,
.pager .page.hover {
background-color: #428bca;
color: #ffffff;
}
.pager .prev,
.pager .next {
padding: .4em .6em;
margin: 0 .3em;
background-color: #505050;
color: #fafafa;
}
.pager .prev.invalid,
.pager .next.invalid {
background-color: #eeeeee;
color: #aaaaaa;
cursor: not-allowed;
}
.pager .page-radius {
border-radius: .2em;
}

View File

@@ -14,22 +14,20 @@
.mega-octicon is optimized for 32px but can be used larger.
*/
.octicon {
font: normal normal 16px octicons;
line-height: 1;
display: inline-block;
text-decoration: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.mega-octicon {
font: normal normal 32px octicons;
line-height: 1;
.octicon, .mega-octicon {
font: normal normal normal 16px/1 octicons;
display: inline-block;
text-decoration: none;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.mega-octicon { font-size: 32px; }
.octicon-alert:before { content: '\f02d'} /*  */
.octicon-alignment-align:before { content: '\f08a'} /*  */

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,9 @@
// @codekit-prepend "lib/jquery-1.11.1.min.js"
// @codekit-prepend "lib/lib.js"
// @codekit-prepend "lib/tabs.js"
// @codekit-prepend "utils/tabs.js"
// @codekit-prepend "utils/preview.js"
// @codekit-prepend "gogs/issue_label.js"
// @codekit-prepend "lib/jquery.tipsy.js"
var Gogs = {};
@@ -201,14 +204,21 @@ var Gogs = {};
// Search users by keyword.
Gogs.searchUsers = function (val, $target) {
var notEmpty = function (str) {
return str && str.length > 0;
}
$.ajax({
url: '/api/v1/users/search?q=' + val,
url: Gogs.AppSubUrl + '/api/v1/users/search?q=' + val,
dataType: "json",
success: function (json) {
if (json.ok && json.data.length) {
var html = '';
$.each(json.data, function (i, item) {
html += '<li><a><img src="' + item.avatar + '">' + item.username + '</a></li>';
html += '<li><a><img src="' + item.avatar_url + '">' + item.username;
if (notEmpty(item.full_name)) {
html += ' (' + item.full_name + ')';
}
html += '</a></li>';
});
$target.html(html);
$target.toggleShow();
@@ -222,13 +232,13 @@ var Gogs = {};
// Search repositories by keyword.
Gogs.searchRepos = function (val, $target, $param) {
$.ajax({
url: '/api/v1/repos/search?q=' + val + '&' + $param,
url: Gogs.AppSubUrl + '/api/v1/repos/search?q=' + val + '&' + $param,
dataType: "json",
success: function (json) {
if (json.ok && json.data.length) {
var html = '';
$.each(json.data, function (i, item) {
html += '<li><a><span class="octicon octicon-repo"></span> ' + item.repolink + '</a></li>';
html += '<li><a><span class="octicon octicon-repo"></span> ' + item.full_name + '</a></li>';
});
$target.html(html);
$target.toggleShow();
@@ -245,7 +255,7 @@ var Gogs = {};
return;
}
$(selector).zclip({
path: "/js/ZeroClipboard.swf",
path: Gogs.AppSubUrl + "/js/ZeroClipboard.swf",
copy: function () {
var t = $(this).data("copy-val");
var to = $($(this).data("copy-from"));
@@ -262,17 +272,14 @@ var Gogs = {};
return str;
},
afterCopy: function () {
alert("Clone URL has copied!");
// var $this = $(this);
// $this.tooltip('hide')
// .attr('data-original-title', 'Copied OK');
// setTimeout(function () {
// $this.tooltip("show");
// }, 200);
// setTimeout(function () {
// $this.tooltip('hide')
// .attr('data-original-title', 'Copy to Clipboard');
// }, 3000);
var $this = $(this);
$this.tipsy("hide").attr('original-title', $this.data('after-title'));
setTimeout(function () {
$this.tipsy("show");
}, 200);
setTimeout(function () {
$this.tipsy('hide').attr('original-title', $this.data('original-title'));
}, 2000);
}
}).addClass("js-copy-bind");
}
@@ -281,29 +288,73 @@ var Gogs = {};
function initCore() {
Gogs.renderMarkdown();
Gogs.renderCodeView();
// Switch list.
$('.js-tab-nav').click(function (e) {
if (!$(this).hasClass('js-tab-nav-show')) {
$(this).parent().find('.js-tab-nav-show').each(function () {
$(this).removeClass('js-tab-nav-show');
$($(this).data('tab-target')).hide();
});
$(this).addClass('js-tab-nav-show');
$($(this).data('tab-target')).show();
}
e.preventDefault();
});
// Popup.
$(document).on('click', '.popup-modal-dismiss', function (e) {
e.preventDefault();
$.magnificPopup.close();
});
// Plugins.
$('.collapse').hide();
$('.tipsy-tooltip').tipsy({
fade: true
});
}
function initUserSetting() {
// Confirmation of change username in user profile page.
$('#user-profile-form').submit(function (e) {
var $username = $('#username');
if (($username.data('uname') != $username.val()) && !confirm('Username has been changed, do you want to continue?')) {
var $username = $('#username');
var $profile_form = $('#user-profile-form');
$('#change-username-btn').magnificPopup({
modal: true,
callbacks: {
open: function () {
if (($username.data('uname') == $username.val())) {
$.magnificPopup.close();
$profile_form.submit();
}
}
}
}).click(function () {
if (($username.data('uname') != $username.val())) {
e.preventDefault();
return true;
}
});
$('#change-username-submit').click(function () {
$.magnificPopup.close();
$profile_form.submit();
});
// Show add SSH key panel.
$('#ssh-add').click(function () {
$('#user-ssh-add-form').removeClass("hide");
// Show panels.
$('.show-form-btn').click(function () {
$($(this).data('target-form')).removeClass("hide");
});
// Confirmation of delete account.
$('#delete-account-button').click(function (e) {
if (!confirm('This account is going to be deleted, do you want to continue?')) {
e.preventDefault();
return true;
}
$('#delete-account-btn').magnificPopup({
modal: true
}).click(function (e) {
e.preventDefault();
return true;
});
$('#delete-account-submit').click(function () {
$.magnificPopup.close();
$('#delete-account-form').submit();
});
}
@@ -343,48 +394,101 @@ function initRepo() {
$('#repo-clone-url').val($(this).data('link'));
$('.clone-url').text($(this).data('link'))
});
// Copy URL.
$('#repo-clone-copy').hover(function () {
var $clone_btn = $('#repo-clone-copy');
$clone_btn.hover(function () {
Gogs.bindCopy($(this));
})
$clone_btn.tipsy({
fade: true
});
// Markdown preview.
$('.markdown-preview').click(function() {
var $this = $(this);
$this.toggleAjax(function (resp) {
$($this.data("preview")).html(resp);
}, function () {
$($this.data("preview")).html("no content");
})
});
}
// when user changes hook type, hide/show proper divs
function initHookTypeChange() {
// web hook type change
$('select#hook-type').on("change", function () {
hookTypes = ['Gogs','Slack'];
hookTypes = ['Gogs', 'Slack'];
var curHook = $(this).val();
hookTypes.forEach(function(hookType) {
if (curHook === hookType) {
$('div#'+hookType.toLowerCase()).toggleShow();
}
else {
$('div#'+hookType.toLowerCase()).toggleHide();
}
});
var curHook = $(this).val();
hookTypes.forEach(function (hookType) {
if (curHook === hookType) {
$('div#' + hookType.toLowerCase()).toggleShow();
}
else {
$('div#' + hookType.toLowerCase()).toggleHide();
}
});
});
}
function initRepoRelease() {
$('#release-new-target-branch-list li').click(function() {
if (!$(this).hasClass('checked')) {
$('#repo-branch-current').text($(this).text());
$('#tag-target').val($(this).text());
$(this).parent().find('.checked').removeClass('checked');
$(this).addClass('checked');
}
})
}
function initRepoSetting() {
// Options.
// Confirmation of changing repository name.
$('#repo-setting-form').submit(function (e) {
var $reponame = $('#repo_name');
if (($reponame.data('repo-name') != $reponame.val()) && !confirm('Repository name has been changed, do you want to continue?')) {
var $reponame = $('#repo_name');
var $setting_form = $('#repo-setting-form');
$('#change-reponame-btn').magnificPopup({
modal: true,
callbacks: {
open: function () {
if (($reponame.data('repo-name') == $reponame.val())) {
$.magnificPopup.close();
$setting_form.submit();
}
}
}
}).click(function () {
if (($reponame.data('repo-name') != $reponame.val())) {
e.preventDefault();
return true;
}
});
$('#change-reponame-submit').click(function () {
$.magnificPopup.close();
$setting_form.submit();
});
initHookTypeChange();
$('#transfer-button').click(function () {
$('#transfer-form').show();
// Transfer repository.
$('#transfer-repo-btn').magnificPopup({
modal: true
});
$('#delete-button').click(function () {
$('#delete-form').show();
$('#transfer-repo-submit').click(function () {
$.magnificPopup.close();
$('#transfer-repo-form').submit();
});
// Delete repository.
$('#delete-repo-btn').magnificPopup({
modal: true
});
$('#delete-repo-submit').click(function () {
$.magnificPopup.close();
$('#delete-repo-form').submit();
});
// Collaboration.
@@ -412,19 +516,39 @@ function initRepoSetting() {
function initOrgSetting() {
// Options.
// Confirmation of changing organization name.
$('#org-setting-form').submit(function (e) {
var $orgname = $('#orgname');
if (($orgname.data('orgname') != $orgname.val()) && !confirm('Organization name has been changed, do you want to continue?')) {
var $orgname = $('#orgname');
var $setting_form = $('#org-setting-form');
$('#change-orgname-btn').magnificPopup({
modal: true,
callbacks: {
open: function () {
if (($orgname.data('orgname') == $orgname.val())) {
$.magnificPopup.close();
$setting_form.submit();
}
}
}
}).click(function () {
if (($orgname.data('orgname') != $orgname.val())) {
e.preventDefault();
return true;
}
});
$('#change-orgname-submit').click(function () {
$.magnificPopup.close();
$setting_form.submit();
});
// Confirmation of delete organization.
$('#delete-org-button').click(function (e) {
if (!confirm('This organization is going to be deleted, do you want to continue?')) {
e.preventDefault();
return true;
}
$('#delete-org-btn').magnificPopup({
modal: true
}).click(function (e) {
e.preventDefault();
return true;
});
$('#delete-org-submit').click(function () {
$.magnificPopup.close();
$('#delete-org-form').submit();
});
initHookTypeChange();
@@ -454,11 +578,14 @@ function initInvite() {
function initOrgTeamCreate() {
// Delete team.
$('#org-team-delete').click(function (e) {
if (!confirm('This team is going to be deleted, do you want to continue?')) {
e.preventDefault();
return true;
}
$('#org-team-delete').magnificPopup({
modal: true
}).click(function (e) {
e.preventDefault();
return true;
});
$('#delete-team-submit').click(function () {
$.magnificPopup.close();
var $form = $('#team-create-form');
$form.attr('action', $form.data('delete-url'));
});
@@ -522,15 +649,20 @@ function initAdmin() {
$('.auth-name').toggleShow();
}
});
// Delete account.
$('#user-delete').click(function (e) {
if (!confirm('This account is going to be deleted, do you want to continue?')) {
e.preventDefault();
return true;
}
$('#delete-account-btn').magnificPopup({
modal: true
}).click(function (e) {
e.preventDefault();
return true;
});
$('#delete-account-submit').click(function () {
$.magnificPopup.close();
var $form = $('#user-profile-form');
$form.attr('action', $form.data('delete-url'));
});
// Create authorization.
$('#auth-type').on("change", function () {
var v = $(this).val();
@@ -543,13 +675,17 @@ function initAdmin() {
$('.ldap').toggleHide();
}
});
// Delete authorization.
$('#auth-delete').click(function (e) {
if (!confirm('This authorization is going to be deleted, do you want to continue?')) {
e.preventDefault();
return true;
}
var $form = $('auth-setting-form');
$('#delete-auth-btn').magnificPopup({
modal: true
}).click(function (e) {
e.preventDefault();
return true;
});
$('#delete-auth-submit').click(function () {
$.magnificPopup.close();
var $form = $('#auth-setting-form');
$form.attr('action', $form.data('delete-url'));
});
}
@@ -591,7 +727,46 @@ function initInstall() {
}());
}
function initProfile() {
// Avatar.
$('#profile-avatar').tipsy({
fade: true
});
}
function initTimeSwitch() {
// Time switch.
$(".time-since[title]").on("click", function () {
var $this = $(this);
var title = $this.attr("title");
var text = $this.text();
$this.text(title);
$this.attr("title", text);
});
}
function initDiff() {
$('.diff-detail-box>a').click(function () {
$($(this).data('target')).slideToggle(100);
})
var $counter = $('.diff-counter');
if ($counter.length < 1) {
return;
}
$counter.each(function (i, item) {
var $item = $(item);
var addLine = $item.find('span[data-line].add').data("line");
var delLine = $item.find('span[data-line].del').data("line");
var addPercent = parseFloat(addLine) / (parseFloat(addLine) + parseFloat(delLine)) * 100;
$item.find(".bar .add").css("width", addPercent + "%");
});
}
$(document).ready(function () {
Gogs.AppSubUrl = $('head').data('suburl') || '';
initCore();
if ($('#user-profile-setting').length) {
initUserSetting();
@@ -600,8 +775,12 @@ $(document).ready(function () {
initRepoCreate();
}
if ($('#repo-header').length) {
initTimeSwitch();
initRepo();
}
if ($('#release').length) {
initRepoRelease();
}
if ($('#repo-setting').length) {
initRepoSetting();
}
@@ -626,8 +805,16 @@ $(document).ready(function () {
if ($('#install-form').length) {
initInstall();
}
if ($('#user-profile-page').length) {
initProfile();
}
if ($('#diff-page').length) {
initTimeSwitch();
initDiff();
}
Tabs('#dashboard-sidebar-menu');
$('#dashboard-sidebar-menu').tabs();
$('#pull-issue-preview').markdown_preview(".issue-add-comment");
homepage();
@@ -644,7 +831,7 @@ function homepage() {
$('#promo-form').submit(function (e) {
if ($('#username').val() === "") {
e.preventDefault();
window.location.href = '/user/login';
window.location.href = Gogs.AppSubUrl + '/user/login';
return true
}
});
@@ -652,9 +839,13 @@ function homepage() {
$('#register-button').click(function (e) {
if ($('#username').val() === "") {
e.preventDefault();
window.location.href = '/user/sign_up';
window.location.href = Gogs.AppSubUrl + '/user/sign_up';
return true
}
$('#promo-form').attr('action', '/user/sign_up');
$('#promo-form').attr('action', Gogs.AppSubUrl + '/user/sign_up');
});
}
String.prototype.endsWith = function (suffix) {
return this.indexOf(suffix, this.length - suffix.length) !== -1;
};

View File

@@ -0,0 +1,43 @@
// when dom ready, init issue label events
$(document).ready(function(){
var labelColors = ["#e11d21","#EB6420","#FBCA04","#009800",
"#006B75","#207DE5","#0052cc","#53E917",
"#F6C6C7","#FAD8C7","#FEF2C0","#BFE5BF",
"#BFDADC","#C7DEF8","#BFD4F2","#D4C5F9"];
var colorDropHtml = "";
labelColors.forEach(function(item){
colorDropHtml += '<a class="color" style="background-color:'+item+'" data-color-hex="'+item+'"></a>';
});
// render label color input
var color_input = $('#label-add-color');
var color_label = $('#label-color-drop label');
color_label.css("background-color",labelColors[0]);
color_input.val(labelColors[0]);
// render label color drop
$('#label-color-drop .drop-down')
.html(colorDropHtml)
.on("click","a",function(){
var color = $(this).data("color-hex");
color_label.css("background-color",color);
color_input.val(color);
});
// color drop visible
var form = $('#label-add-form');
$('#label-new-btn').on("click",function(){
if(form.hasClass("hidden")){
form.removeClass("hidden");
}
});
$('#label-cancel-btn').on("click",function(){
form.addClass("hidden");
})
});

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,258 @@
// tipsy, facebook style tooltips for jquery
// version 1.0.0a
// (c) 2008-2010 jason frame [jason@onehackoranother.com]
// released under the MIT license
(function($) {
function maybeCall(thing, ctx) {
return (typeof thing == 'function') ? (thing.call(ctx)) : thing;
};
function isElementInDOM(ele) {
while (ele = ele.parentNode) {
if (ele == document) return true;
}
return false;
};
function Tipsy(element, options) {
this.$element = $(element);
this.options = options;
this.enabled = true;
this.fixTitle();
};
Tipsy.prototype = {
show: function() {
var title = this.getTitle();
if (title && this.enabled) {
var $tip = this.tip();
$tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
$tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity
$tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
var pos = $.extend({}, this.$element.offset(), {
width: this.$element[0].offsetWidth,
height: this.$element[0].offsetHeight
});
var actualWidth = $tip[0].offsetWidth,
actualHeight = $tip[0].offsetHeight,
gravity = maybeCall(this.options.gravity, this.$element[0]);
var tp;
switch (gravity.charAt(0)) {
case 'n':
tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
break;
case 's':
tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
break;
case 'e':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
break;
case 'w':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
break;
}
if (gravity.length == 2) {
if (gravity.charAt(1) == 'w') {
tp.left = pos.left + pos.width / 2 - 15;
} else {
tp.left = pos.left + pos.width / 2 - actualWidth + 15;
}
}
$tip.css(tp).addClass('tipsy-' + gravity);
$tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0);
if (this.options.className) {
$tip.addClass(maybeCall(this.options.className, this.$element[0]));
}
if (this.options.fade) {
$tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
} else {
$tip.css({visibility: 'visible', opacity: this.options.opacity});
}
}
},
hide: function() {
if (this.options.fade) {
this.tip().stop().fadeOut(function() { $(this).remove(); });
} else {
this.tip().remove();
}
},
fixTitle: function() {
var $e = this.$element;
if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
$e.attr('original-title', $e.attr('title') || '').removeAttr('title');
}
},
getTitle: function() {
var title, $e = this.$element, o = this.options;
this.fixTitle();
var title, o = this.options;
if (typeof o.title == 'string') {
title = $e.attr(o.title == 'title' ? 'original-title' : o.title);
} else if (typeof o.title == 'function') {
title = o.title.call($e[0]);
}
title = ('' + title).replace(/(^\s*|\s*$)/, "");
return title || o.fallback;
},
tip: function() {
if (!this.$tip) {
this.$tip = $('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>');
this.$tip.data('tipsy-pointee', this.$element[0]);
}
return this.$tip;
},
validate: function() {
if (!this.$element[0].parentNode) {
this.hide();
this.$element = null;
this.options = null;
}
},
enable: function() { this.enabled = true; },
disable: function() { this.enabled = false; },
toggleEnabled: function() { this.enabled = !this.enabled; }
};
$.fn.tipsy = function(options) {
if (options === true) {
return this.data('tipsy');
} else if (typeof options == 'string') {
var tipsy = this.data('tipsy');
if (tipsy) tipsy[options]();
return this;
}
options = $.extend({}, $.fn.tipsy.defaults, options);
function get(ele) {
var tipsy = $.data(ele, 'tipsy');
if (!tipsy) {
tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
$.data(ele, 'tipsy', tipsy);
}
return tipsy;
}
function enter() {
var tipsy = get(this);
tipsy.hoverState = 'in';
if (options.delayIn == 0) {
tipsy.show();
} else {
tipsy.fixTitle();
setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn);
}
};
function leave() {
var tipsy = get(this);
tipsy.hoverState = 'out';
if (options.delayOut == 0) {
tipsy.hide();
} else {
setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut);
}
};
if (!options.live) this.each(function() { get(this); });
if (options.trigger != 'manual') {
var binder = options.live ? 'live' : 'bind',
eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus',
eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
this[binder](eventIn, enter)[binder](eventOut, leave);
}
return this;
};
$.fn.tipsy.defaults = {
className: null,
delayIn: 0,
delayOut: 0,
fade: false,
fallback: '',
gravity: 'n',
html: false,
live: false,
offset: 0,
opacity: 0.8,
title: 'title',
trigger: 'hover'
};
$.fn.tipsy.revalidate = function() {
$('.tipsy').each(function() {
var pointee = $.data(this, 'tipsy-pointee');
if (!pointee || !isElementInDOM(pointee)) {
$(this).remove();
}
});
};
// Overwrite this method to provide options on a per-element basis.
// For example, you could store the gravity in a 'tipsy-gravity' attribute:
// return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
// (remember - do not modify 'options' in place!)
$.fn.tipsy.elementOptions = function(ele, options) {
return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
};
$.fn.tipsy.autoNS = function() {
return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
};
$.fn.tipsy.autoWE = function() {
return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
};
/**
* yields a closure of the supplied parameters, producing a function that takes
* no arguments and is suitable for use as an autogravity function like so:
*
* @param margin (int) - distance from the viewable region edge that an
* element should be before setting its tooltip's gravity to be away
* from that edge.
* @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer
* if there are no viewable region edges effecting the tooltip's
* gravity. It will try to vary from this minimally, for example,
* if 'sw' is preferred and an element is near the right viewable
* region edge, but not the top edge, it will set the gravity for
* that element's tooltip to be 'se', preserving the southern
* component.
*/
$.fn.tipsy.autoBounds = function(margin, prefer) {
return function() {
var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)},
boundTop = $(document).scrollTop() + margin,
boundLeft = $(document).scrollLeft() + margin,
$this = $(this);
if ($this.offset().top < boundTop) dir.ns = 'n';
if ($this.offset().left < boundLeft) dir.ew = 'w';
if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e';
if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's';
return dir.ns + (dir.ew ? dir.ew : '');
}
};
})(jQuery);

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,53 @@
/**
* preview plugin
* @param selector
* @param target_selector
*/
function Preview(selector, target_selector) {
// get input element
function get_input($e) {
return $e.find(".js-preview-input").eq(0);
}
// get result html container element
function get_container($t) {
if ($t.hasClass("js-preview-container")) {
return $t
}
return $t.find(".js-preview-container").eq(0);
}
var $e = $(selector);
var $t = $(target_selector);
var $ipt = get_input($t);
if (!$ipt.length) {
console.log("[preview]: no preview input");
return
}
var $cnt = get_container($t);
if (!$cnt.length) {
console.log("[preview]: no preview container");
return
}
// call api via ajax
$e.on("click", function () {
$.post("/api/v1/markdown", {
text: $ipt.val()
}, function (html) {
$cnt.html(html);
})
});
console.log("[preview]: init preview @", selector, "&", target_selector);
}
$.fn.extend({
markdown_preview: function (target) {
Preview(this, target);
}
});

View File

@@ -1,7 +1,6 @@
/**
* Created by fuxiaohei on 14-6-26.
/*
js tabs and tabbed content plugin
*/
function Tabs(selector) {
function hide($nav) {
@@ -24,7 +23,8 @@ function Tabs(selector) {
$($current.data("tab-target")).addClass("js-tab-show");
}
// bind nav click
$e.on("click", ".js-tab-nav", function () {
$e.on("click", ".js-tab-nav", function (e) {
e.preventDefault();
var $this = $(this);
// is showing, not change.
if ($this.hasClass("js-tab-nav-show")) {
@@ -36,4 +36,10 @@ function Tabs(selector) {
});
console.log("init tabs @", selector)
}
}
}
$.fn.extend({
tabs: function () {
Tabs(this);
}
});

View File

@@ -7,4 +7,5 @@
@import "gogs/settings";
@import "gogs/issue";
@import "gogs/organization";
@import "gogs/admin";
@import "gogs/admin";
@import "gogs/profile";

View File

@@ -17,9 +17,6 @@ body {
width: 16px;
text-align: center;
}
.fa {
font-size: 14px;
}
.container {
max-width: 1170px;
padding: 0 1.5em;
@@ -30,6 +27,11 @@ img.avatar-16 {
height: 16px;
vertical-align: middle;
}
img.avatar-20 {
width: 20px;
height: 20px;
vertical-align: middle;
}
img.avatar-24 {
width: 24px;
height: 24px;
@@ -55,6 +57,11 @@ img.avatar-100{
height: 100px;
vertical-align: middle;
}
.drop-down{
.panel-header{
font-size: 14px;
}
}
#wrapper {
padding: 0;
margin: 0 0 -55px 0;
@@ -95,6 +102,7 @@ clear: both;
z-index: 100;
font-size: 12px;
width: 120%;
min-width: 100px;
li > a {
padding: 3px 9px;
}
@@ -102,7 +110,7 @@ clear: both;
}
#header {
background-color: @headerBgColor;
height: 44px;
height: 45px;
> .menu-line {
> li > a {
display: inline-block;
@@ -128,7 +136,7 @@ clear: both;
#header-nav-user {
height: 44px;
img {
margin: -4px 10px 0 0;
margin: -2px 10px 0 0;
border-radius: 3px;
}
}
@@ -136,7 +144,7 @@ clear: both;
color: @headerSignOutColor !important;
}
#header-nav-logo {
padding: 6px 1.2em;
padding: 6px 1.2em 6px 0;
}
#header-nav-explore,
#header-nav-help {
@@ -251,6 +259,9 @@ clear: both;
.text-black {
color: #444444;
}
.text-gold {
color: #a1882b;
}
.table {
width: 100%;
max-width: 100%;
@@ -283,4 +294,8 @@ clear: both;
li {
display: inline;
}
}
.list-unstyled {
padding-left: 0;
list-style: none;
}

Some files were not shown because too many files have changed in this diff Show More