diff --git a/.drone.yml b/.drone.yml index 011d4fefd..9a27e1c21 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,7 +4,7 @@ workspace: clone: git: - image: plugins/git:1 + image: plugins/git:next depth: 50 tags: true @@ -75,6 +75,7 @@ pipeline: - make lint - make fmt-check - make swagger-check + - make swagger-validate - make misspell-check - make test-vendor - make build @@ -254,6 +255,18 @@ pipeline: when: event: [ push, tag ] + gpg-sign: + image: plugins/gpgsign:1 + pull: true + secrets: [ gpgsign_key, gpgsign_passphrase ] + detach_sign: true + files: + - dist/release/* + excludes: + - dist/release/*.sha256 + when: + event: [ push, tag ] + release: image: plugins/s3:1 pull: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 373ce00e6..b31730a09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log; to see the highlights of what has been added to each release, please refer to the [blog](https://blog.gitea.io). +## [1.4.3](https://github.com/go-gitea/gitea/releases/tag/v1.4.3) - 2018-06-26 +* SECURITY + * HTML-escape plain-text READMEs (#4192) (#4214) + * Fix open redirect vulnerability on login screen (#4312) (#4312) +* BUGFIXES + * Fix broken monitoring page when running processes are shown (#4203) (#4208) + * Fix delete comment bug (#4216) (#4228) + * Delete reactions added to issues and comments when deleting repository (#4232) (#4237) + * Fix wiki URL encoding bug (#4091) (#4254) + * Fix code tab link when viewing tags (#3908) (#4263) + * Fix webhook type conflation (#4285) (#4285) + ## [1.4.2](https://github.com/go-gitea/gitea/releases/tag/v1.4.2) - 2018-06-04 * BUGFIXES * Adjust z-index for floating labels (#3939) (#3950) diff --git a/cmd/admin.go b/cmd/admin.go index 6c79141ea..4fb0810c6 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -73,6 +73,11 @@ var ( Value: "", Usage: "New password to set for user", }, + cli.StringFlag{ + Name: "config, c", + Value: "custom/conf/app.ini", + Usage: "Custom configuration file path", + }, }, } @@ -123,6 +128,10 @@ func runChangePassword(c *cli.Context) error { return err } + if c.IsSet("config") { + setting.CustomConf = c.String("config") + } + if err := initDB(); err != nil { return err } diff --git a/contrib/init/sunos/gitea.xml b/contrib/init/sunos/gitea.xml new file mode 100644 index 000000000..4b8cc3a38 --- /dev/null +++ b/contrib/init/sunos/gitea.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index ef88e5c32..f823f68e4 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -601,9 +601,9 @@ ko-KR = ko [U2F] ; Two Factor authentication with security keys ; https://developers.yubico.com/U2F/App_ID.html -APP_ID = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s +APP_ID = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/ ; Comma seperated list of truisted facets -TRUSTED_FACETS = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s +TRUSTED_FACETS = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/ ; Extension mapping to highlight class ; e.g. .toml=ini diff --git a/docker/etc/s6/gitea/setup b/docker/etc/s6/gitea/setup index 500cca584..03758ed81 100755 --- a/docker/etc/s6/gitea/setup +++ b/docker/etc/s6/gitea/setup @@ -1,7 +1,5 @@ #!/bin/bash -/usr/sbin/update-ca-certificates - if [ ! -d /data/git/.ssh ]; then mkdir -p /data/git/.ssh chmod 700 /data/git/.ssh diff --git a/docs/content/doc/advanced/api-usage.en-us.md b/docs/content/doc/advanced/api-usage.en-us.md new file mode 100644 index 000000000..f04a99f14 --- /dev/null +++ b/docs/content/doc/advanced/api-usage.en-us.md @@ -0,0 +1,75 @@ +--- +date: "2018-06-24:00:00+02:00" +title: "API Usage" +slug: "api-usage" +weight: 40 +toc: true +draft: false +menu: + sidebar: + parent: "advanced" + name: "API Usage" + weight: 40 + identifier: "api-usage" +--- + +# Gitea API Usage + +## Enabling/configuring API access + +By default, `ENABLE_SWAGGER_ENDPOINT` is true, and +`MAX_RESPONSE_ITEMS` is set to 50. See [Config Cheat +Sheet](https://docs.gitea.io/en-us/config-cheat-sheet/) for more +information. + +## Authentication via the API + +Gitea supports these methods of API authentication: + +- HTTP basic authentication +- `token=...` parameter in URL query string +- `access_token=...` parameter in URL query string +- `Authorization: token ...` header in HTTP headers + +All of these methods accept the same apiKey token type. You can +better understand this by looking at the code -- as of this writing, +Gitea parses queries and headers to find the token in +[modules/auth/auth.go](https://github.com/go-gitea/gitea/blob/6efdcaed86565c91a3dc77631372a9cc45a58e89/modules/auth/auth.go#L47). + +You can create an apiKey token via your gitea install's web interface: +`Settings | Applications | Generate New Token`. + +### More on the `Authorization:` header + +For historical reasons, Gitea needs the word `token` included before +the apiKey token in an authorization header, like this: + +``` +Authorization: token 65eaa9c8ef52460d22a93307fe0aee76289dc675 +``` + +In a `curl` command, for instance, this would look like: + +``` +curl -X POST "http://localhost:4000/api/v1/repos/test1/test1/issues" \ + -H "accept: application/json" \ + -H "Authorization: token 65eaa9c8ef52460d22a93307fe0aee76289dc675" \ + -H "Content-Type: application/json" -d "{ \"body\": \"testing\", \"title\": \"test 20\"}" -i +``` + +As mentioned above, the token used is the same one you would use in +the `token=` string in a GET request. + +## Listing your issued tokens via the API + +As mentioned in +[#3842](https://github.com/go-gitea/gitea/issues/3842#issuecomment-397743346), +`/users/:name/tokens` is special and requires you to authenticate +using BasicAuth, as follows: + +### Using basic authentication: + +``` +$ curl --request GET --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens +[{"name":"test","sha1":"..."},{"name":"dev","sha1":"..."}] +``` diff --git a/docs/content/doc/features/comparison.en-us.md b/docs/content/doc/features/comparison.en-us.md index 981365c71..698de4f46 100644 --- a/docs/content/doc/features/comparison.en-us.md +++ b/docs/content/doc/features/comparison.en-us.md @@ -27,659 +27,93 @@ _Symbols used in table:_ * _✘ - unsupported_ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FeatureGiteaGogsGitHub EEGitLab CEGitLab EEBitBucketRhodeCode CE
Open source and free
Issue tracker
Pull/Merge requests
Squash merging
Rebase merging
Pull/Merge request inline comments
Pull/Merge request approval
Merge conflict resolution
Restrict push and merge access to certain users
Markdown support
Issues and pull/merge requests templates
Revert specific commits or a merge request
Labels
Time tracking
Multiple assignees for issues
Related issues
Confidential issues
Comment reactions
Lock Discussion
Batch issue handling
Issue Boards
Create new branches from issues
Commit graph
Web code editor
Branch manager
Create new branches
Repository topics
Repository code search
Global code search
Issue search
Global issue search
Git LFS 2.0
Integrated Git-powered wiki
Static Git-powered pages
Group Milestones
Granular user roles (Code, Issues, Wiki etc)
Cherry-picking changes
GPG Signed Commits
Reject unsigned commits
Verified Committer?
Subgroups: groups within groups
Custom Git Hooks
Repository Activity page
Deploy Tokens
Repository Tokens with write rights
Easy upgrade process
Built-in Container Registry
External git mirroring
AD / LDAP integration
Multiple LDAP / AD server support
LDAP user synchronization
OpenId Connect support?
OAuth 2.0 integration (external authorization)?
Act as OAuth 2.0 provider
Two factor authentication (2FA)
FIDO U2F (2FA)
Webhook support
Mattermost/Slack integration
Discord integration
Built-in CI/CD
External CI/CD status display
Multiple database support
Multiple OS support
Low resource usage (RAM/CPU)
+#### General Features + +| Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE | +|---------|-------|------|-----------|-----------|-----------|-----------|--------------| +| Open source and free | ✓ | ✓ | ✘| ✓ | ✘ | ✘ | ✓ | +| Low resource usage (RAM/CPU) | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ | +| Multiple database support | ✓ | ✓ | ✘ | ⁄ | ⁄ | ✓ | ✓ | +| Multiple OS support | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ | +| Easy upgrade process | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ | +| Markdown support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Static Git-powered pages | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | +| Integrated Git-powered wiki | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | +| Deploy Tokens | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Repository Tokens with write rights | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✓ | +| Built-in Container Registry | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | +| External git mirroring | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ | +| FIDO U2F (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ | +| Built-in CI/CD | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | +| Subgroups: groups within groups | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✓ | + +#### Code management + +| Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE | +|---------|-------|------|-----------|-----------|-----------|-----------|--------------| +| Repository topics | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | +| Repository code search | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Global code search | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Git LFS 2.0 | ✓ | ✘ | ✓ | ✓ | ✓ | ⁄ | ✓ | +| Group Milestones | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | +| Granular user roles (Code, Issues, Wiki etc) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | +| Verified Committer | ✘ | ✘ | ? | ✓ | ✓ | ✓ | ✘ | +| GPG Signed Commits | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Reject unsigned commits | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✓ | +| Repository Activity page | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Branch manager | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Create new branches | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | +| Web code editor | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Commit graph | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | + +#### Issue Tracker + +| Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE | +|---------|-------|------|-----------|-----------|-----------|-----------|--------------| +| Issue tracker | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | +| Issue templates | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ | +| Labels | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ | +| Time tracking | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | +| Multiple assignees for issues | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | +| Related issues | ✘ | ✘ | ⁄ | ✘ | ✓ | ✘ | ✘ | +| Confidential issues | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | +| Comment reactions | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | +| Lock Discussion | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | +| Batch issue handling | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | +| Issue Boards | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | +| Create new branches from issues | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | +| Issue search | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ | +| Global issue search | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ | + +#### Pull/Merge requests + +| Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE | +|---------|-------|------|-----------|-----------|-----------|-----------|--------------| +| Pull/Merge requests | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Squash merging | ✓ | ✘ | ✓ | ✘ | ✓ | ✓ | ✓ | +| Rebase merging | ✓ | ✓ | ✓ | ✘ | ⁄ | ✘ | ✓ | +| Pull/Merge request inline comments | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Pull/Merge request approval | ✘ | ✘ | ⁄ | ✓ | ✓ | ✓ | ✓ | +| Merge conflict resolution | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ | +| Restrict push and merge access to certain users | ✓ | ✘ | ✓ | ⁄ | ✓ | ✓ | ✓ | +| Revert specific commits or a merge request | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ | +| Pull/Merge requests templates | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ | +| Cherry-picking changes | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | + + +#### 3rd-party integrations + +| Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE | +|---------|-------|------|-----------|-----------|-----------|-----------|--------------| +| Webhook support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Custom Git Hooks | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| AD / LDAP integration | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Multiple LDAP / AD server support | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ | +| LDAP user synchronization | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | +| OpenId Connect support | ✓ | ✘ | ✓ | ✓ | ✓ | ? | ✘ | +| OAuth 2.0 integration (external authorization) | ✓ | ✘ | ⁄ | ✓ | ✓ | ? | ✓ | +| Act as OAuth 2.0 provider | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ | +| Two factor authentication (2FA) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | +| Mattermost/Slack integration | ✓ | ✓ | ⁄ | ✓ | ✓ | ⁄ | ✓ | +| Discord integration | ✓ | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | +| External CI/CD status display | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | diff --git a/docs/content/doc/installation/with-docker.en-us.md b/docs/content/doc/installation/with-docker.en-us.md index c672393cc..aa4337e08 100644 --- a/docs/content/doc/installation/with-docker.en-us.md +++ b/docs/content/doc/installation/with-docker.en-us.md @@ -245,6 +245,8 @@ You can configure some of Gitea's settings via environment variables: * `SECRET_KEY`: **""**: Global secret key. This should be changed. If this has a value and `INSTALL_LOCK` is empty, `INSTALL_LOCK` will automatically set to `true`. * `DISABLE_REGISTRATION`: **false**: Disable registration, after which only admin can create accounts for users. * `REQUIRE_SIGNIN_VIEW`: **false**: Enable this to force users to log in to view any page. +* `USER_UID`: **1000**: The UID (Unix user ID) of the user that runs Gitea within the container. Match this to the UID of the owner of the `/data` volume if using host volumes (this is not necessary with named volumes). +* `USER_GID`: **1000**: The GID (Unix group ID) of the user that runs Gitea within the container. Match this to the GID of the owner of the `/data` volume if using host volumes (this is not necessary with named volumes). # Customization diff --git a/docs/content/doc/usage/backup-and-restore.en-us.md b/docs/content/doc/usage/backup-and-restore.en-us.md index fa73c38bc..a0816ebe4 100644 --- a/docs/content/doc/usage/backup-and-restore.en-us.md +++ b/docs/content/doc/usage/backup-and-restore.en-us.md @@ -20,7 +20,7 @@ file can be unpacked and used to restore an instance. ## Backup Command (`dump`) -Switch to the user running gitea: `su git`. Run `./gitea dump` in the gitea installation +Switch to the user running gitea: `su git`. Run `./gitea dump -c /path/to/app.ini` in the gitea installation directory. There should be some output similar to the following: ``` diff --git a/docs/content/doc/usage/command-line.md b/docs/content/doc/usage/command-line.md index 9c16d4904..a7a10b4e4 100644 --- a/docs/content/doc/usage/command-line.md +++ b/docs/content/doc/usage/command-line.md @@ -62,6 +62,7 @@ Admin operations: - Options: - `--username value`, `-u value`: Username. Required. - `--password value`, `-p value`: New password. Required. + - `--config path`: Gitea configuration file path. Optional. (default: custom/conf/app.ini). - Examples: - `gitea admin change-password --username myname --password asecurepassword` - `regenerate` diff --git a/integrations/README.md b/integrations/README.md index 64041b546..927368818 100644 --- a/integrations/README.md +++ b/integrations/README.md @@ -13,7 +13,7 @@ Make sure to perform a clean build before running tests: ## Run all tests via local drone ``` -drone exec --local --build.event "pull_request" +drone exec --local --build-event "pull_request" ``` ## Run sqlite integrations tests diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index b766dd584..12429c88a 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -67,9 +67,9 @@ func TestAPISearchRepo(t *testing.T) { expectedResults }{ {name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50", expectedResults: expectedResults{ - nil: {count: 15}, - user: {count: 15}, - user2: {count: 15}}, + nil: {count: 16}, + user: {count: 16}, + user2: {count: 16}}, }, {name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10", expectedResults: expectedResults{ nil: {count: 10}, diff --git a/integrations/setting_test.go b/integrations/setting_test.go index a8d1f01e8..2aac8e90e 100644 --- a/integrations/setting_test.go +++ b/integrations/setting_test.go @@ -68,3 +68,25 @@ func TestSettingShowUserEmailProfile(t *testing.T) { setting.UI.ShowUserEmail = showUserEmail } + +func TestSettingLandingPage(t *testing.T) { + prepareTestEnv(t) + + landingPage := setting.LandingPageURL + + setting.LandingPageURL = setting.LandingPageHome + req := NewRequest(t, "GET", "/") + MakeRequest(t, req, http.StatusOK) + + setting.LandingPageURL = setting.LandingPageExplore + req = NewRequest(t, "GET", "/") + resp := MakeRequest(t, req, http.StatusFound) + assert.Equal(t, "/explore", resp.Header().Get("Location")) + + setting.LandingPageURL = setting.LandingPageOrganizations + req = NewRequest(t, "GET", "/") + resp = MakeRequest(t, req, http.StatusFound) + assert.Equal(t, "/explore/organizations", resp.Header().Get("Location")) + + setting.LandingPageURL = landingPage +} diff --git a/models/access_test.go b/models/access_test.go index 59575acb7..46d6f723e 100644 --- a/models/access_test.go +++ b/models/access_test.go @@ -22,8 +22,12 @@ func TestAccessLevel(t *testing.T) { user1 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) user2 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) - repo1 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 2, IsPrivate: false}).(*Repository) - repo2 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 3, IsPrivate: true}).(*Repository) + // A public repository owned by User 2 + repo1 := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) + assert.False(t, repo1.IsPrivate) + // A private repository owned by Org 3 + repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) + assert.True(t, repo2.IsPrivate) level, err := AccessLevel(user1.ID, repo1) assert.NoError(t, err) @@ -47,8 +51,12 @@ func TestHasAccess(t *testing.T) { user1 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) user2 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) - repo1 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 2, IsPrivate: false}).(*Repository) - repo2 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 3, IsPrivate: true}).(*Repository) + // A public repository owned by User 2 + repo1 := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) + assert.False(t, repo1.IsPrivate) + // A private repository owned by Org 3 + repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) + assert.True(t, repo2.IsPrivate) for _, accessMode := range accessModes { has, err := HasAccess(user1.ID, repo1, accessMode) diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml index c7d73fe17..3238b65ea 100644 --- a/models/fixtures/repository.yml +++ b/models/fixtures/repository.yml @@ -351,7 +351,7 @@ is_mirror: true num_forks: 1 is_fork: false - + - id: 29 fork_id: 27 @@ -365,7 +365,7 @@ num_closed_pulls: 0 is_mirror: false is_fork: true - + - id: 30 fork_id: 28 @@ -389,3 +389,14 @@ num_forks: 0 num_issues: 0 is_mirror: false + +- + id: 32 + owner_id: 3 + lower_name: repo21 + name: repo21 + is_private: false + num_stars: 0 + num_forks: 0 + num_issues: 0 + is_mirror: false diff --git a/models/fixtures/team.yml b/models/fixtures/team.yml index 1d242cb5b..4b4a1d798 100644 --- a/models/fixtures/team.yml +++ b/models/fixtures/team.yml @@ -4,9 +4,8 @@ lower_name: owners name: Owners authorize: 4 # owner - num_repos: 2 + num_repos: 3 num_members: 1 - unit_types: '[1,2,3,4,5,6,7]' - id: 2 @@ -16,7 +15,6 @@ authorize: 2 # write num_repos: 1 num_members: 2 - unit_types: '[1,2,3,4,5,6,7]' - id: 3 @@ -26,7 +24,6 @@ authorize: 4 # owner num_repos: 0 num_members: 1 - unit_types: '[1,2,3,4,5,6,7]' - id: 4 @@ -36,7 +33,6 @@ authorize: 4 # owner num_repos: 0 num_members: 1 - unit_types: '[1,2,3,4,5,6,7]' - id: 5 @@ -46,7 +42,6 @@ authorize: 4 # owner num_repos: 2 num_members: 2 - unit_types: '[1,2,3,4,5,6,7]' - id: 6 @@ -56,4 +51,3 @@ authorize: 4 # owner num_repos: 2 num_members: 1 - unit_types: '[1,2,3,4,5,6,7]' \ No newline at end of file diff --git a/models/fixtures/team_repo.yml b/models/fixtures/team_repo.yml index 9e6d74553..b324e0941 100644 --- a/models/fixtures/team_repo.yml +++ b/models/fixtures/team_repo.yml @@ -33,9 +33,15 @@ org_id: 19 team_id: 6 repo_id: 27 - + - id: 7 org_id: 19 team_id: 6 - repo_id: 28 \ No newline at end of file + repo_id: 28 + +- + id: 8 + org_id: 3 + team_id: 1 + repo_id: 32 diff --git a/models/fixtures/team_unit.yml b/models/fixtures/team_unit.yml new file mode 100644 index 000000000..ad5466a5c --- /dev/null +++ b/models/fixtures/team_unit.yml @@ -0,0 +1,209 @@ +- + id: 1 + team_id: 1 + type: 1 + +- + id: 2 + team_id: 1 + type: 2 + +- + id: 3 + team_id: 1 + type: 3 + +- + id: 4 + team_id: 1 + type: 4 + +- + id: 5 + team_id: 1 + type: 5 + +- + id: 6 + team_id: 1 + type: 6 + +- + id: 7 + team_id: 1 + type: 7 + +- + id: 8 + team_id: 2 + type: 1 + +- + id: 9 + team_id: 2 + type: 2 + +- + id: 10 + team_id: 2 + type: 3 + +- + id: 11 + team_id: 2 + type: 4 + +- + id: 12 + team_id: 2 + type: 5 + +- + id: 13 + team_id: 2 + type: 6 + +- + id: 14 + team_id: 2 + type: 7 + +- + id: 15 + team_id: 3 + type: 1 + +- + id: 16 + team_id: 3 + type: 2 + +- + id: 17 + team_id: 3 + type: 3 + +- + id: 18 + team_id: 3 + type: 4 + +- + id: 19 + team_id: 3 + type: 5 + +- + id: 20 + team_id: 3 + type: 6 + +- + id: 21 + team_id: 3 + type: 7 + +- + id: 22 + team_id: 4 + type: 1 + +- + id: 23 + team_id: 4 + type: 2 + +- + id: 24 + team_id: 4 + type: 3 + +- + id: 25 + team_id: 4 + type: 4 + +- + id: 26 + team_id: 4 + type: 5 + +- + id: 27 + team_id: 4 + type: 6 + +- + id: 28 + team_id: 4 + type: 7 + +- + id: 29 + team_id: 5 + type: 1 + +- + id: 30 + team_id: 5 + type: 2 + +- + id: 31 + team_id: 5 + type: 3 + +- + id: 32 + team_id: 5 + type: 4 + +- + id: 33 + team_id: 5 + type: 5 + +- + id: 34 + team_id: 5 + type: 6 + +- + id: 35 + team_id: 5 + type: 7 + +- + id: 36 + team_id: 6 + type: 1 + +- + id: 37 + team_id: 6 + type: 2 + +- + id: 38 + team_id: 6 + type: 3 + +- + id: 39 + team_id: 6 + type: 4 + +- + id: 40 + team_id: 6 + type: 5 + +- + id: 41 + team_id: 6 + type: 6 + +- + id: 42 + team_id: 6 + type: 7 diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 7ad48f7fb..a2e3b88d7 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -45,7 +45,7 @@ is_admin: false avatar: avatar3 avatar_email: user3@example.com - num_repos: 2 + num_repos: 3 num_members: 2 num_teams: 2 diff --git a/models/issue_comment.go b/models/issue_comment.go index a829c8066..1c7c57dd0 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -147,6 +147,10 @@ func (c *Comment) AfterLoad(session *xorm.Session) { // AfterDelete is invoked from XORM after the object is deleted. func (c *Comment) AfterDelete() { + if c.ID <= 0 { + return + } + _, err := DeleteAttachmentsByComment(c.ID, true) if err != nil { diff --git a/models/issue_watch.go b/models/issue_watch.go index 69e218af0..3e7d24821 100644 --- a/models/issue_watch.go +++ b/models/issue_watch.go @@ -71,3 +71,15 @@ func getIssueWatchers(e Engine, issueID int64) (watches []*IssueWatch, err error Find(&watches) return } + +func removeIssueWatchersByRepoID(e Engine, userID int64, repoID int64) error { + iw := &IssueWatch{ + IsWatching: false, + } + _, err := e. + Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID). + Cols("is_watching", "updated_unix"). + Where("`issue_watch`.user_id = ?", userID). + Update(iw) + return err +} diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 1300065ab..cc262d810 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -186,6 +186,12 @@ var migrations = []Migration{ NewMigration("add u2f", addU2FReg), // v66 -> v67 NewMigration("add login source id column for public_key table", addLoginSourceIDToPublicKeyTable), + // v67 -> v68 + NewMigration("remove stale watches", removeStaleWatches), + // v68 -> V69 + NewMigration("Reformat and remove incorrect topics", reformatAndRemoveIncorrectTopics), + // v69 -> v70 + NewMigration("move team units to team_unit table", moveTeamUnitsToTeamUnitTable), } // Migrate database to current version diff --git a/models/migrations/v38.go b/models/migrations/v38.go index 6f66484b0..eb90f9fbf 100644 --- a/models/migrations/v38.go +++ b/models/migrations/v38.go @@ -25,10 +25,15 @@ func removeCommitsUnitType(x *xorm.Engine) (err error) { Created time.Time `xorm:"-"` } + type Team struct { + ID int64 + UnitTypes []int `xorm:"json"` + } + // Update team unit types const batchSize = 100 for start := 0; ; start += batchSize { - teams := make([]*models.Team, 0, batchSize) + teams := make([]*Team, 0, batchSize) if err := x.Limit(batchSize, start).Find(&teams); err != nil { return err } @@ -36,7 +41,7 @@ func removeCommitsUnitType(x *xorm.Engine) (err error) { break } for _, team := range teams { - ut := make([]models.UnitType, 0, len(team.UnitTypes)) + ut := make([]int, 0, len(team.UnitTypes)) for _, u := range team.UnitTypes { if u < V16UnitTypeCommits { ut = append(ut, u) diff --git a/models/migrations/v67.go b/models/migrations/v67.go new file mode 100644 index 000000000..278221919 --- /dev/null +++ b/models/migrations/v67.go @@ -0,0 +1,158 @@ +// Copyright 2018 The Gitea 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 migrations + +import ( + "code.gitea.io/gitea/modules/setting" + + "github.com/go-xorm/xorm" +) + +func removeStaleWatches(x *xorm.Engine) error { + type Watch struct { + ID int64 + UserID int64 + RepoID int64 + } + + type IssueWatch struct { + ID int64 + UserID int64 + RepoID int64 + IsWatching bool + } + + type Repository struct { + ID int64 + IsPrivate bool + OwnerID int64 + } + + type Access struct { + UserID int64 + RepoID int64 + Mode int + } + + const ( + // AccessModeNone no access + AccessModeNone int = iota // 0 + // AccessModeRead read access + AccessModeRead // 1 + ) + + accessLevel := func(userID int64, repo *Repository) (int, error) { + mode := AccessModeNone + if !repo.IsPrivate { + mode = AccessModeRead + } + + if userID == 0 { + return mode, nil + } + + if userID == repo.OwnerID { + return 4, nil + } + + a := &Access{UserID: userID, RepoID: repo.ID} + if has, err := x.Get(a); !has || err != nil { + return mode, err + } + return a.Mode, nil + } + + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + repoCache := make(map[int64]*Repository) + err := x.BufferSize(setting.IterateBufferSize).Iterate(new(Watch), + func(idx int, bean interface{}) error { + watch := bean.(*Watch) + + repo := repoCache[watch.RepoID] + if repo == nil { + repo = &Repository{ + ID: watch.RepoID, + } + if _, err := x.Get(repo); err != nil { + return err + } + repoCache[watch.RepoID] = repo + } + + // Remove watches from now unaccessible repositories + mode, err := accessLevel(watch.UserID, repo) + if err != nil { + return err + } + has := AccessModeRead <= mode + if has { + return nil + } + + if _, err = sess.Delete(&Watch{0, watch.UserID, repo.ID}); err != nil { + return err + } + _, err = sess.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repo.ID) + + return err + }) + if err != nil { + return err + } + + repoCache = make(map[int64]*Repository) + err = x.BufferSize(setting.IterateBufferSize). + Distinct("issue_watch.user_id", "issue.repo_id"). + Join("INNER", "issue", "issue_watch.issue_id = issue.id"). + Where("issue_watch.is_watching = ?", true). + Iterate(new(IssueWatch), + func(idx int, bean interface{}) error { + watch := bean.(*IssueWatch) + + repo := repoCache[watch.RepoID] + if repo == nil { + repo = &Repository{ + ID: watch.RepoID, + } + if _, err := x.Get(repo); err != nil { + return err + } + repoCache[watch.RepoID] = repo + } + + // Remove issue watches from now unaccssible repositories + mode, err := accessLevel(watch.UserID, repo) + if err != nil { + return err + } + has := AccessModeRead <= mode + if has { + return nil + } + + iw := &IssueWatch{ + IsWatching: false, + } + + _, err = sess. + Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", watch.RepoID). + Cols("is_watching", "updated_unix"). + Where("`issue_watch`.user_id = ?", watch.UserID). + Update(iw) + + return err + + }) + if err != nil { + return err + } + + return sess.Commit() +} diff --git a/models/migrations/v68.go b/models/migrations/v68.go new file mode 100644 index 000000000..e27b896c8 --- /dev/null +++ b/models/migrations/v68.go @@ -0,0 +1,213 @@ +// Copyright 2018 The Gitea 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 migrations + +import ( + "fmt" + "regexp" + "strings" + + "code.gitea.io/gitea/modules/log" + + "github.com/go-xorm/xorm" +) + +var topicPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*$`) + +func validateTopic(topic string) bool { + return len(topic) <= 35 && topicPattern.MatchString(topic) +} + +func reformatAndRemoveIncorrectTopics(x *xorm.Engine) (err error) { + log.Info("This migration could take up to minutes, please be patient.") + + type Topic struct { + ID int64 + Name string `xorm:"UNIQUE"` + RepoCount int + CreatedUnix int64 `xorm:"INDEX created"` + UpdatedUnix int64 `xorm:"INDEX updated"` + } + + type RepoTopic struct { + RepoID int64 `xorm:"UNIQUE(s)"` + TopicID int64 `xorm:"UNIQUE(s)"` + } + + type Repository struct { + ID int64 `xorm:"pk autoincr"` + Topics []string `xorm:"TEXT JSON"` + } + + if err := x.Sync2(new(Topic)); err != nil { + return fmt.Errorf("Sync2: %v", err) + } + if err := x.Sync2(new(RepoTopic)); err != nil { + return fmt.Errorf("Sync2: %v", err) + } + + sess := x.NewSession() + defer sess.Close() + + const batchSize = 100 + touchedRepo := make(map[int64]struct{}) + delTopicIDs := make([]int64, 0, batchSize) + + log.Info("Validating existed topics...") + if err := sess.Begin(); err != nil { + return err + } + for start := 0; ; start += batchSize { + topics := make([]*Topic, 0, batchSize) + if err := x.Cols("id", "name").Asc("id").Limit(batchSize, start).Find(&topics); err != nil { + return err + } + if len(topics) == 0 { + break + } + for _, topic := range topics { + if validateTopic(topic.Name) { + continue + } + log.Info("Incorrect topic: id = %v, name = %q", topic.ID, topic.Name) + + topic.Name = strings.Replace(strings.TrimSpace(strings.ToLower(topic.Name)), " ", "-", -1) + + ids := make([]int64, 0, 30) + if err := sess.Table("repo_topic").Cols("repo_id"). + Where("topic_id = ?", topic.ID).Find(&ids); err != nil { + return err + } + log.Info("Touched repo ids: %v", ids) + for _, id := range ids { + touchedRepo[id] = struct{}{} + } + + if validateTopic(topic.Name) { + unifiedTopic := Topic{Name: topic.Name} + exists, err := sess.Cols("id", "name").Get(&unifiedTopic) + log.Info("Exists topic with the name %q? %v, id = %v", topic.Name, exists, unifiedTopic.ID) + if err != nil { + return err + } + if exists { + log.Info("Updating repo_topic rows with topic_id = %v to topic_id = %v", topic.ID, unifiedTopic.ID) + if _, err := sess.Where("topic_id = ? AND repo_id NOT IN "+ + "(SELECT rt1.repo_id FROM repo_topic rt1 INNER JOIN repo_topic rt2 "+ + "ON rt1.repo_id = rt2.repo_id WHERE rt1.topic_id = ? AND rt2.topic_id = ?)", + topic.ID, topic.ID, unifiedTopic.ID).Update(&RepoTopic{TopicID: unifiedTopic.ID}); err != nil { + return err + } + log.Info("Updating topic `repo_count` field") + if _, err := sess.Exec( + "UPDATE topic SET repo_count = (SELECT COUNT(*) FROM repo_topic WHERE topic_id = ? GROUP BY topic_id) WHERE id = ?", + unifiedTopic.ID, unifiedTopic.ID); err != nil { + return err + } + } else { + log.Info("Updating topic: id = %v, name = %q", topic.ID, topic.Name) + if _, err := sess.Table("topic").ID(topic.ID). + Update(&Topic{Name: topic.Name}); err != nil { + return err + } + continue + } + } + delTopicIDs = append(delTopicIDs, topic.ID) + } + } + if err := sess.Commit(); err != nil { + return err + } + + sess.Init() + + log.Info("Deleting incorrect topics...") + if err := sess.Begin(); err != nil { + return err + } + log.Info("Deleting 'repo_topic' rows for topics with ids = %v", delTopicIDs) + if _, err := sess.In("topic_id", delTopicIDs).Delete(&RepoTopic{}); err != nil { + return err + } + log.Info("Deleting topics with id = %v", delTopicIDs) + if _, err := sess.In("id", delTopicIDs).Delete(&Topic{}); err != nil { + return err + } + if err := sess.Commit(); err != nil { + return err + } + + delRepoTopics := make([]*RepoTopic, 0, batchSize) + + log.Info("Checking the number of topics in the repositories...") + for start := 0; ; start += batchSize { + repoTopics := make([]*RepoTopic, 0, batchSize) + if err := x.Cols("repo_id").Asc("repo_id").Limit(batchSize, start). + GroupBy("repo_id").Having("COUNT(*) > 25").Find(&repoTopics); err != nil { + return err + } + if len(repoTopics) == 0 { + break + } + + log.Info("Number of repositories with more than 25 topics: %v", len(repoTopics)) + for _, repoTopic := range repoTopics { + touchedRepo[repoTopic.RepoID] = struct{}{} + + tmpRepoTopics := make([]*RepoTopic, 0, 30) + if err := x.Where("repo_id = ?", repoTopic.RepoID).Find(&tmpRepoTopics); err != nil { + return err + } + + log.Info("Repository with id = %v has %v topics", repoTopic.RepoID, len(tmpRepoTopics)) + + for i := len(tmpRepoTopics) - 1; i > 24; i-- { + delRepoTopics = append(delRepoTopics, tmpRepoTopics[i]) + } + } + } + + sess.Init() + + log.Info("Deleting superfluous topics for repositories (more than 25 topics)...") + if err := sess.Begin(); err != nil { + return err + } + for _, repoTopic := range delRepoTopics { + log.Info("Deleting 'repo_topic' rows for 'repository' with id = %v. Topic id = %v", + repoTopic.RepoID, repoTopic.TopicID) + + if _, err := sess.Where("repo_id = ? AND topic_id = ?", repoTopic.RepoID, + repoTopic.TopicID).Delete(&RepoTopic{}); err != nil { + return err + } + if _, err := sess.Exec( + "UPDATE topic SET repo_count = (SELECT repo_count FROM topic WHERE id = ?) - 1 WHERE id = ?", + repoTopic.TopicID, repoTopic.TopicID); err != nil { + return err + } + } + + log.Info("Updating repositories 'topics' fields...") + for repoID := range touchedRepo { + topicNames := make([]string, 0, 30) + if err := sess.Table("topic").Cols("name"). + Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id"). + Where("repo_topic.repo_id = ?", repoID).Desc("topic.repo_count").Find(&topicNames); err != nil { + return err + } + log.Info("Updating 'topics' field for repository with id = %v", repoID) + if _, err := sess.ID(repoID).Cols("topics"). + Update(&Repository{Topics: topicNames}); err != nil { + return err + } + } + if err := sess.Commit(); err != nil { + return err + } + + return nil +} diff --git a/models/migrations/v69.go b/models/migrations/v69.go new file mode 100644 index 000000000..8d964688a --- /dev/null +++ b/models/migrations/v69.go @@ -0,0 +1,80 @@ +// Copyright 2018 The Gitea 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 migrations + +import ( + "fmt" + + "github.com/go-xorm/xorm" +) + +func moveTeamUnitsToTeamUnitTable(x *xorm.Engine) error { + // Team see models/team.go + type Team struct { + ID int64 + OrgID int64 + UnitTypes []int `xorm:"json"` + } + + // TeamUnit see models/org_team.go + type TeamUnit struct { + ID int64 `xorm:"pk autoincr"` + OrgID int64 `xorm:"INDEX"` + TeamID int64 `xorm:"UNIQUE(s)"` + Type int `xorm:"UNIQUE(s)"` + } + + if err := x.Sync2(new(TeamUnit)); err != nil { + return fmt.Errorf("Sync2: %v", err) + } + + sess := x.NewSession() + defer sess.Close() + + if err := sess.Begin(); err != nil { + return err + } + + // Update team unit types + const batchSize = 100 + for start := 0; ; start += batchSize { + teams := make([]*Team, 0, batchSize) + if err := x.Limit(batchSize, start).Find(&teams); err != nil { + return err + } + if len(teams) == 0 { + break + } + + for _, team := range teams { + var unitTypes []int + if len(team.UnitTypes) == 0 { + unitTypes = allUnitTypes + } else { + unitTypes = team.UnitTypes + } + + // insert units for team + var units = make([]TeamUnit, 0, len(unitTypes)) + for _, tp := range unitTypes { + units = append(units, TeamUnit{ + OrgID: team.OrgID, + TeamID: team.ID, + Type: tp, + }) + } + + if _, err := sess.Insert(&units); err != nil { + return fmt.Errorf("Insert team units: %v", err) + } + + } + } + + if err := dropTableColumns(sess, "team", "unit_types"); err != nil { + return err + } + return sess.Commit() +} diff --git a/models/models.go b/models/models.go index ddf784dee..aaf1370fd 100644 --- a/models/models.go +++ b/models/models.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2018 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -121,6 +122,7 @@ func init() { new(Reaction), new(IssueAssignees), new(U2FRegistration), + new(TeamUnit), ) gonicNames := []string{"SSL", "UID"} @@ -184,6 +186,18 @@ func parsePostgreSQLHostPort(info string) (string, string) { return host, port } +func getPostgreSQLConnectionString(DBHost, DBUser, DBPasswd, DBName, DBParam, DBSSLMode string) (connStr string) { + host, port := parsePostgreSQLHostPort(DBHost) + if host[0] == '/' { // looks like a unix socket + connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s", + url.PathEscape(DBUser), url.PathEscape(DBPasswd), port, DBName, DBParam, DBSSLMode, host) + } else { + connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s", + url.PathEscape(DBUser), url.PathEscape(DBPasswd), host, port, DBName, DBParam, DBSSLMode) + } + return +} + func parseMSSQLHostPort(info string) (string, string) { host, port := "127.0.0.1", "1433" if strings.Contains(info, ":") { @@ -214,14 +228,7 @@ func getEngine() (*xorm.Engine, error) { DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param) } case "postgres": - host, port := parsePostgreSQLHostPort(DbCfg.Host) - if host[0] == '/' { // looks like a unix socket - connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s", - url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), port, DbCfg.Name, Param, DbCfg.SSLMode, host) - } else { - connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s", - url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), host, port, DbCfg.Name, Param, DbCfg.SSLMode) - } + connStr = getPostgreSQLConnectionString(DbCfg.Host, DbCfg.User, DbCfg.Passwd, DbCfg.Name, Param, DbCfg.SSLMode) case "mssql": host, port := parseMSSQLHostPort(DbCfg.Host) connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, DbCfg.Name, DbCfg.User, DbCfg.Passwd) diff --git a/models/models_test.go b/models/models_test.go index 649b1e02e..7016fdb4b 100644 --- a/models/models_test.go +++ b/models/models_test.go @@ -1,4 +1,5 @@ // Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2018 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -53,3 +54,42 @@ func Test_parsePostgreSQLHostPort(t *testing.T) { assert.Equal(t, test.Port, port) } } + +func Test_getPostgreSQLConnectionString(t *testing.T) { + tests := []struct { + Host string + Port string + User string + Passwd string + Name string + Param string + SSLMode string + Output string + }{ + { + Host: "/tmp/pg.sock", + Port: "4321", + User: "testuser", + Passwd: "space space !#$%^^%^```-=?=", + Name: "gitea", + Param: "", + SSLMode: "false", + Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/giteasslmode=false&host=/tmp/pg.sock", + }, + { + Host: "localhost", + Port: "1234", + User: "pgsqlusername", + Passwd: "I love Gitea!", + Name: "gitea", + Param: "", + SSLMode: "true", + Output: "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/giteasslmode=true", + }, + } + + for _, test := range tests { + connStr := getPostgreSQLConnectionString(test.Host, test.User, test.Passwd, test.Name, test.Param, test.SSLMode) + assert.Equal(t, test.Output, connStr) + } +} diff --git a/models/notification.go b/models/notification.go index c8376a857..8c36c0c5c 100644 --- a/models/notification.go +++ b/models/notification.go @@ -119,7 +119,17 @@ func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthor } } + issue.loadRepo(e) + for _, watch := range watches { + issue.Repo.Units = nil + if issue.IsPull && !issue.Repo.CheckUnitUser(watch.UserID, false, UnitTypePullRequests) { + continue + } + if !issue.IsPull && !issue.Repo.CheckUnitUser(watch.UserID, false, UnitTypeIssues) { + continue + } + if err := notifyUser(watch.UserID); err != nil { return err } diff --git a/models/org.go b/models/org.go index ed0d58306..23f6c58bf 100644 --- a/models/org.go +++ b/models/org.go @@ -154,12 +154,26 @@ func CreateOrganization(org, owner *User) (err error) { Name: ownerTeamName, Authorize: AccessModeOwner, NumMembers: 1, - UnitTypes: allRepUnitTypes, } if _, err = sess.Insert(t); err != nil { return fmt.Errorf("insert owner team: %v", err) } + // insert units for team + var units = make([]TeamUnit, 0, len(allRepUnitTypes)) + for _, tp := range allRepUnitTypes { + units = append(units, TeamUnit{ + OrgID: org.ID, + TeamID: t.ID, + Type: tp, + }) + } + + if _, err = sess.Insert(&units); err != nil { + sess.Rollback() + return err + } + if _, err = sess.Insert(&TeamUser{ UID: owner.ID, OrgID: org.ID, @@ -238,6 +252,7 @@ func deleteOrg(e *xorm.Session, u *User) error { &Team{OrgID: u.ID}, &OrgUser{OrgID: u.ID}, &TeamUser{OrgID: u.ID}, + &TeamUnit{OrgID: u.ID}, ); err != nil { return fmt.Errorf("deleteBeans: %v", err) } diff --git a/models/org_team.go b/models/org_team.go index 9d8a03141..3b37f936f 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -1,3 +1,4 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. // Copyright 2016 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. @@ -10,7 +11,6 @@ import ( "strings" "code.gitea.io/gitea/modules/log" - "github.com/go-xorm/xorm" ) @@ -28,15 +28,16 @@ type Team struct { Members []*User `xorm:"-"` NumRepos int NumMembers int - UnitTypes []UnitType `xorm:"json"` + Units []*TeamUnit `xorm:"-"` } -// GetUnitTypes returns unit types the team owned, empty means all the unit types -func (t *Team) GetUnitTypes() []UnitType { - if len(t.UnitTypes) == 0 { - return allRepUnitTypes +func (t *Team) getUnits(e Engine) (err error) { + if t.Units != nil { + return nil } - return t.UnitTypes + + t.Units, err = getUnitsByTeamID(e, t.ID) + return err } // HasWriteAccess returns true if team has at least write level access mode. @@ -178,6 +179,11 @@ func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (e if err = watchRepo(e, teamUser.UID, repo.ID, false); err != nil { return err } + + // Remove all IssueWatches a user has subscribed to in the repositories + if err := removeIssueWatchersByRepoID(e, teamUser.UID, repo.ID); err != nil { + return err + } } return nil @@ -209,11 +215,12 @@ func (t *Team) RemoveRepository(repoID int64) error { // UnitEnabled returns if the team has the given unit type enabled func (t *Team) UnitEnabled(tp UnitType) bool { - if len(t.UnitTypes) == 0 { - return true + if err := t.getUnits(x); err != nil { + log.Warn("Error loading repository (ID: %d) units: %s", t.ID, err.Error()) } - for _, u := range t.UnitTypes { - if u == tp { + + for _, unit := range t.Units { + if unit.Type == tp { return true } } @@ -270,6 +277,17 @@ func NewTeam(t *Team) (err error) { return err } + // insert units for team + if len(t.Units) > 0 { + for _, unit := range t.Units { + unit.TeamID = t.ID + } + if _, err = sess.Insert(&t.Units); err != nil { + sess.Rollback() + return err + } + } + // Update organization number of teams. if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil { sess.Rollback() @@ -374,11 +392,34 @@ func DeleteTeam(t *Team) error { return err } + if err := t.getMembers(sess); err != nil { + return err + } + // Delete all accesses. for _, repo := range t.Repos { if err := repo.recalculateTeamAccesses(sess, t.ID); err != nil { return err } + + // Remove watches from all users and now unaccessible repos + for _, user := range t.Members { + has, err := hasAccess(sess, user.ID, repo, AccessModeRead) + if err != nil { + return err + } else if has { + continue + } + + if err = watchRepo(sess, user.ID, repo.ID, false); err != nil { + return err + } + + // Remove all IssueWatches a user has subscribed to in the repositories + if err = removeIssueWatchersByRepoID(sess, user.ID, repo.ID); err != nil { + return err + } + } } // Delete team-repo @@ -396,6 +437,13 @@ func DeleteTeam(t *Team) error { return err } + // Delete team-unit. + if _, err := sess. + Where("team_id=?", t.ID). + Delete(new(TeamUnit)); err != nil { + return err + } + // Delete team. if _, err := sess.ID(t.ID).Delete(new(Team)); err != nil { return err @@ -518,6 +566,10 @@ func AddTeamMember(team *Team, userID int64) error { if err := repo.recalculateTeamAccesses(sess, 0); err != nil { return err } + + if err = watchRepo(sess, userID, repo.ID, true); err != nil { + return err + } } return sess.Commit() @@ -558,6 +610,23 @@ func removeTeamMember(e *xorm.Session, team *Team, userID int64) error { if err := repo.recalculateTeamAccesses(e, 0); err != nil { return err } + + // Remove watches from now unaccessible + has, err := hasAccess(e, userID, repo, AccessModeRead) + if err != nil { + return err + } else if has { + continue + } + + if err = watchRepo(e, userID, repo.ID, false); err != nil { + return err + } + + // Remove all IssueWatches a user has subscribed to in the repositories + if err := removeIssueWatchersByRepoID(e, userID, repo.ID); err != nil { + return err + } } // Check if the user is a member of any team in the organization. @@ -646,3 +715,47 @@ func GetTeamsWithAccessToRepo(orgID, repoID int64, mode AccessMode) ([]*Team, er And("team_repo.repo_id = ?", repoID). Find(&teams) } + +// ___________ ____ ___ .__ __ +// \__ ___/___ _____ _____ | | \____ |__|/ |_ +// | |_/ __ \\__ \ / \| | / \| \ __\ +// | |\ ___/ / __ \| Y Y \ | / | \ || | +// |____| \___ >____ /__|_| /______/|___| /__||__| +// \/ \/ \/ \/ + +// TeamUnit describes all units of a repository +type TeamUnit struct { + ID int64 `xorm:"pk autoincr"` + OrgID int64 `xorm:"INDEX"` + TeamID int64 `xorm:"UNIQUE(s)"` + Type UnitType `xorm:"UNIQUE(s)"` +} + +// Unit returns Unit +func (t *TeamUnit) Unit() Unit { + return Units[t.Type] +} + +func getUnitsByTeamID(e Engine, teamID int64) (units []*TeamUnit, err error) { + return units, e.Where("team_id = ?", teamID).Find(&units) +} + +// UpdateTeamUnits updates a teams's units +func UpdateTeamUnits(team *Team, units []TeamUnit) (err error) { + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return err + } + + if _, err = sess.Where("team_id = ?", team.ID).Delete(new(TeamUnit)); err != nil { + return err + } + + if _, err = sess.Insert(units); err != nil { + sess.Rollback() + return err + } + + return sess.Commit() +} diff --git a/models/org_test.go b/models/org_test.go index 42ab4a2a4..c54e7a93b 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -489,8 +489,8 @@ func TestAccessibleReposEnv_CountRepos(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, expectedCount, count) } - testSuccess(2, 2) - testSuccess(4, 1) + testSuccess(2, 3) + testSuccess(4, 2) } func TestAccessibleReposEnv_RepoIDs(t *testing.T) { @@ -503,8 +503,8 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expectedRepoIDs, repoIDs) } - testSuccess(2, 1, 100, []int64{3, 5}) - testSuccess(4, 0, 100, []int64{3}) + testSuccess(2, 1, 100, []int64{3, 5, 32}) + testSuccess(4, 0, 100, []int64{3, 32}) } func TestAccessibleReposEnv_Repos(t *testing.T) { @@ -522,8 +522,8 @@ func TestAccessibleReposEnv_Repos(t *testing.T) { } assert.Equal(t, expectedRepos, repos) } - testSuccess(2, []int64{3, 5}) - testSuccess(4, []int64{3}) + testSuccess(2, []int64{3, 5, 32}) + testSuccess(4, []int64{3, 32}) } func TestAccessibleReposEnv_MirrorRepos(t *testing.T) { diff --git a/models/release.go b/models/release.go index 630995a74..e3760e9ef 100644 --- a/models/release.go +++ b/models/release.go @@ -448,6 +448,11 @@ func DeleteReleaseByID(id int64, u *User, delTag bool) error { } } + rel.Repo = repo + if err = rel.LoadAttributes(); err != nil { + return fmt.Errorf("LoadAttributes: %v", err) + } + mode, _ := accessLevel(x, u.ID, rel.Repo) if err := PrepareWebhooks(rel.Repo, HookEventRelease, &api.ReleasePayload{ Action: api.HookReleaseDeleted, diff --git a/models/repo.go b/models/repo.go index c95c867f3..d1cc290c4 100644 --- a/models/repo.go +++ b/models/repo.go @@ -365,22 +365,14 @@ func (repo *Repository) getUnitsByUserID(e Engine, userID int64, isAdmin bool) ( return err } - var allTypes = make(map[UnitType]struct{}, len(allRepUnitTypes)) - for _, team := range teams { - // Administrators can not be limited - if team.Authorize >= AccessModeAdmin { - return nil - } - for _, unitType := range team.UnitTypes { - allTypes[unitType] = struct{}{} - } - } - // unique var newRepoUnits = make([]*RepoUnit, 0, len(repo.Units)) for _, u := range repo.Units { - if _, ok := allTypes[u.Type]; ok { - newRepoUnits = append(newRepoUnits, u) + for _, team := range teams { + if team.UnitEnabled(u.Type) { + newRepoUnits = append(newRepoUnits, u) + break + } } } @@ -1848,6 +1840,12 @@ func DeleteRepository(doer *User, uid, repoID int64) error { if _, err = sess.In("issue_id", issueIDs).Delete(&IssueUser{}); err != nil { return err } + if _, err = sess.In("issue_id", issueIDs).Delete(&Reaction{}); err != nil { + return err + } + if _, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{}); err != nil { + return err + } attachments := make([]*Attachment, 0, 5) if err = sess. diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index 0448149e6..9d2935d58 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -172,5 +172,14 @@ func (repo *Repository) DeleteCollaboration(uid int64) (err error) { return err } + if err = watchRepo(sess, uid, repo.ID, false); err != nil { + return err + } + + // Remove all IssueWatches a user has subscribed to in the repository + if err := removeIssueWatchersByRepoID(sess, uid, repo.ID); err != nil { + return err + } + return sess.Commit() } diff --git a/models/repo_list_test.go b/models/repo_list_test.go index 3bccb1aeb..164bc19bf 100644 --- a/models/repo_list_test.go +++ b/models/repo_list_test.go @@ -147,10 +147,10 @@ func TestSearchRepositoryByName(t *testing.T) { count: 14}, {name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true}, - count: 15}, + count: 16}, {name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, - count: 19}, + count: 20}, {name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName", opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, count: 13}, @@ -159,7 +159,7 @@ func TestSearchRepositoryByName(t *testing.T) { count: 11}, {name: "AllPublic/PublicRepositoriesOfOrganization", opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse}, - count: 15}, + count: 16}, } for _, testCase := range testCases { diff --git a/models/repo_watch.go b/models/repo_watch.go index fb89a55a1..8019027c1 100644 --- a/models/repo_watch.go +++ b/models/repo_watch.go @@ -109,6 +109,23 @@ func notifyWatchers(e Engine, act *Action) error { act.ID = 0 act.UserID = watches[i].UserID + act.Repo.Units = nil + + switch act.OpType { + case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionDeleteBranch: + if !act.Repo.CheckUnitUser(act.UserID, false, UnitTypeCode) { + continue + } + case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue: + if !act.Repo.CheckUnitUser(act.UserID, false, UnitTypeIssues) { + continue + } + case ActionCreatePullRequest, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest: + if !act.Repo.CheckUnitUser(act.UserID, false, UnitTypePullRequests) { + continue + } + } + if _, err = e.InsertOne(act); err != nil { return fmt.Errorf("insert new action: %v", err) } diff --git a/models/topic.go b/models/topic.go index 3b1737f8a..678795a3d 100644 --- a/models/topic.go +++ b/models/topic.go @@ -6,6 +6,7 @@ package models import ( "fmt" + "regexp" "strings" "code.gitea.io/gitea/modules/util" @@ -20,10 +21,12 @@ func init() { ) } +var topicPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*$`) + // Topic represents a topic of repositories type Topic struct { ID int64 - Name string `xorm:"unique"` + Name string `xorm:"UNIQUE"` RepoCount int CreatedUnix util.TimeStamp `xorm:"INDEX created"` UpdatedUnix util.TimeStamp `xorm:"INDEX updated"` @@ -31,8 +34,8 @@ type Topic struct { // RepoTopic represents associated repositories and topics type RepoTopic struct { - RepoID int64 `xorm:"unique(s)"` - TopicID int64 `xorm:"unique(s)"` + RepoID int64 `xorm:"UNIQUE(s)"` + TopicID int64 `xorm:"UNIQUE(s)"` } // ErrTopicNotExist represents an error that a topic is not exist @@ -51,6 +54,11 @@ func (err ErrTopicNotExist) Error() string { return fmt.Sprintf("topic is not exist [name: %s]", err.Name) } +// ValidateTopic checks topics by length and match pattern rules +func ValidateTopic(topic string) bool { + return len(topic) <= 35 && topicPattern.MatchString(topic) +} + // GetTopicByName retrieves topic by name func GetTopicByName(name string) (*Topic, error) { var topic Topic @@ -182,6 +190,13 @@ func SaveTopics(repoID int64, topicNames ...string) error { } } + topicNames = make([]string, 0, 25) + if err := sess.Table("topic").Cols("name"). + Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id"). + Where("repo_topic.repo_id = ?", repoID).Desc("topic.repo_count").Find(&topicNames); err != nil { + return err + } + if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{ Topics: topicNames, }); err != nil { diff --git a/models/topic_test.go b/models/topic_test.go index 472f4e52d..ef374e557 100644 --- a/models/topic_test.go +++ b/models/topic_test.go @@ -55,3 +55,16 @@ func TestAddTopic(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 2, len(topics)) } + +func TestTopicValidator(t *testing.T) { + assert.True(t, ValidateTopic("12345")) + assert.True(t, ValidateTopic("2-test")) + assert.True(t, ValidateTopic("test-3")) + assert.True(t, ValidateTopic("first")) + assert.True(t, ValidateTopic("second-test-topic")) + assert.True(t, ValidateTopic("third-project-topic-with-max-length")) + + assert.False(t, ValidateTopic("$fourth-test,topic")) + assert.False(t, ValidateTopic("-fifth-test-topic")) + assert.False(t, ValidateTopic("sixth-go-project-topic-with-excess-length")) +} diff --git a/models/user.go b/models/user.go index 4eeb3e22a..59a27e129 100644 --- a/models/user.go +++ b/models/user.go @@ -546,28 +546,46 @@ func (u *User) GetRepositories(page, pageSize int) (err error) { return err } -// GetRepositoryIDs returns repositories IDs where user owned -func (u *User) GetRepositoryIDs() ([]int64, error) { +// GetRepositoryIDs returns repositories IDs where user owned and has unittypes +func (u *User) GetRepositoryIDs(units ...UnitType) ([]int64, error) { var ids []int64 - return ids, x.Table("repository").Cols("id").Where("owner_id = ?", u.ID).Find(&ids) + + sess := x.Table("repository").Cols("repository.id") + + if len(units) > 0 { + sess = sess.Join("INNER", "repo_unit", "repository.id = repo_unit.repo_id") + sess = sess.In("repo_unit.type", units) + } + + return ids, sess.Where("owner_id = ?", u.ID).Find(&ids) } -// GetOrgRepositoryIDs returns repositories IDs where user's team owned -func (u *User) GetOrgRepositoryIDs() ([]int64, error) { +// GetOrgRepositoryIDs returns repositories IDs where user's team owned and has unittypes +func (u *User) GetOrgRepositoryIDs(units ...UnitType) ([]int64, error) { var ids []int64 - return ids, x.Table("repository"). + + sess := x.Table("repository"). Cols("repository.id"). - Join("INNER", "team_user", "repository.owner_id = team_user.org_id AND team_user.uid = ?", u.ID). + Join("INNER", "team_user", "repository.owner_id = team_user.org_id"). + Join("INNER", "team_repo", "repository.is_private != ? OR (team_user.team_id = team_repo.team_id AND repository.id = team_repo.repo_id)", true) + + if len(units) > 0 { + sess = sess.Join("INNER", "team_unit", "team_unit.team_id = team_user.team_id") + sess = sess.In("team_unit.type", units) + } + + return ids, sess. + Where("team_user.uid = ?", u.ID). GroupBy("repository.id").Find(&ids) } // GetAccessRepoIDs returns all repositories IDs where user's or user is a team member organizations -func (u *User) GetAccessRepoIDs() ([]int64, error) { - ids, err := u.GetRepositoryIDs() +func (u *User) GetAccessRepoIDs(units ...UnitType) ([]int64, error) { + ids, err := u.GetRepositoryIDs(units...) if err != nil { return nil, err } - ids2, err := u.GetOrgRepositoryIDs() + ids2, err := u.GetOrgRepositoryIDs(units...) if err != nil { return nil, err } diff --git a/models/user_test.go b/models/user_test.go index 4fd0bc0fa..20de1a64b 100644 --- a/models/user_test.go +++ b/models/user_test.go @@ -159,3 +159,25 @@ func BenchmarkHashPassword(b *testing.B) { u.HashPassword(pass) } } + +func TestGetOrgRepositoryIDs(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) + user5 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) + + accessibleRepos, err := user2.GetOrgRepositoryIDs() + assert.NoError(t, err) + // User 2's team has access to private repos 3, 5, repo 32 is a public repo of the organization + assert.Equal(t, []int64{3, 5, 32}, accessibleRepos) + + accessibleRepos, err = user4.GetOrgRepositoryIDs() + assert.NoError(t, err) + // User 4's team has access to private repo 3, repo 32 is a public repo of the organization + assert.Equal(t, []int64{3, 32}, accessibleRepos) + + accessibleRepos, err = user5.GetOrgRepositoryIDs() + assert.NoError(t, err) + // User 5's team has no access to any repo + assert.Len(t, accessibleRepos, 0) +} diff --git a/modules/context/auth.go b/modules/context/auth.go index 372bcb187..c38cc3948 100644 --- a/modules/context/auth.go +++ b/modules/context/auth.go @@ -37,12 +37,6 @@ func Toggle(options *ToggleOptions) macaron.Handler { return } - // Check non-logged users landing page. - if !ctx.IsSigned && ctx.Req.RequestURI == "/" && setting.LandingPageURL != setting.LandingPageHome { - ctx.Redirect(setting.AppSubURL + string(setting.LandingPageURL)) - return - } - // Redirect to dashboard if user tries to visit any non-login page. if options.SignOutRequired && ctx.IsSigned && ctx.Req.RequestURI != "/" { ctx.Redirect(setting.AppSubURL + "/") diff --git a/modules/markup/html.go b/modules/markup/html.go index 4f9d02a8f..a4ef86de2 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -469,6 +469,9 @@ func shortLinkProcessorFull(ctx *postProcessCtx, node *html.Node, noLink bool) { } else { link = strings.Replace(link, " ", "-", -1) } + if !strings.Contains(link, "/") { + link = url.PathEscape(link) + } } urlPrefix := ctx.urlPrefix if image { diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index fc11532d1..bf7606e1d 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -82,12 +82,18 @@ func TestRender_ShortLinks(t *testing.T) { rawtree := util.URLJoin(AppSubURL, "raw", "master") url := util.URLJoin(tree, "Link") otherURL := util.URLJoin(tree, "Other-Link") + encodedURL := util.URLJoin(tree, "Link%3F") imgurl := util.URLJoin(rawtree, "Link.jpg") otherImgurl := util.URLJoin(rawtree, "Link+Other.jpg") + encodedImgurl := util.URLJoin(rawtree, "Link+%23.jpg") + notencodedImgurl := util.URLJoin(rawtree, "some", "path", "Link+#.jpg") urlWiki := util.URLJoin(AppSubURL, "wiki", "Link") otherURLWiki := util.URLJoin(AppSubURL, "wiki", "Other-Link") + encodedURLWiki := util.URLJoin(AppSubURL, "wiki", "Link%3F") imgurlWiki := util.URLJoin(AppSubURL, "wiki", "raw", "Link.jpg") otherImgurlWiki := util.URLJoin(AppSubURL, "wiki", "raw", "Link+Other.jpg") + encodedImgurlWiki := util.URLJoin(AppSubURL, "wiki", "raw", "Link+%23.jpg") + notencodedImgurlWiki := util.URLJoin(AppSubURL, "wiki", "raw", "some", "path", "Link+#.jpg") favicon := "http://google.com/favicon.ico" test( @@ -134,4 +140,24 @@ func TestRender_ShortLinks(t *testing.T) { "[[Link]] [[Other Link]]", `

Link Other Link

`, `

Link Other Link

`) + test( + "[[Link?]]", + `

Link?

`, + `

Link?

`) + test( + "[[Link]] [[Other Link]] [[Link?]]", + `

Link Other Link Link?

`, + `

Link Other Link Link?

`) + test( + "[[Link #.jpg]]", + `

`, + `

`) + test( + "[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]", + `

AltName

`, + `

AltName

`) + test( + "[[some/path/Link #.jpg]]", + `

`, + `

`) } diff --git a/modules/templates/helper.go b/modules/templates/helper.go index bf5c0130b..b6c835ad4 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -323,7 +323,7 @@ func RenderCommitBody(msg, urlPrefix string, metas map[string]string) template.H // IsMultilineCommitMessage checks to see if a commit message contains multiple lines. func IsMultilineCommitMessage(msg string) bool { - return strings.Count(strings.TrimSpace(msg), "\n") > 1 + return strings.Count(strings.TrimSpace(msg), "\n") >= 1 } // Actioner describes an action diff --git a/modules/util/util.go b/modules/util/util.go index b6acb9796..5dcbe448f 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -10,6 +10,7 @@ import ( "strings" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" ) // OptionalBool a boolean that can be "null" @@ -78,6 +79,18 @@ func URLJoin(base string, elems ...string) string { return joinedURL } +// IsExternalURL checks if rawURL points to an external URL like http://example.com +func IsExternalURL(rawURL string) bool { + parsed, err := url.Parse(rawURL) + if err != nil { + return true + } + if len(parsed.Host) != 0 && strings.Replace(parsed.Host, "www.", "", 1) != strings.Replace(setting.Domain, "www.", "", 1) { + return true + } + return false +} + // Min min of two ints func Min(a, b int) int { if a > b { diff --git a/modules/util/util_test.go b/modules/util/util_test.go index 0d79df605..d9357ffa3 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -7,6 +7,8 @@ package util import ( "testing" + "code.gitea.io/gitea/modules/setting" + "github.com/stretchr/testify/assert" ) @@ -42,3 +44,36 @@ func TestURLJoin(t *testing.T) { assert.Equal(t, test.Expected, URLJoin(test.Base, test.Elements...)) } } + +func TestIsExternalURL(t *testing.T) { + setting.Domain = "try.gitea.io" + type test struct { + Expected bool + RawURL string + } + newTest := func(expected bool, rawURL string) test { + return test{Expected: expected, RawURL: rawURL} + } + for _, test := range []test{ + newTest(false, + "https://try.gitea.io"), + newTest(true, + "https://example.com/"), + newTest(true, + "//example.com"), + newTest(true, + "http://example.com"), + newTest(false, + "a/"), + newTest(false, + "https://try.gitea.io/test?param=false"), + newTest(false, + "test?param=false"), + newTest(false, + "//try.gitea.io/test?param=false"), + newTest(false, + "/hey/hey/hey#3244"), + } { + assert.Equal(t, test.Expected, IsExternalURL(test.RawURL)) + } +} diff --git a/options/locale/locale_bg-BG.ini b/options/locale/locale_bg-BG.ini index 810e8d471..f0d6b1c0a 100644 --- a/options/locale/locale_bg-BG.ini +++ b/options/locale/locale_bg-BG.ini @@ -79,7 +79,6 @@ has_unconfirmed_mail=Здравейте %s, имате непотвърден а resend_mail=Щракнете тук, за да се изпрати ново писмо за потвърждение reset_password=Нулиране на паролата reset_password_helper=Щракнете тук, за да нулирате паролата си -password_too_short=Размерът на паролата не може да бъде по-малък от %d знака. [mail] activate_account=Моля активирайте Вашия профил diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 818611ee2..1fc7e193d 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -79,7 +79,6 @@ has_unconfirmed_mail=Zdravím, %s, máte nepotvrzenou e-mailovou adresu (%sBitte verwende einen absoluten Pfad, wenn Gitea als Service gestartet wird. -err_empty_db_path=Der SQLite3 oder TiDB Datenbankpfad darf nicht leer sein. -err_invalid_tidb_name=Der TiDB Datenbankname darf nicht die Zeichen "." und "-" enthalten. +err_empty_db_path=Der SQLite3- oder TiDB-Datenbankpfad darf nicht leer sein. +err_invalid_tidb_name=Der TiDB-Datenbankname darf nicht die Zeichen „.“ und „-“ enthalten. no_admin_and_disable_registration=Du kannst Selbst-Registrierungen nicht deaktivieren, ohne ein Administratorkonto zu erstellen. err_empty_admin_password=Das Administrator-Passwort darf nicht leer sein. @@ -97,17 +97,17 @@ app_name=Seitentitel app_name_helper=Du kannst hier den Namen deines Unternehmens eingeben. repo_path=Repository-Verzeichnis repo_path_helper=Remote-Git-Repositories werden in diesem Verzeichnis gespeichert. -lfs_path=Git LFS-Wurzelpfad +lfs_path=Git-LFS-Wurzelpfad lfs_path_helper=In diesem Verzeichnis werden die Dateien von Git LFS abgespeichert. Leer lassen um LFS zu deaktivieren. run_user=Ausführen als run_user_helper=Gebe den Betriebssystem-Benutzernamen ein, unter welchem Gitea laufen soll. Beachte, dass dieser Nutzer Zugriff auf den Repository-Ordner haben muss. -domain=SSH Server-Domain +domain=SSH-Server-Domain domain_helper=Domain oder Host-Adresse für die SSH-URL. -ssh_port=SSH Server Port +ssh_port=SSH-Server-Port ssh_port_helper=Der Port deines SSH-Servers. Leer lassen um SSH zu deaktivieren. -http_port=Gitea HTTP-Listen-Port -http_port_helper=Port unter dem der Gitea Web Server laufen soll. -app_url=Gitea Basis-URL +http_port=Gitea-HTTP-Listen-Port +http_port_helper=Port, unter dem der Gitea-Webserver laufen soll. +app_url=Gitea-Basis-URL app_url_helper=Adresse für HTTP(S)-Klon-URLs und E-Mail-Benachrichtigungen. log_root_path=Logdateipfad log_root_path_helper=Log-Dateien werden in diesem Verzeichnis gespeichert. @@ -117,8 +117,8 @@ email_title=E-Mail-Einstellungen smtp_host=SMTP-Server smtp_from=E-Mail senden als smtp_from_helper=E-Mail-Adresse, die von Gitea genutzt werden soll. Bitte gib die E-Mail-Adresse im '"Name" '-Format ein. -mailer_user=SMTP Benutzername -mailer_password=SMTP Passwort +mailer_user=SMTP-Benutzername +mailer_password=SMTP-Passwort register_confirm=E-Mail-Bestätigung benötigt zum Registrieren mail_notify=E-Mail-Benachrichtigungen aktivieren server_service_title=Sonstige Server- und Drittserviceeinstellungen @@ -131,9 +131,9 @@ federated_avatar_lookup_popup=Föderierte Profilbilder via Libravatar aktivieren disable_registration=Registrierung deaktivieren disable_registration_popup=Registrierung neuer Benutzer deaktivieren. Nur Administratoren werden neue Benutzerkonten anlegen können. allow_only_external_registration_popup=Registrierung nur über externe Services aktiveren. -openid_signin=OpenID Anmeldung aktivieren +openid_signin=OpenID-Anmeldung aktivieren openid_signin_popup=Benutzeranmeldung via OpenID aktivieren. -openid_signup=OpenID Selbstregistrierung aktivieren +openid_signup=OpenID-Selbstregistrierung aktivieren openid_signup_popup=OpenID-basierte Selbstregistrierung aktivieren. enable_captcha=CAPTCHA aktivieren enable_captcha_popup=Captcha-Eingabe bei der Registrierung erforderlich. @@ -147,10 +147,10 @@ confirm_password=Passwort bestätigen admin_email=E-Mail-Adresse install_btn_confirm=Gitea installieren test_git_failed=Fehler beim Test des 'git' Kommandos: %v -sqlite3_not_available=Diese Gitea-Version unterstützt SQLite3 nicht. Bitte lade die offizielle binäre Version von %s herunter (nicht die 'gobuild'-Version). +sqlite3_not_available=Diese Gitea-Version unterstützt SQLite3 nicht. Bitte lade die offizielle binäre Version von %s herunter (nicht die „gobuild“-Version). invalid_db_setting=Datenbankeinstellungen sind ungültig: %v invalid_repo_path=Repository-Verzeichnis ist ungültig: %v -run_user_not_match=Der "Ausführen als" Benutzer ist nicht der aktuelle Benutzer: %s -> %s +run_user_not_match=Der „Ausführen als“-Benutzername ist nicht der aktuelle Benutzername: %s -> %s save_config_failed=Fehler beim Speichern der Konfiguration: %v invalid_admin_setting=Administrator-Konto Einstellungen sind ungültig: %v install_success=Willkommen! Danke, dass du Gitea gewählt hast. Viel Spaß! @@ -162,7 +162,7 @@ default_allow_create_organization_popup=Neuen Nutzern das Erstellen von Organisa default_enable_timetracking=Zeiterfassung standardmäßig aktivieren default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Repositories aktivieren. no_reply_address=Versteckte E-Mail-Domain -no_reply_address_helper=Domain-Namen für Benutzer mit einer versteckten Emailadresse. Zum Beispiel wird der Benutzername "Joe" in Git als "joe@noreply.example.org" protokolliert, wenn die versteckte E-Mail-Domäne "noreply.example.org" festgelegt ist. +no_reply_address_helper=Domain-Name für Benutzer mit einer versteckten Emailadresse. Zum Beispiel wird der Benutzername „Joe“ in Git als „joe@noreply.example.org“ protokolliert, wenn die versteckte E-Mail-Domain „noreply.example.org“ festgelegt ist. [home] uname_holder=E-Mail-Adresse oder Benutzername @@ -213,7 +213,7 @@ send_reset_mail=E-Mail zum Passwort-zurücksetzen erneut verschicken reset_password=Passwort zurücksetzen invalid_code=Dein Bestätigungs-Code ist ungültig oder abgelaufen. reset_password_helper=Passwort zurückzusetzen -password_too_short=Das Passwort muss mindenstens %d Zeichen lang sein. +password_too_short=Das Passwort muss mindestens %d Zeichen lang sein. non_local_account=Benutzer, die nicht von Gitea verwaltet werden können ihre Passwörter nicht über das Web Interface ändern. verify=Verifizieren scratch_code=Einmalpasswort @@ -225,9 +225,9 @@ login_userpass=Anmelden login_openid=OpenID openid_connect_submit=Verbinden openid_connect_title=Mit bestehendem Konto verbinden -openid_connect_desc=Die gewählte OpenID URI ist unbekannt. Ordne sie hier einem neuen Account zu. +openid_connect_desc=Die gewählte OpenID-URI ist unbekannt. Ordne sie hier einem neuen Account zu. openid_register_title=Neues Konto einrichten -openid_register_desc=Die gewählte OpenID URI ist unbekannt. Ordne sie hier einem neuen Account zu. +openid_register_desc=Die gewählte OpenID-URI ist unbekannt. Ordne sie hier einem neuen Account zu. openid_signin_desc=Gib deine OpenID-URI ein. Zum Beispiel: https://anne.me, bob.openid.org.cn oder gnusocial.net/carry. disable_forgot_password_mail=Das Zurücksetzen von Passwörtern wurde deaktiviert. Bitte wende dich an den Administrator. @@ -264,8 +264,8 @@ TreeName=Dateipfad Content=Inhalt require_error=` darf nicht leer sein.` -alpha_dash_error=` sollte nur Buchstaben, Zahlen, Bindestriche ('-') und Unterstriche ('_') enthalten` -alpha_dash_dot_error=` sollte nur Buchstaben, Zahlen, Bindestriche ('-'), Unterstriche ('_') und Punkte ('.') enthalten` +alpha_dash_error=` sollte nur Buchstaben, Zahlen, Bindestriche („-“) und Unterstriche („_“) enthalten.` +alpha_dash_dot_error=` sollte nur Buchstaben, Zahlen, Bindestriche („-“), Unterstriche („_“) und Punkte („.“) enthalten.` git_ref_name_error=` muss ein wohlgeformter Git-Referenzname sein.` size_error=` muss die Größe %s haben.` min_size_error=` muss mindestens %s Zeichen enthalten.` @@ -283,13 +283,13 @@ org_name_been_taken=Der Organisationsname ist bereits vergeben. team_name_been_taken=Der Teamname ist bereits vergeben. team_no_units_error=Das Team muss auf mindestens einen Bereich Zugriff haben. email_been_used=Die E-Mail-Adresse wird bereits verwendet. -openid_been_used=Die OpenID-Adresse "%s" wird bereits verwendet. +openid_been_used=Die OpenID-Adresse „%s“ wird bereits verwendet. username_password_incorrect=Benutzername oder Passwort ist falsch. enterred_invalid_repo_name=Der eingegebenen Repository-Name ist falsch. -enterred_invalid_owner_name=Der Name des neuen Besitzers ist invalid. +enterred_invalid_owner_name=Der Name des neuen Besitzers ist ungültig. enterred_invalid_password=Das eingegebene Passwort ist falsch. user_not_exist=Dieser Benutzer ist nicht vorhanden. -last_org_owner=Du kannst den letzten Benutzer nicht aus dem "Besitzer"-Team entferenen. Es muss mindestens ein Besitzer in einer Organisation geben. +last_org_owner=Du kannst den letzten Benutzer nicht aus dem „Besitzer“-Team entfernen. Es muss mindestens einen Besitzer in einer Organisation geben. cannot_add_org_to_team=Eine Organisation kann nicht als Teammitglied hinzugefügt werden. invalid_ssh_key=Dein SSH-Key kann nicht überprüft werden: %s @@ -349,7 +349,7 @@ continue=Weiter cancel=Abbrechen language=Sprache -lookup_avatar_by_mail=Avatar anhand der E-Mail-Addresse suchen +lookup_avatar_by_mail=Profilbild anhand der E-Mail-Addresse suchen federated_avatar_lookup=Suche nach föderierten Profilbildern enable_custom_avatar=Benutzerdefiniertes Profilbild benutzen choose_new_avatar=Neues Profilbild auswählen @@ -364,7 +364,7 @@ new_password=Neues Passwort retype_new_password=Neues Passwort erneut eingeben password_incorrect=Das aktuelle Passwort ist falsch. change_password_success=Dein Passwort wurde aktualisiert. Bitte verwende dieses beim nächsten Einloggen. -password_change_disabled=Benutzer, die nicht von Gitea verwaltet werden, können ihr Passwort im Web Interface nicht ändern. +password_change_disabled=Benutzer, die nicht von Gitea verwaltet werden, können ihr Passwort im Web-Interface nicht ändern. emails=E-Mail-Adressen manage_emails=E-Mail-Adressen verwalten @@ -383,7 +383,7 @@ add_new_email=Neue E-Mail-Adresse hinzufügen add_new_openid=Neue OpenID-URI hinzufügen add_email=E-Mail-Adresse hinzufügen add_openid=OpenID-URI hinzufügen -add_email_confirmation_sent=Eine Bestätigungs-E-Mail wurde an '%s' gesendet. Bitte überprüfe dein Postfach innerhalb der nächsten %s, um die E-Mail-Adresse zu bestätigen. +add_email_confirmation_sent=Eine Bestätigungs-E-Mail wurde an „%s“ gesendet. Bitte überprüfe dein Postfach innerhalb der nächsten %s, um die E-Mail-Adresse zu bestätigen. add_email_success=Die neue E-Mail-Addresse wurde hinzugefügt. add_openid_success=Die neue OpenID-Adresse wurde hinzugefügt. keep_email_private=E-Mail-Adresse verbergen @@ -394,8 +394,8 @@ manage_ssh_keys=SSH-Schlüssel verwalten manage_gpg_keys=GPG-Schlüssel verwalten add_key=Schlüssel hinzufügen ssh_desc=Diese öffentlichen SSH-Keys sind mit deinem Account verbunden. Der dazugehörigen privaten SSH-Keys geben dir vollen Zugriff auf deine Repositories. -gpg_desc=Diese öffentlichen GPG-Keys sind mit deinem Account verbunden. Halte die dazugehörigen privaten SSH-Keys geheim, da diese deine Commits signieren. -ssh_helper=Brauchst du Hilfe? Hier ist Githubs Anleitung zum Erzeugen von SSH-Schlüsseln oder Lösen einfacher SSH-Probleme. +gpg_desc=Diese öffentlichen GPG-Keys sind mit deinem Account verbunden. Halte die dazugehörigen privaten GPG-Keys geheim, da diese deine Commits signieren. +ssh_helper=Brauchst du Hilfe? Hier ist GitHubs Anleitung zum Erzeugen von SSH-Schlüsseln oder zum Lösen einfacher SSH-Probleme. gpg_helper=Brauchst du Hilfe? Hier ist GitHubs Anleitung über GPG. add_new_key=SSH-Schlüssel hinzufügen add_new_gpg_key=GPG-Schlüssel hinzufügen @@ -407,8 +407,8 @@ subkeys=Unterschlüssel key_id=Schlüssel-ID key_name=Schlüsselname key_content=Inhalt -add_key_success=Der SSH-Schlüssel "%s" wurde hinzugefügt. -add_gpg_key_success=Der GPG-Key "%s" wurde hinzugefügt. +add_key_success=Der SSH-Schlüssel „%s“ wurde hinzugefügt. +add_gpg_key_success=Der GPG-Key „%s“ wurde hinzugefügt. delete_key=Entfernen ssh_key_deletion=SSH-Schlüssel entfernen gpg_key_deletion=GPG-Schlüssel entfernen @@ -511,10 +511,10 @@ create_repo=Repository erstellen default_branch=Standardbranch mirror_prune=Entfernen mirror_prune_desc=Entferne veraltete remote-tracking Referenzen -mirror_interval=Spiegelintervall (gültige Zeiteinheiten sind 'h', 'm', 's') +mirror_interval=Spiegelintervall (gültige Zeiteinheiten sind „h“, „m“, „s“) mirror_interval_invalid=Das Spiegel-Intervall ist ungültig. mirror_address=Klonen via URL -mirror_address_desc=Bitte gebe alle benötigten Zugangsdaten in der URL an. +mirror_address_desc=Bitte gib alle benötigten Zugangsdaten in der URL an. mirror_last_synced=Zuletzt synchronisiert watchers=Beobachter stargazers=Favorisiert von @@ -523,7 +523,7 @@ pick_reaction=Wähle eine Reaktion reactions_more=und %d weitere form.reach_limit_of_creation=Du hast bereits dein Limit von %d Repositories erreicht. -form.name_reserved=Der Repository-Name '%s' ist reserviert. +form.name_reserved=Der Repository-Name „%s“ ist reserviert. form.name_pattern_not_allowed='%s' ist nicht erlaubt für Repository-Namen. need_auth=Authentifizierung zum Klonen benötigt @@ -588,7 +588,7 @@ editor.edit_file=Datei bearbeiten editor.preview_changes=Vorschau der Änderungen editor.cannot_edit_non_text_files=Binärdateien können nicht im Webinterface bearbeitet werden. editor.edit_this_file=Datei bearbeiten -editor.must_be_on_a_branch=Du musst dich in einer Branch befinden, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. +editor.must_be_on_a_branch=Du musst dich in einem Branch befinden, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. editor.fork_before_edit=Du musst dieses Repository forken, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. editor.delete_this_file=Datei löschen editor.must_have_write_access=Du benötigst Schreibzugriff, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. @@ -598,31 +598,31 @@ editor.filename_help=Füge einen Ordner hinzu, indem du seinen Namen und anschli editor.or=oder editor.cancel_lower=Abbrechen editor.commit_changes=Änderungen committen -editor.add_tmpl='%s/' hinzufügen -editor.add='%s' hinzufügen -editor.update='%s' ändern -editor.delete='%s' löschen +editor.add_tmpl=„%s/“ hinzufügen +editor.add=„%s“ hinzufügen +editor.update=„%s“ ändern +editor.delete=„%s“ löschen editor.commit_message_desc=Eine ausführlichere (optionale) Beschreibung hinzufügen… -editor.commit_directly_to_this_branch=Direkt in die %s-Branch einchecken. -editor.create_new_branch=Einen neue Branch für diesen Commit erstellen und einen Pull Request starten. +editor.commit_directly_to_this_branch=Direkt in den Branch „%s“ einchecken. +editor.create_new_branch=Einen neuen Branch für diesen Commit erstellen und einen Pull Request starten. editor.new_branch_name_desc=Neuer Branchname… editor.cancel=Abbrechen editor.filename_cannot_be_empty=Der Dateiname darf nicht leer sein. editor.branch_already_exists=Branch '%s' existiert bereits in diesem Repository. -editor.directory_is_a_file=Der Verzeichnisname '%s' wird bereits als Dateiname in diesem Repository verwendet. +editor.directory_is_a_file=Der Verzeichnisname „%s“ wird bereits als Dateiname in diesem Repository verwendet. editor.file_is_a_symlink='%s' ist ein symolischer Link. Symbolische Links können mit dem Web Editor nicht bearbeitet werden. -editor.filename_is_a_directory=Der Dateiname '%s' wird bereits als Verzeichnisname in diesem Repository verwendet. -editor.file_editing_no_longer_exists=Die bearbeitete Datei '%s' existiert nicht mehr in diesem Repository. -editor.file_changed_while_editing=Der Inhalt der Datei hat sich seit dem Beginn der Bearbeitung geändert. Hier klicken um die Änderungen anzusehen, oder Änderungen erneut comitten um sie zu überschreiben. -editor.file_already_exists=Eine Datei mit dem Namen '%s' ist bereits in diesem Repository vorhanden. +editor.filename_is_a_directory=Der Dateiname „%s“ wird bereits als Verzeichnisname in diesem Repository verwendet. +editor.file_editing_no_longer_exists=Die bearbeitete Datei „%s“ existiert nicht mehr in diesem Repository. +editor.file_changed_while_editing=Der Inhalt der Datei hat sich seit dem Beginn der Bearbeitung geändert. Hier klicken, um die Änderungen anzusehen, oder Änderungen erneut comitten, um sie zu überschreiben. +editor.file_already_exists=Eine Datei mit dem Namen „%s“ ist bereits in diesem Repository vorhanden. editor.no_changes_to_show=Keine Änderungen vorhanden. editor.fail_to_update_file=Fehler beim Ändern/Erstellen der Datei '%s'. Fehler: %v editor.add_subdir=Verzeichnis erstellen… -editor.unable_to_upload_files=Fehler beim Hochladen der Dateien nach '%s'. Fehler: %v +editor.unable_to_upload_files=Fehler beim Hochladen der Dateien nach „%s“. Fehler: %v editor.upload_files_to_dir=Dateien hochladen nach '%s' -editor.cannot_commit_to_protected_branch=Commit in den geschützten Branch '%s' ist nicht möglich. +editor.cannot_commit_to_protected_branch=Commit in den geschützten Branch „%s“ ist nicht möglich. -commits.desc=Durchsuche die Quellcode Änderungshistorie. +commits.desc=Durchsuche die Quellcode-Änderungshistorie. commits.commits=Commits commits.search=Commits durchsuchen… commits.find=Suchen @@ -633,7 +633,7 @@ commits.date=Datum commits.older=Älter commits.newer=Neuer commits.signed_by=Signiert von -commits.gpg_key_id=GPG Schlüssel-ID +commits.gpg_key_id=GPG-Schlüssel-ID ext_issues=Externe Issues ext_issues.desc=Link zu externem Issuetracker. @@ -658,7 +658,7 @@ issues.new_label_placeholder=Labelname issues.new_label_desc_placeholder=Beschreibung issues.create_label=Label erstellen issues.label_templates.title=Lade vordefinierte Label -issues.label_templates.info=Es existieren noch keine Labels. Erstelle ein neues Label ("Neues Label") oder verwende das Standard Label-Set: +issues.label_templates.info=Es existieren noch keine Label. Erstelle ein neues Label („Neues Label“) oder verwende das Standard-Label-Set: issues.label_templates.helper=Wähle ein Label issues.label_templates.use=Label-Set verwenden issues.label_templates.fail_to_load_file=Fehler beim Laden der Label Template Datei '%s': %v @@ -676,7 +676,7 @@ issues.delete_branch_at=`löschte die Branch %s %s` issues.open_tab=%d offen issues.close_tab=%d geschlossen issues.filter_label=Label -issues.filter_label_no_select=Alle Labels +issues.filter_label_no_select=Alle Label issues.filter_milestone=Meilenstein issues.filter_milestone_no_select=Alle Meilensteine issues.filter_assignee=Zuständig @@ -768,10 +768,10 @@ issues.cancel_tracking_history=hat die Zeiterfassung %s abgebrochen issues.time_spent_total=Zeitaufwand insgesamt issues.time_spent_from_all_authors=`Aufgewendete Zeit: %s` issues.due_date=Fällig am -issues.invalid_due_date_format=Das Fälligkeitsdatum muss das Format 'JJJJ-MM-TT' haben. +issues.invalid_due_date_format=Das Fälligkeitsdatum muss das Format „JJJJ-MM-TT“ haben. issues.error_modifying_due_date=Fehler beim Ändern des Fälligkeitsdatums. issues.error_removing_due_date=Fehler beim Entfernen des Fälligkeitsdatums. -issues.due_date_form=jjjj-mm-tt +issues.due_date_form=JJJJ-MM-TT issues.due_date_form_add=Fälligkeitsdatum hinzufügen issues.due_date_form_update=Fälligkeitsdatum ändern issues.due_date_form_remove=Fälligkeitsdatum löschen @@ -787,7 +787,7 @@ pulls.new=Neuer Pull-Request pulls.compare_changes=Neuer Pull-Request pulls.compare_changes_desc=Wähle die Ziel- und Quellbranch aus. pulls.compare_base=Ziel -pulls.compare_compare=pull von +pulls.compare_compare=pullen von pulls.filter_branch=Branch filtern pulls.no_results=Keine Ergebnisse verfügbar. pulls.nothing_to_compare=Diese Branches sind identisch. Es muss kein Pull-Request erstellt werden. @@ -827,13 +827,13 @@ milestones.title=Titel milestones.desc=Beschreibung milestones.due_date=Fälligkeitsdatum (optional) milestones.clear=Feld leeren -milestones.invalid_due_date_format=Das Fälligkeitsdatum muss das Format 'JJJJ-MM-TT' haben. -milestones.create_success=Der Meilenstein '%s' wurde erstellt. +milestones.invalid_due_date_format=Das Fälligkeitsdatum muss das Format „JJJJ-MM-TT“ haben. +milestones.create_success=Der Meilenstein „%s“ wurde erstellt. milestones.edit=Meilenstein bearbeiten milestones.edit_subheader=Benutze Meilensteine, um Issues zu organisieren und den Fortschritt darzustellen. milestones.cancel=Abbrechen milestones.modify=Meilenstein bearbeiten -milestones.edit_success=Die Änderungen am Meilenstein "%s" wurden gespeichert. +milestones.edit_success=Die Änderungen am Meilenstein „%s“ wurden gespeichert. milestones.deletion=Meilenstein löschen milestones.deletion_desc=Das Löschen des Meilensteins entfernt ihn von allen Issues. Fortfahren? milestones.deletion_success=Der Meilenstein wurde gelöscht. @@ -849,7 +849,7 @@ ext_wiki.desc=Verweis auf externes Wiki. wiki=Wiki wiki.welcome=Willkommen im Wiki. -wiki.welcome_desc=Im Wiki kannst Dokumentation schreiben und mit Mitarbeitern teilen. +wiki.welcome_desc=Im Wiki kannst du Dokumentation schreiben und sie mit Mitarbeitern teilen. wiki.desc=Schreibe und teile Dokumentation mit Mitarbeitern. wiki.create_first_page=Erstelle die erste Seite wiki.page=Seite @@ -861,9 +861,9 @@ wiki.last_commit_info=%s hat diese Seite bearbeitet %s wiki.edit_page_button=Bearbeiten wiki.new_page_button=Neue Seite wiki.delete_page_button=Seite löschen -wiki.delete_page_notice_1=Das Löschen der Wiki-Seite '%s' kann nicht Rückgängig gemacht werden. Fortfahren? +wiki.delete_page_notice_1=Das Löschen der Wiki-Seite „%s“ kann nicht rückgängig gemacht werden. Fortfahren? wiki.page_already_exists=Eine Wiki-Seite mit dem gleichen Namen existiert bereits. -wiki.reserved_page=Der Wiki-Seitenname "%s" ist reserviert. +wiki.reserved_page=Der Wiki-Seitenname „%s“ ist reserviert. wiki.pages=Seiten wiki.last_updated=Zuletzt aktualisiert %s @@ -911,7 +911,7 @@ activity.published_release_label=Veröffentlicht search=Suchen search.search_repo=Repository durchsuchen -search.results=Suchergebnisse für "%s" in %s +search.results=Suchergebnisse für „%s“ in %s settings=Einstellungen settings.desc=In den Einstellungen kannst du die Einstellungen des Repository anpassen @@ -925,28 +925,28 @@ settings.hooks=Webhooks settings.githooks=Git-Hooks settings.basic_settings=Grundeinstellungen settings.mirror_settings=Mirror Einstellungen -settings.sync_mirror=Jetzt Synchronisieren +settings.sync_mirror=Jetzt synchronisieren settings.mirror_sync_in_progress=Mirror-Synchronisierung wird zurzeit ausgeführt. Komm in ein paar Minuten zurück. settings.site=Webseite settings.update_settings=Einstellungen speichern settings.advanced_settings=Erweiterte Einstellungen -settings.wiki_desc=Repository Wiki aktivieren +settings.wiki_desc=Repository-Wiki aktivieren settings.use_internal_wiki=Eingebautes Wiki verwenden settings.use_external_wiki=Externes Wiki verwenden settings.external_wiki_url=Externe Wiki URL settings.external_wiki_url_error=Die externe Wiki-URL ist ungültig. -settings.external_wiki_url_desc=Besucher werden auf die externe Wiki-URL weitergeleitet wenn sie auf das Wiki-Tab klicken. -settings.issues_desc=Repository Issue-Tracker aktivieren +settings.external_wiki_url_desc=Besucher werden auf die externe Wiki-URL weitergeleitet, wenn sie auf das Wiki-Tab klicken. +settings.issues_desc=Repository-Issue-Tracker aktivieren settings.use_internal_issue_tracker=Integrierten Issue-Tracker verwenden settings.use_external_issue_tracker=Externen Issue-Tracker verwenden settings.external_tracker_url=URL eines externen Issue Trackers settings.external_tracker_url_error=Die URL des externen Issue-Trackers ist ungültig. -settings.external_tracker_url_desc=Besucher werden auf die externe Issue-Tracker-URL weitergeleitet wenn sie auf das Issues-Tab klicken. +settings.external_tracker_url_desc=Besucher werden auf die externe Issue-Tracker-URL weitergeleitet, wenn sie auf das Issues-Tab klicken. settings.tracker_url_format=URL-Format des externen Issue-Systems settings.tracker_issue_style=Namenskonvention des externen Issue-Trackers settings.tracker_issue_style.numeric=Numerisch settings.tracker_issue_style.alphanumeric=Alphanumerisch -settings.tracker_url_format_desc=Du kannst die Platzhalter {user}, {repo}, {index} für den Benutzernamen, den Namen des Repositories und die Issue-Nummer verwenden. +settings.tracker_url_format_desc=Du kannst die Platzhalter {user}, {repo}, {index} für den Benutzernamen, den Namen des Repositorys und die Issue-Nummer verwenden. settings.enable_timetracker=Zeiterfassung aktivieren settings.allow_only_contributors_to_track_time=Nur Mitarbeitern erlauben, die Zeiterfassung zu nutzen settings.pulls_desc=Repository-Pull-Requests aktivieren @@ -964,22 +964,22 @@ settings.convert_notices_1=Dieser Vorgang wandelt das Mirror-Repository in ein n settings.convert_confirm=Repository umwandeln settings.convert_succeed=Das Mirror-Repository wurde erfolgreich in ein normales Repository umgewandelt. settings.transfer=Besitz übertragen -settings.transfer_desc=Übertrage dieses Repository auf einen anderen Benutzer oder eine Organisation in der Du Admin-Rechte hast. -settings.transfer_notices_1=- Du wirst keinen Zugriff mehr haben, wenn der neue Besitzer ein individueller Benutzer ist. -settings.transfer_notices_2=- Du wirst weiterhin Zugriff haben, wenn der neue Besitzer eine Organisation ist und du einer der Besitzer bist. +settings.transfer_desc=Übertrage dieses Repository auf einen anderen Benutzer oder eine Organisation, in der du Admin-Rechte hast. +settings.transfer_notices_1=– Du wirst keinen Zugriff mehr haben, wenn der neue Besitzer ein individueller Benutzer ist. +settings.transfer_notices_2=– Du wirst weiterhin Zugriff haben, wenn der neue Besitzer eine Organisation ist und du einer der Besitzer bist. settings.transfer_form_title=Gib den Repository-Namen zur Bestätigung ein: settings.wiki_delete=Wiki-Daten löschen settings.wiki_delete_desc=Das Löschen von Wiki-Daten kann nicht rückgängig gemacht werden. Bitte sei vorsichtig. -settings.wiki_delete_notices_1=- Dies löscht und deaktiviert das Wiki für %s. +settings.wiki_delete_notices_1=– Dies löscht und deaktiviert das Wiki für %s. settings.confirm_wiki_delete=Wiki-Daten löschen -settings.wiki_deletion_success=Repository Wiki-Daten wurden gelöscht. +settings.wiki_deletion_success=Repository-Wiki-Daten wurden gelöscht. settings.delete=Dieses Repository löschen settings.delete_desc=Wenn dieses Repository gelöscht wurde, gibt es keinen Weg zurück. Bitte sei vorsichtig. settings.delete_notices_1=- Diese Operation kann NICHT rückgängig gemacht werden. -settings.delete_notices_2=- Die Operation wird das %s-Repository dauerhaft löschen, inklusive der Dateien, Issues, Kommentare und Zugriffseinstellungen. -settings.delete_notices_fork_1=- Nach dem Löschen werden alle Forks unabhängig. +settings.delete_notices_2=– Die Operation wird das %s-Repository dauerhaft löschen, inklusive der Dateien, Issues, Kommentare und Zugriffseinstellungen. +settings.delete_notices_fork_1=– Forks dieses Repositorys werden nach dem Löschen unabhängig. settings.deletion_success=Das Repository wurde gelöscht. -settings.update_settings_success=Repository Einstellungen wurden aktualisiert. +settings.update_settings_success=Repository-Einstellungen wurden aktualisiert. settings.transfer_owner=Neuer Besitzer settings.make_transfer=Transfer durchführen settings.transfer_succeed=Das Repository wurde transferiert. @@ -994,7 +994,7 @@ settings.search_user_placeholder=Benutzer suchen… settings.org_not_allowed_to_be_collaborator=Organisationen können nicht als Mitarbeiter hinzugefügt werden. settings.user_is_org_member=Der Benutzer ist ein Organisationsmitglied und kann nicht als Mitarbeiter hinzugefügt werden. settings.add_webhook=Webhook hinzufügen -settings.hooks_desc=Webhooks senden bei bestimmten Gitea-Events automatisch HTTP POST-Requets an einen Server. Lies mehr in unserer Anleitung zu Webhooks (Englisch). +settings.hooks_desc=Webhooks senden bei bestimmten Gitea-Events automatisch „HTTP POST“-Anfragen an einen Server. Lies mehr in unserer Anleitung zu Webhooks (auf Englisch). settings.webhook_deletion=Webhook löschen settings.webhook_deletion_desc=Das Entfernen eines Webhooks löscht seine Einstellungen und Zustellungsverlauf. Fortfahren? settings.webhook_deletion_success=Webhook wurde entfernt. @@ -1066,18 +1066,18 @@ settings.title=Titel settings.deploy_key_content=Inhalt settings.key_been_used=Ein Deploy-Key mit identischem Inhalt wird bereits verwendet. settings.key_name_used=Ein Deploy-Key mit diesem Namen existiert bereits. -settings.add_key_success=Der Deploy-Key '%s' wurde erfolgreich hinzugefügt. +settings.add_key_success=Der Deploy-Key „%s“ wurde erfolgreich hinzugefügt. settings.deploy_key_deletion=Deploy-Key löschen settings.deploy_key_deletion_desc=Nach dem Löschen wird dieser Deploy-Key keinen Zugriff mehr auf dieses Repository haben. Fortfahren? settings.deploy_key_deletion_success=Der Deploy-Key wurde entfernt. settings.branches=Branches -settings.protected_branch=Branch-Protection +settings.protected_branch=Branch-Schutz settings.protected_branch_can_push=Push erlauben? settings.protected_branch_can_push_yes=Du kannst pushen settings.protected_branch_can_push_no=Du kannst nicht pushen -settings.branch_protection=Branch-Schutz" für Branch '%s' +settings.branch_protection=Branch-Schutz für Branch „%s“ settings.protect_this_branch=Brach-Schutz aktivieren -settings.protect_this_branch_desc=Verhindere Löschen und deaktiviere Git force push auf diese Branch. +settings.protect_this_branch_desc=Verhindere Löschen und deaktiviere das sog. „force pushing” von Git auf diesen Branch. settings.protect_whitelist_committers=Push-Whitelist aktivieren settings.protect_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist Push-Beschränkungen zu umgehen. settings.protect_whitelist_users=Nutzer, die pushen dürfen: @@ -1085,17 +1085,17 @@ settings.protect_whitelist_search_users=Benutzer suchen… settings.protect_whitelist_teams=Teams, die pushen dürfen: settings.protect_whitelist_search_teams=Suche nach Teams… settings.protect_merge_whitelist_committers=Merge-Whitelist aktivieren -settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist Pull-Requests in diese Branch zu mergen. +settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist Pull-Requests in diesen Branch zu mergen. settings.protect_merge_whitelist_users=Nutzer, die mergen dürfen: settings.protect_merge_whitelist_teams=Teams, die mergen dürfen: settings.add_protected_branch=Schutz aktivieren settings.delete_protected_branch=Schutz deaktivieren -settings.update_protect_branch_success=Branch-protection für die Branch '%s' wurde geändert. -settings.remove_protected_branch_success=Branch-protection für die Branch '%s' wurde deaktiviert. -settings.protected_branch_deletion=Brach-Schutz deaktivieren -settings.protected_branch_deletion_desc=Wenn du die Branch-Protection deaktivierst, können alle Nutzer mit Schreibrechten auf die Branch pushen. Fortfahren? -settings.default_branch_desc=Wähle eine Standardbranch für Pull-Requests und Code-Commits: -settings.choose_branch=Wähle eine Branch… +settings.update_protect_branch_success=Branch-Schutz für den Branch „%s“ wurde geändert. +settings.remove_protected_branch_success=Branch-Schutz für den Branch „%s“ wurde deaktiviert. +settings.protected_branch_deletion=Branch-Schutz deaktivieren +settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren? +settings.default_branch_desc=Wähle einen Standardbranch für Pull-Requests und Code-Commits: +settings.choose_branch=Wähle einen Branch … settings.no_protected_branch=Es gibt keine geschützten Branches. diff.browse_source=Quellcode durchsuchen @@ -1131,7 +1131,7 @@ release.write=Schreiben release.preview=Vorschau release.loading=Laden… release.prerelease_desc=Als Pre-Release kennzeichnen -release.prerelease_helper=Dieses Release als "ungeeignet für den produktiven Einsatz" markieren. +release.prerelease_helper=Dieses Release als „ungeeignet für den produktiven Einsatz“ markieren. release.cancel=Abbrechen release.publish=Release veröffentlichen release.save_draft=Entwurf speichern @@ -1146,27 +1146,29 @@ release.downloads=Downloads branch.name=Branchname branch.search=Branches durchsuchen -branch.already_exists=Eine Branch mit dem Namen '%s' existiert bereits. +branch.already_exists=Ein Branch mit dem Namen „%s“ existiert bereits. branch.delete_head=Löschen -branch.delete=Branch '%s' löschen +branch.delete=Branch „%s“ löschen branch.delete_html=Branch löschen -branch.delete_desc=Das Löschen einer Branch ist permanent. Es KANN NICHT Rückgängig gemacht werden. Fortfahren? -branch.deletion_success=Branch '%s' wurde gelöscht. -branch.deletion_failed=Branch '%s' konnte nicht gelöscht werden. -branch.delete_branch_has_new_commits=Die Branch '%s' kann nicht gelöscht weden, da seit dem letzten Merge neue Commits hinzugefügt wurden. +branch.delete_desc=Das Löschen eines Branches ist permanent. Es KANN NICHT rückgängig gemacht werden. Fortfahren? +branch.deletion_success=Branch „%s“ wurde gelöscht. +branch.deletion_failed=Branch „%s“ konnte nicht gelöscht werden. +branch.delete_branch_has_new_commits=Der Branch „%s“ kann nicht gelöscht weden, da seit dem letzten Merge neue Commits hinzugefügt wurden. branch.create_branch=Erstelle Branch %s branch.create_from=von '%s' -branch.create_success=Branch '%s' wurde erstellt. +branch.create_success=Branch „%s“ wurde erstellt. branch.branch_already_exists=Branch '%s' existiert bereits in diesem Repository. -branch.branch_name_conflict=Der Branch-Name '%s' steht in Konflikt mit der bestehendem Branch '%s'. -branch.tag_collision=Branch '%s' kann nicht erstellt werden, da in diesem Repository bereits ein Tag mit dem selben Namen existiert. +branch.branch_name_conflict=Der Branch-Name „%s“ steht in Konflikt mit dem bestehenden Branch „%s“. +branch.tag_collision=Branch „%s“ kann nicht erstellt werden, da in diesem Repository bereits ein Tag mit dem selben Namen existiert. branch.deleted_by=Von %s gelöscht -branch.restore_success=Branch '%s' wurde wiederhergestellt. -branch.restore_failed=Wiederherstellung der Branch '%s' fehlgeschlagen. -branch.protected_deletion_failed=Branch '%s' ist geschützt und kann nicht gelöscht werden. +branch.restore_success=Branch „%s“ wurde wiederhergestellt. +branch.restore_failed=Wiederherstellung des Branches „%s“ fehlgeschlagen. +branch.protected_deletion_failed=Branch „%s“ ist geschützt und kann nicht gelöscht werden. topic.manage_topics=Themen verwalten topic.done=Fertig +topic.count_prompt=Du kannst nicht mehr als 25 Themen auswählen +topic.format_prompt=Themen müssen mit einem Buchstaben oder einer Zahl beginnen. Sie können Bindestriche (-) enthalten und dürfen nicht länger als 35 Zeichen sein [org] org_name_holder=Name der Organisation @@ -1188,9 +1190,9 @@ team_desc_helper=Beschreibe den Zweck oder die Rolle des Teams. team_permission_desc=Berechtigungen team_unit_desc=Zugriff auf Repositorybereiche erlauben -form.name_reserved=Der Organisationsname '%s' ist reserviert. -form.name_pattern_not_allowed=Das Muster '%s' ist in Organisationsnamen nicht erlaubt. -form.create_org_not_allowed=Du bist nicht berechtigt eine Organisation zu erstellen. +form.name_reserved=Der Organisationsname „%s“ ist reserviert. +form.name_pattern_not_allowed=Das Muster „%s“ ist in Organisationsnamen nicht erlaubt. +form.create_org_not_allowed=Du bist nicht berechtigt, eine Organisation zu erstellen. settings=Einstellungen settings.options=Organisation @@ -1229,7 +1231,7 @@ teams.read_access_helper=Mitglieder können Teamrepositories ansehen und klonen. teams.write_access=Schreibzugriff teams.write_access_helper=Mitglieder können Teamrepositories ansehen und auf sie pushen. teams.admin_access=Administratorzugang -teams.admin_access_helper=Mitglieder können auf Team Repositories "pushen", von ihnen "pullen" und Mitarbeiter hinzufügen. +teams.admin_access_helper=Mitglieder können auf Team-Repositorys pushen, von ihnen pullen und Mitarbeiter hinzufügen. teams.no_desc=Dieses Team hat keine Beschreibung teams.settings=Einstellungen teams.owners_permission_desc=Besitzer haben vollen Zugriff auf alle Repositories und Admin-Rechte für diese Organisation. @@ -1238,7 +1240,7 @@ teams.update_settings=Einstellungen aktualisieren teams.delete_team=Team löschen teams.add_team_member=Teammitglied hinzufügen teams.delete_team_title=Team löschen -teams.delete_team_desc=Das Löschen eines Teams wiederruft den Repository-Zugriff für seine Mitglieder. Fortfahren? +teams.delete_team_desc=Das Löschen eines Teams widerruft den Repository-Zugriff für seine Mitglieder. Fortfahren? teams.delete_team_success=Das Team wurde gelöscht. teams.read_permission_desc=Dieses Team hat Lesezugriff: Mitglieder können Team-Repositories einsehen und klonen. teams.write_permission_desc=Dieses Team hat Schreibzugriff: Mitglieder können Team-Repositories einsehen und darauf pushen. @@ -1277,12 +1279,12 @@ dashboard.delete_repo_archives=Alle Repository-Archive löschen dashboard.delete_repo_archives_success=Alle Repository-Archive wurden gelöscht. dashboard.delete_missing_repos=Alle Repository-Datensätze mit verlorenen gegangenen Git-Dateien löschen dashboard.delete_missing_repos_success=Alle Repository-Datensätze mit verlorenen Git-Dateien wurden gelöscht. -dashboard.git_gc_repos=Garbage Collection auf Repositories ausführen -dashboard.git_gc_repos_success=Alle Repositories haben Garbage Collection beendet. -dashboard.resync_all_sshkeys='.ssh/authorized_keys'-Datei mit Gitea SSH-Keys neu schreiben. (Wenn Du den eingebauten SSH Server nutzt, musst du das nicht ausführen.) +dashboard.git_gc_repos=Garbage-Collection auf Repositories ausführen +dashboard.git_gc_repos_success=Alle Repositories haben Garbage-Collection beendet. +dashboard.resync_all_sshkeys=„.ssh/authorized_keys“-Datei mit Gitea-SSH-Keys neu schreiben. (Wenn Du den eingebauten SSH-Server nutzt, musst du das nicht ausführen.) dashboard.resync_all_sshkeys_success=Alle von Gitea verwalteten öffentlichen Schlüssel wurden neu geschrieben. -dashboard.resync_all_hooks=Synchronisiere pre-receive, update und post-receive Hooks für alle Repositories. -dashboard.resync_all_hooks_success=Alle pre-receive, update und post-receive Repository-Hooks wurden synchronisiert. +dashboard.resync_all_hooks=Synchronisiere „pre-receive“-, „update“- und „post-receive“-Hooks für alle Repositorys erneut. +dashboard.resync_all_hooks_success=Alle „pre-receive“-, „update“- und „post-receive“-Repository-Hooks wurden erneut synchronisiert. dashboard.reinit_missing_repos=Alle Git-Repositories mit Einträgen neu einlesen dashboard.reinit_missing_repos_success=Alle verlorenen Git-Repositories mit existierenden Einträgen wurden erfolgreich aktualisiert. dashboard.sync_external_users=Externe Benutzerdaten synchronisieren @@ -1305,11 +1307,11 @@ dashboard.heap_memory_released=Freigegebener Heap-Memory dashboard.heap_objects=Heap-Objekte dashboard.bootstrap_stack_usage=Bootstrap-Stack-Auslastung dashboard.stack_memory_obtained=Erhaltener Stack-Memory -dashboard.mspan_structures_usage=MSpan-Structures Auslastung -dashboard.mspan_structures_obtained=MSpan-Structures erhalten -dashboard.mcache_structures_usage=MCache-Structures Auslastung +dashboard.mspan_structures_usage=MSpan-Structures-Auslastung +dashboard.mspan_structures_obtained=Erhaltene MSpan-Structures +dashboard.mcache_structures_usage=MCache-Structures-Auslastung dashboard.mcache_structures_obtained=Erhaltene MCache-Structures -dashboard.profiling_bucket_hash_table_obtained=Analysesatz Hashtabellen erhalten +dashboard.profiling_bucket_hash_table_obtained=Erhaltene Analysesatz-Hashtabellen dashboard.gc_metadata_obtained=Erhaltene GC-Metadata dashboard.other_system_allocation_obtained=Andere erhaltene System-Allokationen dashboard.next_gc_recycle=Nächster GC-Zyklus @@ -1342,7 +1344,7 @@ users.max_repo_creation_desc=(Gib -1 ein, um das globale Standardlimit zu verwen users.is_activated=Account ist aktiviert users.prohibit_login=Anmelden deaktivieren users.is_admin=Ist Administrator -users.allow_git_hook=Darf "Git Hooks" erstellen +users.allow_git_hook=Darf „Git Hooks“ erstellen users.allow_import_local=Darf lokale Repositories importieren users.allow_create_organization=Darf Organisationen erstellen users.update_profile=Benutzerkonto aktualisieren @@ -1384,31 +1386,31 @@ auths.bind_dn=DN binden auths.bind_password=Passwort binden auths.bind_password_helper=Achtung: Das Passwort wird im Klartext gespeichert. Benutze wenn möglich einen Account mit nur Lesezugriff. auths.user_base=Basis für Benutzersuche -auths.user_dn=Benutzer DN -auths.attribute_username=Benutzername Attribut +auths.user_dn=Benutzer-DN +auths.attribute_username=Benutzernamens-Attribut auths.attribute_username_placeholder=Leerlassen, um den in Gitea eingegebenen Benutzernamen zu verwenden. auths.attribute_name=Vornamensattribut auths.attribute_surname=Nachnamensattribut -auths.attribute_mail=E-Mail Attribut -auths.attribute_ssh_public_key=Öffentliches SSH-Schlüssel Attribut +auths.attribute_mail=E-Mail-Attribut +auths.attribute_ssh_public_key=Öffentlicher-SSH-Schlüssel-Attribut auths.attributes_in_bind=Hole Attribute im Bind-Kontext auths.use_paged_search=Seitensuche verwenden auths.search_page_size=Seitengröße auths.filter=Benutzerfilter -auths.admin_filter=Admin Filter -auths.ms_ad_sa=MS AD Suchattribute +auths.admin_filter=Admin-Filter +auths.ms_ad_sa=MS-AD-Suchattribute auths.smtp_auth=SMTP-Authentifizierungstyp auths.smtphost=SMTP-Host auths.smtpport=SMTP-Port auths.allowed_domains=Erlaubte Domains -auths.allowed_domains_helper=Leerlassen, um alle Domains zuzulassen. Trenne mehrere Domänen mit einem Komma (','). +auths.allowed_domains_helper=Leerlassen, um alle Domains zuzulassen. Trenne mehrere Domänen mit einem Komma („,“). auths.enable_tls=TLS-Verschlüsselung aktivieren -auths.skip_tls_verify=TLS Verifikation überspringen -auths.pam_service_name=PAM Dienstname -auths.oauth2_provider=OAuth2 Anbieter +auths.skip_tls_verify=TLS-Verifikation überspringen +auths.pam_service_name=PAM-Dienstname +auths.oauth2_provider=OAuth2-Anbieter auths.oauth2_clientID=Client-ID (Schlüssel) auths.oauth2_clientSecret=Client-Secret -auths.openIdConnectAutoDiscoveryURL=OpenID Connect Auto Discovery URL +auths.openIdConnectAutoDiscoveryURL=OpenID-Connect-Auto-Discovery-URL auths.oauth2_use_custom_url=Benutzerdefinierte URLs anstelle von Standard-URLs verwenden auths.oauth2_tokenURL=Token-URL auths.oauth2_authURL=Authorisierungs-URL @@ -1416,48 +1418,48 @@ auths.oauth2_profileURL=Profil-URL auths.oauth2_emailURL=E-Mail-URL auths.enable_auto_register=Automatische Registrierung aktivieren auths.tips=Tipps -auths.tips.oauth2.general=OAuth2 Authentifizierung -auths.tips.oauth2.general.tip=Beim Registrieren einer neuen OAuth2 Authentifizierung sollte die Callback/Weiterleitungs-URL /user/oauth2//callback sein. -auths.tip.oauth2_provider=OAuth2 Anbieter -auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter https://bitbucket.org/account/user//oauth-consumers/new und füge die Berechtigung "Account"-"Read" hinzu. +auths.tips.oauth2.general=OAuth2-Authentifizierung +auths.tips.oauth2.general.tip=Beim Registrieren einer neuen OAuth2-Authentifizierung sollte die Callback-/Weiterleitungs-URL „/user/oauth2//callback“ sein. +auths.tip.oauth2_provider=OAuth2-Anbieter +auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter https://bitbucket.org/account/user//oauth-consumers/new und füge die Berechtigung „Account“ – „Read“ hinzu. auths.tip.dropbox=Erstelle eine neue App auf https://www.dropbox.com/developers/apps. -auths.tip.facebook=Erstelle eine neue Anwendung auf https://developers.facebook.com/apps und füge das Produkt "Facebook Login" hinzu. -auths.tip.github=Erstelle unter https://github.com/settings/applications/new eine neue OAuth Anwendung. +auths.tip.facebook=Erstelle eine neue Anwendung auf https://developers.facebook.com/apps und füge das Produkt „Facebook Login“ hinzu. +auths.tip.github=Erstelle unter https://github.com/settings/applications/new eine neue OAuth-Anwendung. auths.tip.gitlab=Erstelle unter https://gitlab.com/profile/applications eine neue Anwendung. -auths.tip.google_plus=Du erhältst die OAuth2 Client Zugangsdaten in der Google API Console unter https://console.developers.google.com/ +auths.tip.google_plus=Du erhältst die OAuth2-Client-Zugangsdaten in der Google-API-Konsole unter https://console.developers.google.com/ auths.tip.openid_connect=Benutze die OpenID Connect Discovery URL (/.well-known/openid-configuration) als Endpunkt. -auths.tip.twitter=Gehe auf https://dev.twitter.com/apps, erstelle eine Anwendung und stelle sicher, dass die Option “Allow this application to be used to Sign in with Twitter” aktiviert ist +auths.tip.twitter=Gehe auf https://dev.twitter.com/apps, erstelle eine Anwendung und stelle sicher, dass die Option „Allow this application to be used to Sign in with Twitter“ aktiviert ist auths.edit=Authentifikationsquelle bearbeiten auths.activated=Diese Authentifikationsquelle ist aktiviert -auths.new_success=Die Authentifizierung "%s" wurde hinzugefügt. +auths.new_success=Die Authentifizierung „%s“ wurde hinzugefügt. auths.update_success=Diese Authentifizierungsquelle wurde aktualisiert. auths.update=Authentifizierungsquelle aktualisieren auths.delete=Authentifikationsquelle löschen auths.delete_auth_title=Authentifizierungsquelle löschen auths.delete_auth_desc=Das Löschen einer Authentifizierungsquelle verhindert, dass Benutzer sich darüber anmelden können. Fortfahren? auths.still_in_used=Diese Authentifizierungsquelle wird noch verwendet. Bearbeite oder lösche zuerst alle Benutzer, die diese Authentifizierungsquelle benutzen. -auths.deletion_success=Die Authentifizierungsquelle '%s' wurde gelöscht. -auths.login_source_exist=Die Authentifizierungsquelle '%s' existiert bereits. +auths.deletion_success=Die Authentifizierungsquelle „%s“ wurde gelöscht. +auths.login_source_exist=Die Authentifizierungsquelle „%s“ existiert bereits. config.server_config=Serverkonfiguration config.app_name=Seitentitel -config.app_ver=Gitea Version -config.app_url=Gitea Basis-URL +config.app_ver=Gitea-Version +config.app_url=Gitea-Basis-URL config.custom_conf=Konfigurations-Datei-Pfad -config.domain=SSH Server-Domain +config.domain=SSH-Server-Domain config.offline_mode=Lokaler Modus config.disable_router_log=Router-Log deaktivieren config.run_user=Ausführen als config.run_mode=Laufzeit-Modus -config.git_version=Git Version -config.repo_root_path=Repository-Verzeichnis +config.git_version=Git-Version +config.repo_root_path=Repository-Wurzelpfad config.lfs_root_path=LFS-Wurzelpfad config.static_file_root_path=Verzeichnis für statische Dateien config.log_file_root_path=Logdateipfad config.script_type=Skript-Typ config.reverse_auth_user=Nutzer bei Reverse-Authentifizierung -config.ssh_config=SSH Konfiguration +config.ssh_config=SSH-Konfiguration config.ssh_enabled=Aktiviert config.ssh_start_builtin_server=Eingebauten Server verwenden config.ssh_domain=Server-Domain @@ -1465,9 +1467,9 @@ config.ssh_port=Port config.ssh_listen_port=Listen-Port config.ssh_root_path=Wurzelverzeichnis config.ssh_key_test_path=Schlüssel-Test-Pfad -config.ssh_keygen_path=Keygen ('ssh-keygen') Pfad +config.ssh_keygen_path=Keygen-Pfad („ssh-keygen“) config.ssh_minimum_key_size_check=Prüfung der Mindestschlüssellänge -config.ssh_minimum_key_sizes=Minimale Schlüssellängen +config.ssh_minimum_key_sizes=Mindestschlüssellängen config.db_config=Datenbankkonfiguration config.db_type=Typ @@ -1481,17 +1483,17 @@ config.service_config=Service-Konfiguration config.register_email_confirm=E-Mail-Bestätigung benötigt zum Registrieren config.disable_register=Selbstegistrierung deaktivieren config.allow_only_external_registration=Registrierung nur über externe Services aktiveren -config.enable_openid_signup=OpenID Selbstregistrierung aktivieren -config.enable_openid_signin=OpenID Anmeldung aktivieren +config.enable_openid_signup=OpenID-Selbstregistrierung aktivieren +config.enable_openid_signin=OpenID-Anmeldung aktivieren config.show_registration_button=Schaltfläche zum Registrieren anzeigen config.require_sign_in_view=Seiten nur für angemeldete Benutzer zugänglich config.mail_notify=E-Mail-Benachrichtigungen aktivieren config.disable_key_size_check=Prüfung der Mindestschlüssellänge deaktiveren config.enable_captcha=CAPTCHA aktivieren -config.active_code_lives=Aktivierungscode Lebensdauer +config.active_code_lives=Aktivierungscode-Lebensdauer config.reset_password_code_lives=Ablaufdatum des Passworts zurücksetzen config.default_keep_email_private=E-Mail-Adressen standardmäßig verbergen -config.default_allow_create_organization=Erstellen von Organisationen standarmäßig erlauben +config.default_allow_create_organization=Erstellen von Organisationen standardmäßig erlauben config.enable_timetracking=Zeiterfassung aktivieren config.default_enable_timetracking=Zeiterfassung standardmäßig aktivieren config.default_allow_only_contributors_to_track_time=Nur Mitarbeitern erlauben, die Zeiterfassung zu nutzen @@ -1500,11 +1502,11 @@ config.no_reply_address=Versteckte E-Mail-Domain config.webhook_config=Webhook-Konfiguration config.queue_length=Warteschlangenlänge config.deliver_timeout=Zeitlimit für Zustellung -config.skip_tls_verify=TLS Verifikation überspringen +config.skip_tls_verify=TLS-Verifikation überspringen -config.mailer_config=SMTP Mailer Konfiguration +config.mailer_config=SMTP-Mailer-Konfiguration config.mailer_enabled=Aktiviert -config.mailer_disable_helo=HELO Deaktivieren +config.mailer_disable_helo=HELO deaktivieren config.mailer_name=Name config.mailer_host=Host config.mailer_user=Benutzer @@ -1512,8 +1514,8 @@ config.mailer_use_sendmail=Sendmail benutzen config.mailer_sendmail_path=Sendmail-Pfad config.mailer_sendmail_args=Zusätzliche Argumente für Sendmail config.send_test_mail=Test-E-Mail senden -config.test_mail_failed=Das Senden der Test-E-Mail an '%s' ist fehlgeschlagen: %v -config.test_mail_sent=Eine Test-E-Mail wurde an '%s' gesendet. +config.test_mail_failed=Das Senden der Test-E-Mail an „%s“ ist fehlgeschlagen: %v +config.test_mail_sent=Eine Test-E-Mail wurde an „%s“ gesendet. config.oauth_config=OAuth-Konfiguration config.oauth_enabled=Aktiviert @@ -1533,16 +1535,16 @@ config.session_life_time=Session-Lebensdauer config.https_only=Nur HTTPS config.cookie_life_time=Cookie-Lebensdauer -config.picture_config=Avatar-Konfiguration +config.picture_config=Bild-und-Profilbild-Konfiguration config.picture_service=Bilderservice config.disable_gravatar=Gravatar deaktivieren config.enable_federated_avatar=Föderierte Profilbilder einschalten -config.git_config=Git Konfiguration -config.git_disable_diff_highlight=Diff Syntaxhervorhebung ausschalten -config.git_max_diff_lines=Max Diff Zeilen (in einer Datei) -config.git_max_diff_line_characters=Max Diff Zeichen (in einer Zeile) -config.git_max_diff_files=Max Diff Dateien (Anzeige) +config.git_config=Git-Konfiguration +config.git_disable_diff_highlight=Diff-Syntaxhervorhebung ausschalten +config.git_max_diff_lines=Max. Diff-Zeilen (in einer Datei) +config.git_max_diff_line_characters=Max. Diff-Zeichen (in einer Zeile) +config.git_max_diff_files=Max. Diff-Dateien (Angezeigte) config.git_gc_args=GC-Argumente config.git_migrate_timeout=Zeitlimit für Migration config.git_mirror_timeout=Zeitlimit für Mirror-Aktualisierung @@ -1638,12 +1640,12 @@ mark_all_as_read=Alle als gelesen markieren [gpg] error.extract_sign=Die Signatur konnte nicht extrahiert werden error.generate_hash=Es konnte kein Hash vom Commit generiert werden -error.no_committer_account=Es ist kein Benutzerkonto mit dieser Commiter-Email verbunden +error.no_committer_account=Es ist kein Benutzerkonto mit der E-Mail-Adresse des Committers verbunden error.no_gpg_keys_found=Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden error.not_signed_commit=Kein signierter Commit error.failed_retrieval_gpg_keys=Fehler beim Abrufen eines Keys des Commiter-Kontos [units] -error.no_unit_allowed_repo=Du hast keine Berechtigung auf einen Bereich dieses Repositories zuzugreifen. -error.unit_not_allowed=Du hast keine Berechtigung auf diesen Repository-Bereich zuzugreifen. +error.no_unit_allowed_repo=Du hast keine Berechtigung, um auf irgendeinen Bereich dieses Repositories zuzugreifen. +error.unit_not_allowed=Du hast keine Berechtigung, um auf diesen Repository-Bereich zuzugreifen. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index e510e9954..21ae775e4 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -213,7 +213,7 @@ send_reset_mail = Click here to resend your password reset email reset_password = Reset Your Password invalid_code = Your confirmation code is invalid or has expired. reset_password_helper = Click here to reset your password -password_too_short = Password length cannot be less then %d. +password_too_short = Password length cannot be less than %d characters. non_local_account = Non-local users can not update their password through the Gitea web interface. verify = Verify scratch_code = Scratch code @@ -1167,6 +1167,8 @@ branch.protected_deletion_failed = Branch '%s' is protected. It cannot be delete topic.manage_topics = Manage Topics topic.done = Done +topic.count_prompt = You can't select more than 25 topics +topic.format_prompt = Topics must start with a letter or number, can include hyphens(-) and must be no more than 35 characters long [org] org_name_holder = Organization Name diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 5f05cce65..cb9c47a2b 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -96,7 +96,6 @@ email_not_associate=Esta dirección de correo electrónico no esta asociada a ni send_reset_mail=Haz clic aquí para reenviar tu email de restauración de contraseña reset_password=Restablecer su contraseña reset_password_helper=Haga Clic aquí para restablecer su contraseña -password_too_short=La longitud de la contraseña no puede ser menor a %d. verify=Verificar twofa_scratch_used=Ya has utilizado el código. Has sido redirigido a la página de configuración de dos factores poder remover la inscripción del dispositivo o generar un nuevo código. twofa_scratch_token_incorrect=El código cero es incorrecto. diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index aefe26919..0d49ff4b3 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -82,7 +82,6 @@ has_unconfirmed_mail=Hei %s, sinulla on varmistamaton sähköposti osoite (%s resend_mail=Klikkaa tästä uudelleenlähettääksesi aktivointi sähköpostisi reset_password=Nollaa salasanasi reset_password_helper=Klikkaa tästä nollataksesi salasanasi -password_too_short=Salasanan pituus ei voi olla vähemmän kuin %d merkkiä. verify=Vahvista [mail] diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 91961719e..50e90b96f 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -97,7 +97,6 @@ email_not_associate=L'adresse e-mail n'est associée à aucun compte. send_reset_mail=Cliquez ici pour renvoyer le mail de réinitialisation de votre mot de passe reset_password=Réinitialiser le mot de passe reset_password_helper=Cliquez ici pour réinitialiser votre mot de passe -password_too_short=Le mot de passe doit contenir %d caractères minimum. verify=Vérifier scratch_code=Code de secours use_scratch_code=Utiliser un code de secours diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index b52f8f840..7693a4312 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -101,7 +101,6 @@ email_not_associate=Az email cím nincsen hozzárendelve egyetlen fiókhoz sem. send_reset_mail=Kattints ide hogy újraküldd a jelszó visszaállító emailt reset_password=Jelszó visszaállítása reset_password_helper=Kattintson ide, hogy visszaállítsa a jelszavát -password_too_short=A jelszó nem lehet rövidebb, mint %d karakter. verify=Ellenőrzés scratch_code=Kaparós kód use_scratch_code=Kaparós kód használata diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index b6999654f..56f14d771 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -97,7 +97,6 @@ email_not_associate=Alamat surel tidak terhubung dengan akun apapun. send_reset_mail=Klik di sini untuk mengirim ulang surel pengaturan ulang kata sandi reset_password=Atur Ulang Kata Sandi Anda reset_password_helper=Klik di sini untuk mengatur ulang kata sandi anda -password_too_short=Panjang kata sandi tidak boleh kurang dari %d. verify=Verifikasi scratch_code=Kode coretan use_scratch_code=Gunakan kode coretan diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 9ea365639..e3b752851 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -97,7 +97,6 @@ email_not_associate=L'indirizzo email non è associato ad alcuna conta. send_reset_mail=Clicca qui per inviare nuovamente la tua email di reimpostazione della password reset_password=Reimposta la tua Password reset_password_helper=Clicca qui per reimpostare la password -password_too_short=La lunghezza della password non può essere meno %d caratteri. verify=Verifica scratch_code=Codice Gratta e Vinci use_scratch_code=Utilizza un codice di zero diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 3c422034e..ea32ad3c3 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -101,7 +101,6 @@ email_not_associate=このEメールアドレスは、どのアカウントに send_reset_mail=パスワードリセットメールを再送するにはここをクリックしてください reset_password=パスワードリセット reset_password_helper=パスワードをリセットするにはここをクリック -password_too_short=%d文字未満のパスワードは設定できません。 verify=確認 scratch_code=スクラッチコード use_scratch_code=スクラッチコードを使う diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index cd4528ffb..6497467f4 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -97,7 +97,6 @@ email_not_associate=이 이메일 주소로 등록된 계정이 없습니다. send_reset_mail=여기를 눌러 비밀번호 초기화 메일을 재전송 reset_password=비밀번호 초기화 reset_password_helper=이곳을 눌러 비밀번호를 재설정 -password_too_short=비밀번호의 길이는 %d글자 미만일 수 없습니다. verify=확인 scratch_code=스크래치 코드 use_scratch_code=스크래치 코드 사용 diff --git a/options/locale/locale_lt-LT.ini b/options/locale/locale_lt-LT.ini index 8a9f067ab..e12afe377 100644 --- a/options/locale/locale_lt-LT.ini +++ b/options/locale/locale_lt-LT.ini @@ -75,7 +75,6 @@ active_your_account=Aktyvinkite savo paskyrą resend_mail=Spauskite čia norėdami persiųsti aktyvacijos laišką reset_password=Atstatyti slaptažodį reset_password_helper=Paspauskite čia norėdami pakeisti savo slaptažodį -password_too_short=Slaptažodis negali būti trumpesnis nei %d simbolių. verify=Patikrinti [mail] diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 656910dc8..1b0129ba1 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -54,13 +54,14 @@ password=Parole db_name=Datu bāzes nosaukums path=Ceļš -repo_path=Repozitoriju glabāšanas vieta -log_root_path=Žurnalizēšanas direktorija +repo_path=Repozitoriju glabāšanas ceļš +log_root_path=Žurnalizēšanas ceļš optional_title=Neobligātie iestatījumi smtp_host=SMTP resursdators federated_avatar_lookup_popup=Iespējot apvienoto profila bilžu meklētāju, lai izmantotu atvērtā koda apvienoto servisu balstītu uz libravatar. openid_signin=Iespējot OpenID autorizāciju +openid_signin_popup=Iespējot lietotāju autorizāciju ar OpenID. enable_captcha_popup=Lietotājam reģistrējoties, pieprasīt ievadīt drošības kodu. admin_password=Parole confirm_password=Apstipriniet paroli @@ -101,7 +102,6 @@ email_not_associate=Šī e-pasta adrese nav saistīta ar nevienu 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 reset_password_helper=Nospiediet šeit, lai atjaunotu paroli -password_too_short=Paroles garums nedrīkst būt mazāks par %d. verify=Pārbaudīt scratch_code=Vienreizējais kods use_scratch_code=Izmantot vienreizējo kodu diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 6b96b91de..64db50357 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -101,7 +101,6 @@ email_not_associate=Dit emailadres is niet gekoppeld aan een account. send_reset_mail=Klik hier om de wachtwoord reset mail (nogmaals) te versturen reset_password=Reset uw wachtwoord reset_password_helper=Klik hier om uw wachtwoord opnieuw in te stellen. -password_too_short=De lengte van uw wachtwoord moet minimaal %d karakters zijn. verify=Verifiëren scratch_code=Eenmalige code use_scratch_code=Gebruik een eenmalige code diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 954a37f5f..3dcb05082 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -97,7 +97,6 @@ email_not_associate=Adres e-mail nie jest powiązany z żadnym kontem. send_reset_mail=Kliknij tutaj, aby ponownie wysłać e-mail resetowania hasła reset_password=Resetowanie hasła reset_password_helper=Kliknij tutaj, aby zresetować hasło -password_too_short=Długość hasła nie może być mniejsza niż %d znaków. verify=Potwierdź scratch_code=Kod jednorazowy use_scratch_code=Użyj kodu jednorazowego diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 17b59f7b1..f2ec96dd0 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -213,7 +213,7 @@ send_reset_mail=Clique aqui para re-enviar seu e-mail de redefinição de senha reset_password=Redefinir sua senha invalid_code=Seu código de confirmação é inválido ou expirou. reset_password_helper=Clique aqui para redefinir sua senha -password_too_short=O comprimento da senha não pode ser menor que %d. +password_too_short=A senha deve ter %d ou mais caracteres. non_local_account=Usuários não-locais não podem atualizar sua senha através da interface web do Gitea. verify=Verificar scratch_code=Código de backup @@ -1167,6 +1167,8 @@ branch.protected_deletion_failed=A branch '%s' está protegida. Ela não pode se topic.manage_topics=Gerenciar Tópicos topic.done=Feito +topic.count_prompt=Você não pode selecionar mais de 25 tópicos +topic.format_prompt=Tópicos devem começar com uma letra ou um número, podem incluir hífens (-) e não devem ter mais de 35 caracteres [org] org_name_holder=Nome da organização diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index a8cf3098b..9df7e4e16 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -213,7 +213,7 @@ send_reset_mail=Нажмите сюда, чтобы отправить пись reset_password=Сброс пароля invalid_code=Этот код подтверждения недействителен или истек. reset_password_helper=Нажмите здесь, чтобы сбросить свой пароль -password_too_short=Длина пароля не менее %d символов. +password_too_short=Длина пароля не может быть меньше, чем %d символов. non_local_account=Нелокальные аккаунты не могут изменить пароль через Gitea. verify=Проверить scratch_code=Одноразовый пароль @@ -490,7 +490,7 @@ delete_account_desc=Вы уверены, что хотите навсегда у [repo] owner=Владелец repo_name=Имя репозитория -repo_name_helper=Лучшие названия репозиториев состоят их коротких, легко запоминаемых и уникальных ключевых слов. +repo_name_helper=Лучшие названия репозиториев состоят из коротких, легко запоминаемых и уникальных ключевых слов. visibility=Видимость visiblity_helper=Сделать репозиторий приватным visiblity_helper_forced=Администратор сайта настроил параметр видимости новых репозиториев. Репозиторий приватный по умолчанию. @@ -1167,6 +1167,8 @@ branch.protected_deletion_failed=Ветка '%s' защищена. Её нель topic.manage_topics=Редактировать тематические метки topic.done=Сохранить +topic.count_prompt=Вы не можете выбрать более 25 тем +topic.format_prompt=Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов [org] org_name_holder=Название организации diff --git a/options/locale/locale_sr-SP.ini b/options/locale/locale_sr-SP.ini index 3acc22c3a..e20c9ba91 100644 --- a/options/locale/locale_sr-SP.ini +++ b/options/locale/locale_sr-SP.ini @@ -79,7 +79,6 @@ has_unconfirmed_mail=Здраво, %s! Имате непотврђену адр resend_mail=Кликните овде да поново пошаљете писмо reset_password=Ресет лозинке reset_password_helper=Кликните овде да ресетујете вашу лозинку -password_too_short=Лозинка неможе бити краћа од %d карактера. [mail] activate_account=Молимо вас активирајте ваш налог diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index e6bb938a5..ca3d703f1 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -97,7 +97,6 @@ email_not_associate=Denna e-postadress är inte knutet till något konto. send_reset_mail=Klicka här för att skicka e-post med lösenordsåterställning igen reset_password=Återställ ditt lösenord reset_password_helper=Klicka här för att återställa ditt lösenord -password_too_short=Lösenordet får ej vara kortare än %d tecken. verify=Verifiera scratch_code=Skrapkod use_scratch_code=Använd en skrapkod diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index f3ed0f727..17f4fe02b 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -97,7 +97,6 @@ email_not_associate=Bu e-posta adresi hiçbir hesap ile ilişkilendirilmemiştir send_reset_mail=Parola sıfırlama e-postasını (yeniden) göndermek için buraya tıklayın reset_password=Parolanızı Sıfırlayın reset_password_helper=Parolanızı sıfırlamak için buraya tıklayın -password_too_short=Parola uzunluğu %d karakterden az olamaz. verify=Doğrula scratch_code=Çizgi kodu use_scratch_code=Bir çizgi kodu kullanınız diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 4f4fda0de..14d6335d8 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1,4 +1,4 @@ -app_desc=Зручний сервіс, власного Git хостінгу +app_desc=Зручний сервіс, власного Git хостингу home=Головна dashboard=Панель управління @@ -38,7 +38,10 @@ u2f_use_twofa=Використовуйте дво-факторний код з u2f_error=Неможливо прочитати ваш ключ безпеки! u2f_unsupported_browser=Ваш браузер не підтримує U2F ключі. Будь ласка, спробуйте інший браузер. u2f_error_1=Сталася невідома помилка. Спробуйте ще раз. +u2f_error_2=Переконайтеся, що ви використовуєте зашифроване з'єднання (https://) та відвідуєте правильну URL-адресу. u2f_error_3=Сервер не може обробити, ваш запит. +u2f_error_4=Представлений ключ не дає право на цей запит. Якщо спробуєте зареєструвати його, переконайтеся, що ключ ще не зареєстровано. +u2f_error_5=Таймаут досягнуто до того, як ваш ключ можна буде прочитати. Перезавантажте, щоб повторити спробу. u2f_reload=Оновити repository=Репозиторій @@ -67,12 +70,12 @@ activities=Дії pull_requests=Запити на злиття issues=Проблеми -cancel=Відміна +cancel=Відмінити [install] install=Встановлення title=Початкова конфігурація -docker_helper=Якщо ви запускаєте Gitea всередині Docker, будь ласка уважно прочитайте документацію перед тим, як що-небудь змінити на цій сторінці. +docker_helper=Якщо ви запускаєте Gitea всередині Docker, будь ласка уважно прочитайте документацію перед тим, як щось змінити на цій сторінці. requite_db_desc=Gitea потребує MySQL, PostgreSQL, MSSQL, SQLite3 або TiDB. db_title=Налаштування бази даних db_type=Тип бази даних @@ -104,6 +107,7 @@ ssh_port_helper=Номер порту, який використовує SSH с http_port=Gitea HTTP порт http_port_helper=Номер порту, який буде прослуховуватися Giteas веб-сервером. app_url=Базова URL-адреса Gitea +app_url_helper=Базова адреса для HTTP(S) клонування через URL та повідомлень електронної пошти. log_root_path=Шлях до лог файлу log_root_path_helper=Файли журналу будуть записані в цей каталог. @@ -111,6 +115,7 @@ optional_title=Додаткові налаштування email_title=Налаштування Email smtp_host=SMTP хост smtp_from=Відправляти Email від імені +smtp_from_helper=Електронна пошта для використання в Gіtea. Введіть звичайну електронну адресу або використовуйте формат: "Ім'я" . mailer_user=SMTP Ім'я кристувача mailer_password=SMTP Пароль register_confirm=Потрібно підтвердити електронну пошту для реєстрації @@ -124,9 +129,11 @@ federated_avatar_lookup=Увімкнути федеративні аватари federated_avatar_lookup_popup=Увімкнути зовнішний Аватар за допомогою Libravatar. disable_registration=Вимкнути самостійну реєстрацію disable_registration_popup=Вимкнути самостійну реєстрацію користувачів, тільки адміністратор може створювати нові облікові записи. +allow_only_external_registration_popup=Включити реєстрацію тільки через зовнішні сервіси. openid_signin=Увімкнути реєстрацію за допомогою OpenID openid_signin_popup=Увімкнути вхід за допомогою OpenID. openid_signup=Увімкнути самостійну реєстрацію за допомогою OpenID +openid_signup_popup=Увімкнути самореєстрацію користувачів на основі OpenID. enable_captcha=Увімкнути CAPTCHA enable_captcha_popup=Вимагати перевірку CAPTCHA при самостійній реєстрації користувача. require_sign_in_view=Вимагати авторизації для перегляду сторінок @@ -139,7 +146,9 @@ confirm_password=Підтвердження пароля admin_email=Адреса електронної пошти install_btn_confirm=Встановлення Gitea test_git_failed=Не в змозі перевірити 'git' команду: %v +sqlite3_not_available=Ця версія Gitea не підтримує SQLite3. Будь ласка, завантажте офіційну бінарну версію з %s (не версію gobuild). invalid_db_setting=Налаштування бази даних є некоректними: %v +invalid_repo_path=Помилковий шлях до кореня репозиторію: %v save_config_failed=Не в змозі зберегти конфігурацію: %v invalid_admin_setting=Неприпустимі налаштування облікового запису адміністратора: %v install_success=Ласкаво просимо! Дякуємо вам за вибір Gitea. Розважайтеся, і будьте обережні! @@ -151,6 +160,7 @@ default_allow_create_organization_popup=Дозволити новим облік default_enable_timetracking=Увімкнути відстеження часу за замовчуванням default_enable_timetracking_popup=Включити відстеження часу для нових репозиторіїв за замовчуванням. no_reply_address=Прихований поштовий домен +no_reply_address_helper=Доменне ім'я для користувачів із прихованою електронною адресою. Наприклад, ім'я користувача 'joe' буде входити в Git як 'joe@noreply.example.org', якщо для прихованого домену електронної пошти встановлено 'noreply.example.org'. [home] uname_holder=Ім'я користувача або Ел. пошта @@ -175,11 +185,13 @@ code=Код repo_no_results=Відповідних репозиторіїв не знайдено. user_no_results=Відповідних користувачів не знайдено. org_no_results=Відповідних організацій не знайдено. +code_no_results=Відповідний пошуковому запитанню код не знайдено. code_search_results=Результати пошуку '%s' [auth] create_new_account=Реєстрація облікового запису register_helper_msg=Вже зареєстровані? Увійдіть зараз! +social_register_helper_msg=Вже є аккаунт? Зв'яжіть його зараз! disable_register_prompt=Вибачте, можливість реєстрації відключена. Будь ласка, зв'яжіться з адміністратором сайту. disable_register_mail=Підтвердження реєстрації електронною поштою вимкнено. remember_me=Запам'ятати мене @@ -199,19 +211,22 @@ send_reset_mail=Натисніть сюди, щоб відправити лис reset_password=Скинути пароль invalid_code=Цей код підтвердження недійсний або закінчився. reset_password_helper=Натисніть тут для скидання пароля -password_too_short=Довжина пароля не може бути меншою за %d. +password_too_short=Довжина пароля не може бути меншою за %d символів. non_local_account=Нелокальні акаунти не можуть змінити пароль через Gitea. verify=Підтвердити scratch_code=Одноразовий пароль use_scratch_code=Використовувати одноразовий пароль -twofa_scratch_used=Ви використовували одноразовий пароль. Ви були перенаправлені на сторінку налаштувань для генерації нового коду або відключення двуфакторной аутентифікації. +twofa_scratch_used=Ви використовували одноразовий пароль. Ви були перенаправлені на сторінку налаштувань для генерації нового коду або відключення двуфакторної автентифікації. twofa_passcode_incorrect=Ваш пароль є невірним. Якщо ви втратили пристрій, використовуйте ваш одноразовий пароль. twofa_scratch_token_incorrect=Невірний одноразовий пароль. login_userpass=Увійти login_openid=OpenID openid_connect_submit=Під’єднатися openid_connect_title=Підключитися до існуючого облікового запису +openid_connect_desc=Вибраний OpenID URI невідомий. Пов'яжіть його з новим обліковим записом тут. openid_register_title=Створити новий обліковий запис +openid_register_desc=Вибраний OpenID URI невідомий. Пов'яжіть йогоз новим обліковим записом тут. +openid_signin_desc=Введіть свій ідентифікатор OpenID. Наприклад: https://anne.me, bob.openid.org.cn або gnusocial.net/carry. disable_forgot_password_mail=На жаль скидання пароля відключене. Будь ласка, зв'яжіться з адміністратором сайту. [mail] @@ -247,6 +262,8 @@ TreeName=Шлях до файлу Content=Зміст require_error=` не може бути пустим.` +alpha_dash_error=` повинен містити тільки літерно-цифрові символи, дефіс ('-') та підкреслення ('_'). ` +alpha_dash_dot_error=` повинен містити тільки літерно-цифрові символи, дефіс ('-') , підкреслення ('_') та точки ('.'). ` git_ref_name_error=` повинен бути правильним посилальним ім'ям Git.` size_error=` повинен бути розмір %s.` min_size_error=` повинен бути принаймні %s символів.` @@ -262,32 +279,41 @@ username_been_taken=Ім'я користувача вже зайнято. repo_name_been_taken=Ім'я репозіторію вже використовується. org_name_been_taken=Назва організації вже зайнято. team_name_been_taken=Назва команди вже зайнято. +team_no_units_error=Дозволити доступ до принаймні одного розділу репозитарію. email_been_used=Ця електронна адреса вже використовується. openid_been_used=OpenID адреса '%s' вже використовується. username_password_incorrect=Неправильне ім'я користувача або пароль. enterred_invalid_repo_name=Невірно введено ім'я репозиторію. +enterred_invalid_owner_name=Ім'я нового власника не є дійсним. +enterred_invalid_password=Введений вами пароль некоректний. user_not_exist=Даний користувач не існує. +last_org_owner=Ви не можете вилучити останнього користувача з команди 'власники'. У кожній команді має бути хоча б один власник. cannot_add_org_to_team=Організацію неможливо додати як учасника команди. invalid_ssh_key=Неможливо перевірити ваш SSH ключ: %s invalid_gpg_key=Неможливо перевірити ваш GPG ключ: %s +unable_verify_ssh_key=Не вдається підтвердити ключ SSH; подвійно перевірте його на наявність похибки. auth_failed=Помилка автентифікації: %v +still_own_repo=Ваш обліковий запис володіє одним або декількома репозиторіями; видаліть або перенесіть їх в першу чергу. +still_has_org=Ваш обліковий запис є учасником однієї чи декількох організацій; вийдіть з них в першу чергу. +org_still_own_repo=Ця організація як і раніше володіє одним або декількома репозиторіями; спочатку видаліть або перенесіть їх. target_branch_not_exist=Цільової гілки не існує. [user] change_avatar=Змінити свій аватар… -join_on=Приєднався +join_on=Приєднався(-лась) repositories=Репозиторії activity=Публічна активність -followers=Підписники +followers=Читачі starred=Обрані Репозиторії -following=Слідкувати +following=Читає follow=Підписатися unfollow=Відписатися form.name_reserved=Ім'я користувача "%s" зарезервовано. +form.name_pattern_not_allowed=Шаблон '%s' не дозволено в імені користувача. [settings] profile=Профіль @@ -309,6 +335,7 @@ u2f=Ключі безпеки public_profile=Загальнодоступний профіль profile_desc=Ваша адреса електронної пошти використовуватиметься для сповіщення та інших операцій. +password_username_disabled=Нелокальним користувачам заборонено змінювати ім'я користувача. Щоб отримати докладнішу інформацію, зв'яжіться з адміністратором сайту. full_name=Повне ім'я website=Веб-сайт location=Місцезнаходження @@ -316,7 +343,7 @@ update_profile=Оновити профіль update_profile_success=Профіль успішно оновлено. change_username=Ваше Ім'я кристувача було змінено. continue=Продовжити -cancel=Відміна +cancel=Відмінити language=Мова lookup_avatar_by_mail=Знайти Аватар за адресою електронної пошти @@ -338,32 +365,47 @@ password_change_disabled=Нелокальні акаунти не можуть emails=Адреса електронної пошти manage_emails=Керування адресами ел. пошти +manage_openid=Керування OpenID email_desc=Ваша основна адреса електронної пошти використовуватиметься для сповіщення та інших операцій. primary=Основний primary_email=Зробити основним delete_email=Видалити email_deletion=Видалити адресу електронної пошти +email_deletion_success=Адресу електронної пошти було видалено. +openid_deletion=Видалити адресу OpenID +openid_deletion_success=Адреса OpenID була видалена. add_new_email=Додати нову адресу електронної пошти +add_new_openid=Додати новий OpenID URI add_email=Додати адресу електронної пошти +add_openid=Додати OpenID URI add_email_confirmation_sent=Електронний лист із підтвердженням було відправлено на '%s', будь ласка, перевірте вашу поштову скриньку протягом наступних %s, щоб підтвердити адресу. +add_email_success=Додано нову адресу електронної пошти. +add_openid_success=Нова адреса OpenID була додана. keep_email_private=Приховати адресу електронної пошти keep_email_private_popup=Вашу адресу електронної пошти буде приховано від інших користувачів. manage_ssh_keys=Керувати SSH ключами manage_gpg_keys=Керувати GPG ключами add_key=Додати ключ +ssh_desc=Ці відкриті SSH-ключі пов'язані з вашим обліковим записом. Відповідні приватні ключі дозволяють отримати повний доступ до ваших репозиторіїв. +gpg_desc=Ці публічні ключі GPG пов'язані з вашим обліковим записом. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють здійснювати перевірку комітів. ssh_helper=Потрібна допомога? Дивіться гід на GitHub з генерації ключів SSH або виправлення типових неполадок SSH. gpg_helper= Потрібна допомога? Перегляньте посібник GitHub про GPG . add_new_key=Додати SSH ключ add_new_gpg_key=Додати GPG ключ +ssh_key_been_used=Цей ключ SSH вже додано до вашого облікового запису. +ssh_key_name_used=Ключ SSH з таким самим ім'ям вже додано до вашого облікового запису. +gpg_key_id_used=Публічний ключ GPG з таким самим ідентифікатором вже існує. subkeys=Підключі key_id=ID ключа key_name=Ім'я ключа key_content=Зміст add_key_success=SSH ключ '%s' додано. +add_gpg_key_success=GPG ключ '%s' додано. delete_key=Видалити ssh_key_deletion=Видалити SSH ключ gpg_key_deletion=Видалити GPG ключ +ssh_key_deletion_desc=Видалення ключа SSH скасовує доступ до вашого облікового запису. Продовжити? gpg_key_deletion_desc=Видалення GPG ключа скасовує перевірку підписаних ним комітів. Продовжити? ssh_key_deletion_success=SSH було видалено. gpg_key_deletion_success=GPG було видалено. @@ -392,19 +434,33 @@ generate_token=Згенерувати токен delete_token=Видалити access_token_deletion=Видалити токен доступу +twofa_desc=Двофакторна автентифікація підвищує безпеку вашого облікового запису. +twofa_is_enrolled=Ваш обліковий запис на даний час використовує двофакторну автентифікацію. +twofa_not_enrolled=Ваш обліковий запис наразі не використовує двофакторну автентифікаціїю. twofa_disable=Вимкнути двофакторну автентифікацію +twofa_scratch_token_regenerate=Перестворити токен одноразового пароля +twofa_enroll=Увімкнути двофакторну автентифікацію +twofa_disable_note=При необхідності можна відключити двофакторну автентифікацію. +regenerate_scratch_token_desc=Якщо ви втратили свій токен одноразового пароля або вже використовували його для входу, ви можете скинути його тут. +twofa_disabled=Двофакторна автентифікація вимкнена. +scan_this_image=Проскануйте це зображення вашим додатком для двуфакторної автентифікації: or_enter_secret=Або введіть секрет: %s +passcode_invalid=Некоректний пароль. Спробуй ще раз. +u2f_desc=Ключами безпеки є апаратні пристрої, що містять криптографічні ключі. Вони можуть використовуватися для двофакторної автентифікації. Ключ безпеки повинен підтримувати стандарт FIDO U2F. u2f_register_key=Додати ключ безпеки u2f_nickname=Псевдонім u2f_delete_key=Видалити ключ безпеки manage_account_links=Керування обліковими записами +manage_account_links_desc=Ці зовнішні акаунти прив'язані до вашого аккаунту Gitea. remove_account_link=Видалити облікові записи orgs_none=Ви не є учасником будь-якої організації. +repos_none=У вас немає власних репозиторіїв delete_account=Видалити ваш обліковий запис +delete_prompt=Ця операція остаточно видалить обліковий запис користувача. Це НЕ МОЖЛИВО відмінити. confirm_delete_account=Підтвердження видалення delete_account_title=Видалити цей обліковий запис @@ -413,6 +469,7 @@ owner=Власник repo_name=Назва репозиторію visibility=Видимість visiblity_helper=Зробити репозиторій приватним +visiblity_fork_helper=(Зміна цього вплине на всі форки.) clone_helper=Потрібна допомога у клонуванні? Відвідайте Допомогу. fork_repo=Форкнути репозиторій fork_from=Форк з @@ -424,6 +481,7 @@ license=Ліцензія license_helper=Виберіть ліцензійний файл. readme=README readme_helper=Виберіть шаблон README. +auto_init=Ініціалізувати репозиторій (Додає .gitignore, LICENSE та README) create_repo=Створити репозиторій default_branch=Головна гілка mirror_prune=Очистити @@ -490,6 +548,7 @@ file_history=Історія file_view_raw=Перегляд Raw file_permalink=Постійне посилання file_too_large=Цей файл завеликий щоб бути показаним. +video_not_supported_in_browser=Ваш браузер не підтримує тег 'video' HTML5. stored_lfs=Збережено з Git LFS commit_graph=Графік комітів @@ -497,6 +556,7 @@ editor.new_file=Новий файл editor.upload_file=Завантажити файл editor.edit_file=Редагування файлу editor.preview_changes=Попередній перегляд змін +editor.cannot_edit_non_text_files=Бінарні файли не можливо редагувати у веб-інтерфейсі. editor.edit_this_file=Редагувати файл editor.must_be_on_a_branch=Ви повинні бути у гілці щоб зробити, або запропонувати зміни до цього файлу. editor.fork_before_edit=Необхідно зробити форк цього репозиторій, щоб внести або запропонувати зміни в цей файл. @@ -515,7 +575,7 @@ editor.commit_message_desc=Додати необов'язковий розшир editor.commit_directly_to_this_branch=Зробіть коміт прямо в гілку %s. editor.create_new_branch=Створити нову гілку для цього коміту та відкрити запит на злиття. editor.new_branch_name_desc=Ім'я нової гілки… -editor.cancel=Відміна +editor.cancel=Відмінити editor.filename_cannot_be_empty=Ім'я файлу не може бути порожнім. editor.branch_already_exists=Гілка '%s' вже присутня в репозиторії. editor.directory_is_a_file=Ім'я каталогу "%s" уже використовується як ім'я файлу в цьому репозиторії. @@ -536,8 +596,10 @@ commits.date=Дата commits.older=Давніше commits.newer=Новіше commits.signed_by=Підписано +commits.gpg_key_id=Ідентифікатор GPG ключа ext_issues=Зов. Проблеми +ext_issues.desc=Посилання на зовнішню систему відстеження проблем. issues.new=Нова проблема issues.new.labels=Мітки @@ -559,6 +621,7 @@ issues.new_label_desc_placeholder=Опис issues.create_label=Створити мітку issues.label_templates.title=Завантажити визначений набір міток issues.label_templates.helper=Оберіть набір міток +issues.label_templates.use=Використовувати набір міток issues.label_templates.fail_to_load_file=Не вдалося завантажити файл шаблона мітки '%s': %v issues.add_label_at=додав(ла) мітку
%s
%s issues.add_milestone_at=`додав(ла) до %s етапу %s` @@ -566,6 +629,7 @@ issues.deleted_milestone=`(видалено)` issues.add_assignee_at=`був призначений %s %s` issues.remove_assignee_at=`видалили із призначених %s` issues.change_title_at=`змінив(ла) заголовок з %s на %s %s` +issues.delete_branch_at=`видалена гілка %s %s` issues.open_tab=%d відкрито issues.close_tab=%d закрито issues.filter_label=Мітка @@ -586,7 +650,10 @@ issues.filter_sort.recentupdate=Нещодавно оновлено issues.filter_sort.leastupdate=Найдавніше оновлені issues.filter_sort.mostcomment=Найбільш коментовані issues.filter_sort.leastcomment=Найменш коментовані +issues.filter_sort.moststars=Найбільш обраних +issues.filter_sort.feweststars=Найменш обраних issues.filter_sort.mostforks=Найбільше форків +issues.filter_sort.fewestforks=Найменше форків issues.action_open=Відкрити issues.action_close=Закрити issues.action_label=Мітка @@ -617,7 +684,7 @@ issues.collaborator=Співавтор issues.owner=Власник issues.sign_in_require_desc=Підпишіться щоб приєднатися до обговорення. issues.edit=Редагувати -issues.cancel=Відміна +issues.cancel=Відмінити issues.save=Зберегти issues.label_title=Назва мітки issues.label_description=Опис мітки @@ -644,17 +711,21 @@ issues.start_tracking=Почати відстеження часу issues.start_tracking_history=`почав працювати %s` issues.tracking_already_started=`Ви вже почали відстежувати час для цієї проблеми!` issues.stop_tracking=Стоп +issues.stop_tracking_history=`перестав(-ла) працювати %s` issues.add_time=Вручну додати час issues.add_time_short=Додати час -issues.add_time_cancel=Відміна +issues.add_time_cancel=Відмінити +issues.add_time_history=`додав(-ла) витрачений час %s` issues.add_time_hours=Години issues.add_time_minutes=Хвилини issues.add_time_sum_to_small=Час не введено. -issues.cancel_tracking=Відміна +issues.cancel_tracking=Відмінити issues.cancel_tracking_history=`скасував відстеження часу %s` issues.time_spent_total=Загальний витрачений час issues.time_spent_from_all_authors=`Загальний витрачений час: %s` issues.due_date=Дата завершення +issues.invalid_due_date_format=Дата закінчення має бути в форматі 'ррр-мм-дд'. +issues.error_modifying_due_date=Не вдалося змінити дату завершення. issues.due_date_form=рррр-мм-дд issues.due_date_form_add=Додати дату завершення issues.due_date_form_update=Оновити дату завершення @@ -701,13 +772,16 @@ milestones.desc=Опис milestones.due_date=Дата завершення (опціонально) milestones.clear=Очистити milestones.edit=Редагувати етап -milestones.cancel=Відміна +milestones.cancel=Відмінити milestones.modify=Оновити етап milestones.deletion=Видалити етап +milestones.filter_sort.closest_due_date=Найближче за датою +milestones.filter_sort.furthest_due_date=Далі за датою milestones.filter_sort.most_issues=Найбільш проблем milestones.filter_sort.least_issues=Найменш проблем ext_wiki=Зов. Вікі +ext_wiki.desc=Посилання на зовнішню вікі. wiki=Вікі wiki.welcome=Ласкаво просимо до Вікі. @@ -757,6 +831,7 @@ activity.closed_issue_label=Закрито activity.new_issues_count_1=Нова Проблема activity.new_issues_count_n=%d Проблем activity.new_issue_label=Відкриті +activity.title.unresolved_conv_1=%d Незавершене обговорення activity.unresolved_conv_label=Відкрити activity.title.releases_1=%d Реліз activity.title.releases_n=%d Релізів @@ -922,7 +997,7 @@ release.preview=Переглянути release.loading=Завантаження… release.prerelease_desc=Позначити як пре-реліз release.prerelease_helper=Позначте цей випуск непридатним для ПРОД використання. -release.cancel=Відміна +release.cancel=Відмінити release.publish=Опублікувати реліз release.save_draft=Зберегти чернетку release.edit_release=Оновити реліз @@ -951,6 +1026,7 @@ topic.done=Готово [org] org_name_holder=Назва організації org_full_name_holder=Повна назва організації +org_name_helper=Назва організації має бути простою та зрозумілою. create_org=Створити організацію repo_updated=Оновлено people=Учасники @@ -963,6 +1039,7 @@ org_desc=Опис team_name=Назва команди team_desc=Опис team_name_helper=Назва команди має бути простою та зрозумілою. +team_desc_helper=Опишіть мету або роль команди. team_permission_desc=Права доступу team_unit_desc=Дозволити доступ до розділів репозиторію @@ -975,6 +1052,8 @@ settings.website=Веб-сайт settings.location=Розташування settings.update_settings=Оновити налаштування settings.update_setting_success=Налаштування організації оновлені. +settings.change_orgname_prompt=Ця зміна змінить посилання на організацію. +settings.update_avatar_success=Аватар організації оновлений. settings.delete=Видалити організацію settings.delete_account=Видалити цю організацію settings.confirm_delete_account=Підтвердіть видалення @@ -1040,9 +1119,11 @@ dashboard.delete_inactivate_accounts_success=Усі неактивні облі dashboard.delete_repo_archives=Видалити всі архіви репозиторіїв dashboard.git_gc_repos_success=Всі репозиторії завершили збирання сміття. dashboard.server_uptime=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.current_heap_usage=Поточне використання динамічної пам'яті dashboard.heap_memory_obtained=Отримано динамічної пам'яті @@ -1050,6 +1131,7 @@ dashboard.heap_memory_idle=Не використовується динаміч dashboard.heap_memory_in_use=Використовується динамічною пам'яттю dashboard.heap_memory_released=Звільнено динамічної пам'яті dashboard.heap_objects=Об'єктів динамічної пам'яті +dashboard.bootstrap_stack_usage=Використання стеку Bootstrap dashboard.stack_memory_obtained=Зайнято пам'яті стеком dashboard.mspan_structures_usage=Використання структур MSpan dashboard.mspan_structures_obtained=Отримано структур MSpan @@ -1073,7 +1155,9 @@ users.admin=Адміністратор users.repos=Репозиторії users.created=Створено users.last_login=Останній вхід +users.never_login=Ніколи не входив users.send_register_notify=Надіслати повідомлення про реєстрацію користувача +users.new_success=Обліковий запис '%s' створений. users.edit=Редагувати users.auth_source=Джерело автентифікації users.local=Локальні @@ -1107,7 +1191,7 @@ repos.forks=Форки repos.issues=Проблеми repos.size=Розмір -auths.auth_manage_panel=Керування джерелом аутентифікації +auths.auth_manage_panel=Керування джерелом автентифікації auths.new=Додати джерело автентифікації auths.name=Ім'я auths.type=Тип @@ -1124,6 +1208,7 @@ auths.bind_dn=Прив'язати DN auths.bind_password=Прив'язати пароль auths.user_base=База пошуку користувачів auths.user_dn=DN користувача +auths.attribute_username=Атрибут імені користувача auths.search_page_size=Розмір сторінки auths.filter=Користувацький фільтр auths.admin_filter=Фільтр адміністратора @@ -1143,8 +1228,8 @@ auths.oauth2_profileURL=URL профілю auths.oauth2_emailURL=URL електронної пошти auths.enable_auto_register=Увімкнути автоматичну реєстрацію auths.tips=Поради -auths.tips.oauth2.general=OAuth2 аутентифікація -auths.tips.oauth2.general.tip=При додаванні нового OAuth2 провайдера, URL адреса переадресації по завершенні аутентифікації повинена виглядати так:/user/oauth2//callback +auths.tips.oauth2.general=OAuth2 автентифікація +auths.tips.oauth2.general.tip=При додаванні нового OAuth2 провайдера, URL адреса переадресації по завершенні автентифікації повинена виглядати так:/user/oauth2//callback auths.tip.oauth2_provider=Постачальник OAuth2 auths.tip.dropbox=Додайте новий додаток на https://www.dropbox.com/developers/apps auths.tip.facebook=Створіть новий додаток на https://developers.facebook.com/apps і додайте модуль "Facebook Login @@ -1208,6 +1293,7 @@ config.default_keep_email_private=Приховати адресу електро config.default_allow_create_organization=Дозволити створення організацій за замовчуванням config.enable_timetracking=Увімкнути відстеження часу config.default_enable_timetracking=Увімкнути відстеження часу за замовчуванням +config.no_reply_address=Прихований домен електронної пошти config.webhook_config=Конфігурація web-хуків config.queue_length=Довжина черги @@ -1267,12 +1353,14 @@ 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.view_detail_header=Переглянути деталі повідомлення notices.actions=Дії notices.select_all=Вибрати все notices.deselect_all=Скасувати виділення @@ -1327,6 +1415,7 @@ raw_minutes=хвилини [dropzone] default_message=Перетягніть файли або натисніть тут, щоб завантажити. +invalid_input_type=Ви не можете завантажувати файли цього типу. file_too_big=Розмір файлу ({{filesize}} MB), що більше ніж максимальний розмір: ({{maxFilesize}} MB). remove_file=Видалити файл diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 8b23e404c..4e59ed08e 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -213,7 +213,7 @@ send_reset_mail=单击此处(重新)发送您的密码重置邮件 reset_password=重置密码 invalid_code=此确认密钥无效或已过期。 reset_password_helper=单击此处重置密码 -password_too_short=密码长度不能少于 %d 位! +password_too_short=密码长度不能少于 %d 位。 non_local_account=非本地帐户不能通过 Gitea 的 web 界面更改密码。 verify=验证 scratch_code=验证口令 diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini index a64367d39..d8b8e210c 100644 --- a/options/locale/locale_zh-HK.ini +++ b/options/locale/locale_zh-HK.ini @@ -94,7 +94,6 @@ email_not_associate=此電子郵件地址未與任何帳戶連結 send_reset_mail=點選此處重發您的密碼重製郵件 reset_password=重置密碼 reset_password_helper=單擊此處重置密碼 -password_too_short=密碼長度不能少於 %d 位! verify=驗證 scratch_code=備用碼 use_scratch_code=使用備用碼 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index fa716b27d..8a6914af2 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -97,7 +97,6 @@ email_not_associate=此電子郵件地址未與任何帳戶連結 send_reset_mail=點選此處重發您的密碼重製郵件 reset_password=重置密碼 reset_password_helper=單擊此處重置密碼 -password_too_short=密碼長度不能少於 %d 位! verify=驗證 scratch_code=備用碼 use_scratch_code=使用備用碼 diff --git a/public/css/index.css b/public/css/index.css index 607ab3f6a..2ee3e8314 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -1 +1 @@ -.tribute-container{box-shadow:0 1px 3px 1px #c7c7c7}.tribute-container ul{background:#fff}.tribute-container li{padding:8px 12px;border-bottom:1px solid #dcdcdc}.tribute-container li img{display:inline-block;vertical-align:middle;width:28px;height:28px;margin-right:5px}.tribute-container li span.fullname{font-weight:400;font-size:.8rem;margin-left:3px}.tribute-container li.highlight,.tribute-container li:hover{background:#2185D0;color:#fff}.emoji{width:1.5em;height:1.5em;display:inline-block;background-size:contain}body{font-family:Lato,"Segoe UI","Microsoft YaHei",Arial,Helvetica,sans-serif!important;background-color:#fff;overflow-y:scroll;-webkit-font-smoothing:antialiased}img{border-radius:3px}.rounded{border-radius:.28571429rem!important}code,pre{font:12px Consolas,"Liberation Mono",Menlo,Courier,monospace}code.raw,pre.raw{padding:7px 12px;margin:10px 0;background-color:#f8f8f8;border:1px solid #ddd;border-radius:3px;font-size:13px;line-height:1.5;overflow:auto}code.wrap,pre.wrap{white-space:pre-wrap;-ms-word-break:break-all;word-break:break-all;overflow-wrap:break-word;word-wrap:break-word}.dont-break-out{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.full.height{padding:0;margin:0 0 -40px 0;min-height:100%}.following.bar{z-index:900;left:0;width:100%;margin:0}.following.bar.light{background-color:#fff;border-bottom:1px solid #DDD;box-shadow:0 2px 3px rgba(0,0,0,.04)}.following.bar .column .menu{margin-top:0}.following.bar .top.menu a.item.brand{padding-left:0}.following.bar .brand .ui.mini.image{width:30px}.following.bar .top.menu .dropdown.item.active,.following.bar .top.menu .dropdown.item:hover,.following.bar .top.menu a.item:hover{background-color:transparent}.following.bar .top.menu a.item:hover{color:rgba(0,0,0,.45)}.following.bar .top.menu .menu{z-index:900}.following.bar .octicon{margin-right:.75em}.following.bar .octicon.fitted{margin-right:0}.following.bar .searchbox{background-color:#f4f4f4!important}.following.bar .searchbox:focus{background-color:#e9e9e9!important}.following.bar .text .octicon{width:16px;text-align:center}@media only screen and (max-width:767px){.following.bar #navbar:not(.shown)>:not(:first-child){display:none}}.right.stackable.menu{margin-left:auto;display:flex;display:-ms-flexbox;-ms-flex-align:inherit;align-items:inherit;-ms-flex-direction:inherit;flex-direction:inherit}.ui.left{float:left}.ui.right{float:right}.ui.button,.ui.menu .item{-moz-user-select:auto;-ms-user-select:auto;-webkit-user-select:auto;user-select:auto}.ui.container.fluid.padded{padding:0 10px 0 10px}.ui.form .ui.button{font-weight:400}.ui.floating.label{z-index:10}.ui.menu,.ui.segment,.ui.vertical.menu{box-shadow:none}.ui .menu:not(.vertical) .item>.button.compact{padding:.58928571em 1.125em}.ui .menu:not(.vertical) .item>.button.small{font-size:.92857143rem}.ui.dropdown .menu>.item>.floating.label{z-index:11}.ui.dropdown .menu .menu>.item>.floating.label{z-index:21}.ui .text.red{color:#d95c5c!important}.ui .text.red a{color:#d95c5c!important}.ui .text.red a:hover{color:#E67777!important}.ui .text.blue{color:#428bca!important}.ui .text.blue a{color:#15c!important}.ui .text.blue a:hover{color:#428bca!important}.ui .text.black{color:#444}.ui .text.black:hover{color:#000}.ui .text.grey{color:#767676!important}.ui .text.grey a{color:#444!important}.ui .text.grey a:hover{color:#000!important}.ui .text.light.grey{color:#888!important}.ui .text.green{color:#6cc644!important}.ui .text.purple{color:#6e5494!important}.ui .text.yellow{color:#FBBD08!important}.ui .text.gold{color:#a1882b!important}.ui .text.left{text-align:left!important}.ui .text.right{text-align:right!important}.ui .text.small{font-size:.75em}.ui .text.normal{font-weight:400}.ui .text.bold{font-weight:700}.ui .text.italic{font-style:italic}.ui .text.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.ui .text.thin{font-weight:400}.ui .text.middle{vertical-align:middle}.ui .message{text-align:center}.ui .header>i+.content{padding-left:.75rem;vertical-align:middle}.ui .warning.header{background-color:#F9EDBE!important;border-color:#F0C36D}.ui .warning.segment{border-color:#F0C36D}.ui .info.segment{border:1px solid #c5d5dd}.ui .info.segment.top{background-color:#e6f1f6!important}.ui .info.segment.top h3,.ui .info.segment.top h4{margin-top:0}.ui .info.segment.top h3:last-child{margin-top:4px}.ui .info.segment.top>:last-child{margin-bottom:0}.ui .normal.header{font-weight:400}.ui .avatar.image{border-radius:3px}.ui .form .fake{display:none!important}.ui .form .sub.field{margin-left:25px}.ui .sha.label{font-family:Consolas,Menlo,Monaco,"Lucida Console",monospace;font-size:13px;padding:6px 10px 4px 10px;font-weight:400;margin:0 6px}.ui.status.buttons .octicon{margin-right:4px}.ui.inline.delete-button{padding:8px 15px;font-weight:400}.ui .background.red{background-color:#d95c5c!important}.ui .background.blue{background-color:#428bca!important}.ui .background.black{background-color:#444}.ui .background.grey{background-color:#767676!important}.ui .background.light.grey{background-color:#888!important}.ui .background.green{background-color:#6cc644!important}.ui .background.purple{background-color:#6e5494!important}.ui .background.yellow{background-color:#FBBD08!important}.ui .background.gold{background-color:#a1882b!important}.ui .branch-tag-choice{line-height:20px}.overflow.menu .items{max-height:300px;overflow-y:auto}.overflow.menu .items .item{position:relative;cursor:pointer;display:block;border:none;height:auto;border-top:none;line-height:1em;color:rgba(0,0,0,.8);padding:.71428571em 1.14285714em!important;font-size:1rem;text-transform:none;font-weight:400;box-shadow:none;-webkit-touch-callout:none}.overflow.menu .items .item.active{font-weight:700}.overflow.menu .items .item:hover{background:rgba(0,0,0,.05);color:rgba(0,0,0,.8);z-index:13}.scrolling.menu .item.selected{font-weight:700!important}footer{height:40px;background-color:#fff;border-top:1px solid #d6d6d6;clear:both;width:100%;color:#888}footer .container{padding-top:10px}footer .container .fa{width:16px;text-align:center;color:#428bca}footer .container .links>*{border-left:1px solid #d6d6d6;padding-left:8px;margin-left:5px}footer .container .links>:first-child{border-left:none}footer .ui.language .menu{max-height:500px;overflow-y:auto;margin-bottom:7px}.hide{display:none}.center{text-align:center}.img-1{width:2px!important;height:2px!important}.img-2{width:4px!important;height:4px!important}.img-3{width:6px!important;height:6px!important}.img-4{width:8px!important;height:8px!important}.img-5{width:10px!important;height:10px!important}.img-6{width:12px!important;height:12px!important}.img-7{width:14px!important;height:14px!important}.img-8{width:16px!important;height:16px!important}.img-9{width:18px!important;height:18px!important}.img-10{width:20px!important;height:20px!important}.img-11{width:22px!important;height:22px!important}.img-12{width:24px!important;height:24px!important}.img-13{width:26px!important;height:26px!important}.img-14{width:28px!important;height:28px!important}.img-15{width:30px!important;height:30px!important}.img-16{width:32px!important;height:32px!important}@media only screen and (min-width:768px){.mobile-only,.ui.button.mobile-only{display:none}.sr-mobile-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}}@media only screen and (max-width:767px){.not-mobile{display:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}@media only screen and (max-width:991px) and (min-width:768px){.ui.container{width:95%}}.hljs{background:inherit!important;padding:0!important}.ui.menu.new-menu{justify-content:center!important;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}@media only screen and (max-width:1200px){.ui.menu.new-menu{overflow-x:auto!important;justify-content:left!important;padding-bottom:5px}.ui.menu.new-menu::-webkit-scrollbar{height:8px;display:none}.ui.menu.new-menu:hover::-webkit-scrollbar{display:block}.ui.menu.new-menu::-webkit-scrollbar-track{background:rgba(0,0,0,.01)}.ui.menu.new-menu::-webkit-scrollbar-thumb{background:rgba(0,0,0,.2)}.ui.menu.new-menu:after{position:absolute;margin-top:-15px;display:block;background-image:linear-gradient(to right,rgba(255,255,255,0),#fff 100%);content:' ';right:0;height:53px;z-index:1000;width:60px;clear:none;visibility:visible}.ui.menu.new-menu a.item:last-child{padding-right:30px!important}}[v-cloak]{display:none!important}.repos-search{padding-bottom:0!important}.repos-filter{margin-top:0!important;border-bottom-width:0!important;margin-bottom:2px!important}.markdown:not(code){overflow:hidden;font-family:"Helvetica Neue",Helvetica,"Segoe UI",Arial,freesans,sans-serif;font-size:16px;line-height:1.6!important;word-wrap:break-word}.markdown:not(code).file-view{padding:2em 2em 2em!important}.markdown:not(code)>:first-child{margin-top:0!important}.markdown:not(code)>:last-child{margin-bottom:0!important}.markdown:not(code) a:not([href]){color:inherit;text-decoration:none}.markdown:not(code) .absent{color:#c00}.markdown:not(code) .anchor{position:absolute;top:0;left:0;display:block;padding-right:6px;padding-left:30px;margin-left:-30px}.markdown:not(code) .anchor:focus{outline:0}.markdown:not(code) h1,.markdown:not(code) h2,.markdown:not(code) h3,.markdown:not(code) h4,.markdown:not(code) h5,.markdown:not(code) h6{position:relative;margin-top:1em;margin-bottom:16px;font-weight:700;line-height:1.4}.markdown:not(code) h1:first-of-type,.markdown:not(code) h2:first-of-type,.markdown:not(code) h3:first-of-type,.markdown:not(code) h4:first-of-type,.markdown:not(code) h5:first-of-type,.markdown:not(code) h6:first-of-type{margin-top:0!important}.markdown:not(code) h1 .octicon-link,.markdown:not(code) h2 .octicon-link,.markdown:not(code) h3 .octicon-link,.markdown:not(code) h4 .octicon-link,.markdown:not(code) h5 .octicon-link,.markdown:not(code) h6 .octicon-link{display:none;color:#000;vertical-align:middle}.markdown:not(code) h1:hover .anchor,.markdown:not(code) h2:hover .anchor,.markdown:not(code) h3:hover .anchor,.markdown:not(code) h4:hover .anchor,.markdown:not(code) h5:hover .anchor,.markdown:not(code) h6:hover .anchor{padding-left:8px;margin-left:-30px;text-decoration:none}.markdown:not(code) h1:hover .anchor .octicon-link,.markdown:not(code) h2:hover .anchor .octicon-link,.markdown:not(code) h3:hover .anchor .octicon-link,.markdown:not(code) h4:hover .anchor .octicon-link,.markdown:not(code) h5:hover .anchor .octicon-link,.markdown:not(code) h6:hover .anchor .octicon-link{display:inline-block}.markdown:not(code) h1 code,.markdown:not(code) h1 tt,.markdown:not(code) h2 code,.markdown:not(code) h2 tt,.markdown:not(code) h3 code,.markdown:not(code) h3 tt,.markdown:not(code) h4 code,.markdown:not(code) h4 tt,.markdown:not(code) h5 code,.markdown:not(code) h5 tt,.markdown:not(code) h6 code,.markdown:not(code) h6 tt{font-size:inherit}.markdown:not(code) h1{padding-bottom:.3em;font-size:2.25em;line-height:1.2;border-bottom:1px solid #eee}.markdown:not(code) h1 .anchor{line-height:1}.markdown:not(code) h2{padding-bottom:.3em;font-size:1.75em;line-height:1.225;border-bottom:1px solid #eee}.markdown:not(code) h2 .anchor{line-height:1}.markdown:not(code) h3{font-size:1.5em;line-height:1.43}.markdown:not(code) h3 .anchor{line-height:1.2}.markdown:not(code) h4{font-size:1.25em}.markdown:not(code) h4 .anchor{line-height:1.2}.markdown:not(code) h5{font-size:1em}.markdown:not(code) h5 .anchor{line-height:1.1}.markdown:not(code) h6{font-size:1em;color:#777}.markdown:not(code) h6 .anchor{line-height:1.1}.markdown:not(code) blockquote,.markdown:not(code) dl,.markdown:not(code) ol,.markdown:not(code) p,.markdown:not(code) pre,.markdown:not(code) table,.markdown:not(code) ul{margin-top:0;margin-bottom:16px}.markdown:not(code) blockquote{margin-left:0}.markdown:not(code) hr{height:4px;padding:0;margin:16px 0;background-color:#e7e7e7;border:0 none}.markdown:not(code) ol,.markdown:not(code) ul{padding-left:2em}.markdown:not(code) ol.no-list,.markdown:not(code) ul.no-list{padding:0;list-style-type:none}.markdown:not(code) ol ol,.markdown:not(code) ol ul,.markdown:not(code) ul ol,.markdown:not(code) ul ul{margin-top:0;margin-bottom:0}.markdown:not(code) ol ol,.markdown:not(code) ul ol{list-style-type:lower-roman}.markdown:not(code) li>p{margin-top:0}.markdown:not(code) dl{padding:0}.markdown:not(code) dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:700}.markdown:not(code) dl dd{padding:0 16px;margin-bottom:16px}.markdown:not(code) blockquote{padding:0 15px;color:#777;border-left:4px solid #ddd}.markdown:not(code) blockquote>:first-child{margin-top:0}.markdown:not(code) blockquote>:last-child{margin-bottom:0}.markdown:not(code) table{width:auto;overflow:auto;word-break:normal;word-break:keep-all}.markdown:not(code) table th{font-weight:700}.markdown:not(code) table td,.markdown:not(code) table th{padding:6px 13px!important;border:1px solid #ddd!important}.markdown:not(code) table tr{background-color:#fff;border-top:1px solid #ccc}.markdown:not(code) table tr:nth-child(2n){background-color:#f8f8f8}.markdown:not(code) img{max-width:100%;box-sizing:border-box}.markdown:not(code) .emoji{max-width:none}.markdown:not(code) span.frame{display:block;overflow:hidden}.markdown:not(code) span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid #ddd}.markdown:not(code) span.frame span img{display:block;float:left}.markdown:not(code) span.frame span span{display:block;padding:5px 0 0;clear:both;color:#333}.markdown:not(code) span.align-center{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown:not(code) span.align-center span img{margin:0 auto;text-align:center}.markdown:not(code) span.align-right{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown:not(code) span.align-right span img{margin:0;text-align:right}.markdown:not(code) span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown:not(code) span.float-left span{margin:13px 0 0}.markdown:not(code) span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown:not(code) span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown:not(code) code,.markdown:not(code) tt{padding:0;padding-top:.2em;padding-bottom:.2em;margin:0;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px}.markdown:not(code) code:after,.markdown:not(code) code:before,.markdown:not(code) tt:after,.markdown:not(code) tt:before{letter-spacing:-.2em;content:"\00a0"}.markdown:not(code) code br,.markdown:not(code) tt br{display:none}.markdown:not(code) del code{text-decoration:inherit}.markdown:not(code) pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:0 0;border:0}.markdown:not(code) .highlight{margin-bottom:16px}.markdown:not(code) .highlight pre,.markdown:not(code) pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f7f7f7;border-radius:3px}.markdown:not(code) .highlight pre{margin-bottom:0;word-break:normal}.markdown:not(code) pre{word-wrap:normal}.markdown:not(code) pre code,.markdown:not(code) pre tt{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown:not(code) pre code:after,.markdown:not(code) pre code:before,.markdown:not(code) pre tt:after,.markdown:not(code) pre tt:before{content:normal}.markdown:not(code) kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#555;vertical-align:middle;background-color:#fcfcfc;border:solid 1px #ccc;border-bottom-color:#bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb}.markdown:not(code) input[type=checkbox]{vertical-align:middle!important}.markdown:not(code) .csv-data td,.markdown:not(code) .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown:not(code) .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:#fff;border:0}.markdown:not(code) .csv-data tr{border-top:0}.markdown:not(code) .csv-data th{font-weight:700;background:#f8f8f8;border-top:0}.markdown:not(code) .ui.list .list,.markdown:not(code) ol.ui.list ol,.markdown:not(code) ul.ui.list ul{padding-left:2em}.home{padding-bottom:80px}.home .logo{max-width:220px}.home .hero h1,.home .hero h2{font-family:'PT Sans Narrow',sans-serif,'Microsoft YaHei'}@media only screen and (max-width:767px){.home .hero h1{font-size:3.5em}.home .hero h2{font-size:2em}}@media only screen and (min-width:768px){.home .hero h1{font-size:5.5em}.home .hero h2{font-size:3em}}.home .hero .octicon{color:#5aa509;font-size:40px;width:50px}.home .hero.header{font-size:20px}.home p.large{font-size:16px}.home .stackable{padding-top:30px}.home a{color:#5aa509}.signup{padding-top:15px;padding-bottom:80px}.install{padding-top:45px;padding-bottom:80px}.install form label{text-align:right;width:320px!important}.install form input{width:35%!important}.install form .field{text-align:left}.install form .field .help{margin-left:335px!important}.install form .field.optional .title{margin-left:38%}.install .ui .checkbox{margin-left:40%!important}.install .ui .checkbox label{width:auto!important}.form .help{color:#999;padding-top:.6em;padding-bottom:.6em;display:inline-block}.ui.attached.header{background:#f0f0f0}.ui.attached.header .right{margin-top:-5px}.ui.attached.header .right .button{padding:8px 10px;font-weight:400}#create-page-form form{margin:auto}#create-page-form form .ui.message{text-align:center}@media only screen and (min-width:768px){#create-page-form form{width:800px!important}#create-page-form form .header{padding-left:280px!important}#create-page-form form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}#create-page-form form .help{margin-left:265px!important}#create-page-form form .optional .title{margin-left:250px!important}#create-page-form form input,#create-page-form form textarea{width:50%!important}}@media only screen and (max-width:767px){#create-page-form form .optional .title{margin-left:15px}#create-page-form form .inline.field>label{display:block}}.signin .oauth2 div{display:inline-block}.signin .oauth2 div p{margin:10px 5px 0 0;float:left}.signin .oauth2 a{margin-right:3px}.signin .oauth2 a:last-child{margin-right:0}.signin .oauth2 img{width:32px;height:32px}.signin .oauth2 img.openidConnect{width:auto}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{margin:auto}.user.activate form .ui.message,.user.forgot.password form .ui.message,.user.reset.password form .ui.message,.user.signin form .ui.message,.user.signup form .ui.message{text-align:center}@media only screen and (min-width:768px){.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:800px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:280px!important}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.user.activate form .help,.user.forgot.password form .help,.user.reset.password form .help,.user.signin form .help,.user.signup form .help{margin-left:265px!important}.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:250px!important}.user.activate form input,.user.activate form textarea,.user.forgot.password form input,.user.forgot.password form textarea,.user.reset.password form input,.user.reset.password form textarea,.user.signin form input,.user.signin form textarea,.user.signup form input,.user.signup form textarea{width:50%!important}}@media only screen and (max-width:767px){.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:15px}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{display:block}}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:700px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:0!important;text-align:center}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{width:200px!important}.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{margin:auto}.repository.new.fork form .ui.message,.repository.new.migrate form .ui.message,.repository.new.repo form .ui.message{text-align:center}@media only screen and (min-width:768px){.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{width:800px!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:280px!important}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.repository.new.fork form .help,.repository.new.migrate form .help,.repository.new.repo form .help{margin-left:265px!important}.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:250px!important}.repository.new.fork form input,.repository.new.fork form textarea,.repository.new.migrate form input,.repository.new.migrate form textarea,.repository.new.repo form input,.repository.new.repo form textarea{width:50%!important}}@media only screen and (max-width:767px){.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:15px}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{display:block}}.repository.new.fork form .dropdown .dropdown.icon,.repository.new.migrate form .dropdown .dropdown.icon,.repository.new.repo form .dropdown .dropdown.icon{margin-top:-7px!important}.repository.new.fork form .dropdown .text,.repository.new.migrate form .dropdown .text,.repository.new.repo form .dropdown .text{margin-right:0!important}.repository.new.fork form .dropdown .text i,.repository.new.migrate form .dropdown .text i,.repository.new.repo form .dropdown .text i{margin-right:0!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:0!important;text-align:center}.repository.new.repo .ui.form .selection.dropdown:not(.owner){width:50%!important}@media only screen and (min-width:768px){.repository.new.repo .ui.form #auto-init{margin-left:265px!important}}.new.webhook form .help{margin-left:25px}.new.webhook .events.fields .column{padding-left:40px}.githook textarea{font-family:monospace}.repository{padding-top:15px;padding-bottom:80px}.repository .header-grid{padding-top:5px;padding-bottom:5px}.repository .header-grid .ui.compact.menu{margin-left:1rem}.repository .header-grid .ui.header{margin-top:0}.repository .header-grid .mega-octicon{width:30px;font-size:30px}.repository .header-grid .ui.huge.breadcrumb{font-weight:400;font-size:1.7rem}.repository .header-grid .fork-flag{margin-left:38px;margin-top:3px;display:block;font-size:12px;white-space:nowrap}.repository .header-grid .octicon.octicon-repo-forked{margin-top:-1px;font-size:15px}.repository .header-grid .button{margin-top:2px;margin-bottom:2px}.repository .tabs .navbar{justify-content:initial}.repository .navbar{display:flex;justify-content:space-between}.repository .navbar .ui.label{margin-top:-2px;margin-left:7px;padding:3px 5px}.repository .owner.dropdown{min-width:40%!important}.repository #file-buttons{margin-left:auto!important;font-weight:400}.repository #file-buttons .ui.button{padding:8px 10px;font-weight:400}.repository .metas .menu{max-height:300px;overflow-x:auto}.repository .metas .ui.list .hide{display:none!important}.repository .metas .ui.list .item{padding:0}.repository .metas .ui.list .label.color{padding:0 8px;margin-right:5px}.repository .metas .ui.list a{margin:2px 0}.repository .metas .ui.list a .text{color:#444}.repository .metas .ui.list a .text:hover{color:#000}.repository .header-wrapper{background-color:#FAFAFA;margin-top:-15px;padding-top:15px}.repository .header-wrapper .ui.tabs.divider{border-bottom:none}.repository .header-wrapper .ui.tabular .octicon{margin-right:5px}.repository .filter.menu .label.color{border-radius:3px;margin-left:15px;padding:0 8px}.repository .filter.menu .octicon{float:left;margin:5px -7px 0 -5px;width:16px}.repository .filter.menu .text{margin-left:.9em}.repository .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}.repository .filter.menu .dropdown.item{margin:1px;padding-right:0}.repository .select-label .item{max-width:250px;overflow:hidden;text-overflow:ellipsis}.repository .select-label .desc{padding-left:16px}.repository .ui.tabs.container{margin-top:14px;margin-bottom:0}.repository .ui.tabs.container .ui.menu{border-bottom:none}.repository .ui.tabs.divider{margin-top:0;margin-bottom:20px}.repository #clone-panel{width:350px}.repository #clone-panel input{border-radius:0;padding:5px 10px}.repository #clone-panel .clone.button{font-size:13px;padding:0 5px}.repository #clone-panel .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository #clone-panel .icon.button{padding:0 10px}.repository #clone-panel .dropdown .menu{right:0!important;left:auto!important}.repository.file.list .repo-description{display:flex;justify-content:space-between;align-items:center}.repository.file.list #repo-desc{font-size:1.2em}.repository.file.list .choose.reference .header .icon{font-size:1.4em}.repository.file.list .repo-path .divider,.repository.file.list .repo-path .section{display:inline}.repository.file.list #file-buttons{font-weight:400}.repository.file.list #file-buttons .ui.button{padding:8px 10px;font-weight:400}.repository.file.list #repo-files-table thead th{padding-top:8px;padding-bottom:5px;font-weight:400}.repository.file.list #repo-files-table thead th:first-child{display:block;position:relative;width:325%}.repository.file.list #repo-files-table thead .ui.avatar{margin-bottom:5px}.repository.file.list #repo-files-table tbody .octicon{margin-left:3px;margin-right:5px;color:#777}.repository.file.list #repo-files-table tbody .octicon.octicon-mail-reply{margin-right:10px}.repository.file.list #repo-files-table tbody .octicon.octicon-file-directory,.repository.file.list #repo-files-table tbody .octicon.octicon-file-submodule,.repository.file.list #repo-files-table tbody .octicon.octicon-file-symlink-directory{color:#1e70bf}.repository.file.list #repo-files-table td{padding-top:8px;padding-bottom:8px}.repository.file.list #repo-files-table td.message .isSigned{cursor:default}.repository.file.list #repo-files-table tr:hover{background-color:#ffE}.repository.file.list #repo-files-table .jumpable-path{color:#888}.repository.file.list .non-diff-file-content .header .icon{font-size:1em}.repository.file.list .non-diff-file-content .header .file-actions{margin-top:0;margin-bottom:-5px;padding-left:20px}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon{display:inline-block;padding:5px;margin-left:5px;line-height:1;color:#767676;vertical-align:middle;background:0 0;border:0;outline:0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon:hover{color:#4078c0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon-danger:hover{color:#bd2c00}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon.disabled{color:#bbb;cursor:default}.repository.file.list .non-diff-file-content .header .file-actions #delete-file-form{display:inline-block}.repository.file.list .non-diff-file-content .view-raw{padding:5px}.repository.file.list .non-diff-file-content .view-raw *{max-width:100%}.repository.file.list .non-diff-file-content .view-raw img{padding:5px 5px 0 5px}.repository.file.list .non-diff-file-content .plain-text{padding:1em 2em 1em 2em}.repository.file.list .non-diff-file-content .code-view *{font-size:12px;font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;line-height:20px}.repository.file.list .non-diff-file-content .code-view table{width:100%}.repository.file.list .non-diff-file-content .code-view .lines-num{vertical-align:top;text-align:right;color:#999;background:#f5f5f5;width:1%;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}.repository.file.list .non-diff-file-content .code-view .lines-num span{line-height:20px;padding:0 10px;cursor:pointer;display:block}.repository.file.list .non-diff-file-content .code-view .lines-code,.repository.file.list .non-diff-file-content .code-view .lines-num{padding:0}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs,.repository.file.list .non-diff-file-content .code-view .lines-code ol,.repository.file.list .non-diff-file-content .code-view .lines-code pre,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs,.repository.file.list .non-diff-file-content .code-view .lines-num ol,.repository.file.list .non-diff-file-content .code-view .lines-num pre{background-color:#fff;margin:0;padding:0!important}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-code ol li,.repository.file.list .non-diff-file-content .code-view .lines-code pre li,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-num ol li,.repository.file.list .non-diff-file-content .code-view .lines-num pre li{display:block;width:100%}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-code ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-code pre li.active,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-num ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-num pre li.active{background:#ffd}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-code ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-code pre li:before,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-num ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-num pre li:before{content:' '}.repository.file.list .non-diff-file-content .code-view .active{background:#ffd}.repository.file.list .sidebar{padding-left:0}.repository.file.list .sidebar .octicon{width:16px}.repository.file.editor .treepath{width:100%}.repository.file.editor .treepath input{vertical-align:middle;box-shadow:rgba(0,0,0,.0745098) 0 1px 2px inset;width:inherit;padding:7px 8px;margin-right:5px}.repository.file.editor .tabular.menu .octicon{margin-right:5px}.repository.file.editor .commit-form-wrapper{padding-left:64px}.repository.file.editor .commit-form-wrapper .commit-avatar{float:left;margin-left:-64px;width:3em;height:auto}.repository.file.editor .commit-form-wrapper .commit-form{position:relative;padding:15px;margin-bottom:10px;border:1px solid #ddd;border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form:after,.repository.file.editor .commit-form-wrapper .commit-form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.file.editor .commit-form-wrapper .commit-form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#fff}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .branch-name{display:inline-block;padding:3px 6px;font:12px Consolas,"Liberation Mono",Menlo,Courier,monospace;color:rgba(0,0,0,.65);background-color:rgba(209,227,237,.45);border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input{position:relative;margin-left:25px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input input{width:240px!important;padding-left:26px!important}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .octicon-git-branch{position:absolute;top:9px;left:10px;color:#b0c4ce}.repository.options #interval{width:100px!important;min-width:100px}.repository.options .danger .item{padding:20px 15px}.repository.options .danger .ui.divider{margin:0}.repository.new.issue .comment.form .comment .avatar{width:3em}.repository.new.issue .comment.form .content{margin-left:4em}.repository.new.issue .comment.form .content:after,.repository.new.issue .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.new.issue .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.new.issue .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.new.issue .comment.form .content:after{border-right-color:#fff}.repository.new.issue .comment.form .content .markdown{font-size:14px}.repository.new.issue .comment.form .metas{min-width:220px}.repository.new.issue .comment.form .metas .filter.menu{max-height:300px;overflow-x:auto}.repository.view.issue .title{padding-bottom:0!important}.repository.view.issue .title h1{font-weight:300;font-size:2.3rem;margin-bottom:5px}.repository.view.issue .title h1 .ui.input{font-size:.5em;vertical-align:top;width:50%;min-width:600px}.repository.view.issue .title h1 .ui.input input{font-size:1.5em;padding:6px 10px}.repository.view.issue .title .index{font-weight:300;color:#aaa;letter-spacing:-1px}.repository.view.issue .title .label{margin-right:10px}.repository.view.issue .title .edit-zone{margin-top:10px}.repository.view.issue .pull-desc code{color:#0166E6}.repository.view.issue .pull.tabular.menu{margin-bottom:10px}.repository.view.issue .pull.tabular.menu .octicon{margin-right:5px}.repository.view.issue .pull.tab.segment{border:none;padding:0;padding-top:10px;box-shadow:none;background-color:inherit}.repository.view.issue .pull .merge.box .avatar{margin-left:10px;margin-top:10px}.repository.view.issue .comment-list:before{display:block;content:"";position:absolute;margin-top:12px;margin-bottom:14px;top:0;bottom:0;left:96px;width:2px;background-color:#f3f3f3;z-index:-1}.repository.view.issue .comment-list .comment .avatar{width:3em}.repository.view.issue .comment-list .comment .tag{color:#767676;margin-top:3px;padding:2px 5px;font-size:12px;border:1px solid rgba(0,0,0,.1);border-radius:3px}.repository.view.issue .comment-list .comment .actions .item{float:left}.repository.view.issue .comment-list .comment .actions .item.tag{margin-right:5px}.repository.view.issue .comment-list .comment .actions .item.action{margin-top:6px;margin-left:10px}.repository.view.issue .comment-list .comment .content{margin-left:4em}.repository.view.issue .comment-list .comment .content>.header{font-weight:400;padding:auto 15px;position:relative;color:#767676;background-color:#f7f7f7;border-bottom:1px solid #eee;border-top-left-radius:3px;border-top-right-radius:3px}.repository.view.issue .comment-list .comment .content>.header:after,.repository.view.issue .comment-list .comment .content>.header:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.view.issue .comment-list .comment .content>.header:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.view.issue .comment-list .comment .content>.header:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.view.issue .comment-list .comment .content>.header .text{max-width:78%;padding-top:10px;padding-bottom:10px}.repository.view.issue .comment-list .comment .content .markdown{font-size:14px}.repository.view.issue .comment-list .comment .content .no-content{color:#767676;font-style:italic}.repository.view.issue .comment-list .comment .content>.bottom.segment{background:#f3f4f5}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.images::after{clear:both;content:' ';display:block}.repository.view.issue .comment-list .comment .content>.bottom.segment a{display:block;float:left;margin:5px;padding:5px;height:150px;border:solid 1px #eee;border-radius:3px;max-width:150px;background-color:#fff}.repository.view.issue .comment-list .comment .content>.bottom.segment a:before{content:' ';display:inline-block;height:100%;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.image{max-height:100%;width:auto;margin:0;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image{font-size:128px;color:#000}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image:hover{color:#000}.repository.view.issue .comment-list .comment .ui.form .field:first-child{clear:none}.repository.view.issue .comment-list .comment .ui.form .tab.segment{border:none;padding:0;padding-top:10px}.repository.view.issue .comment-list .comment .ui.form textarea{height:200px;font-family:Consolas,monospace}.repository.view.issue .comment-list .comment .edit.buttons{margin-top:10px}.repository.view.issue .comment-list .event{position:relative;margin:15px 0 15px 79px;padding-left:25px}.repository.view.issue .comment-list .event .octicon{width:30px;float:left;text-align:center}.repository.view.issue .comment-list .event .octicon.octicon-circle-slash{margin-top:5px;margin-left:-34.5px;font-size:20px;color:#bd2c00}.repository.view.issue .comment-list .event .octicon.octicon-primitive-dot{margin-left:-28.5px;margin-right:-1px;font-size:30px;color:#6cc644}.repository.view.issue .comment-list .event .octicon.octicon-bookmark{margin-top:3px;margin-left:-31px;margin-right:-1px;font-size:25px}.repository.view.issue .comment-list .event .detail{font-size:.9rem;margin-top:5px;margin-left:35px}.repository.view.issue .comment-list .event .detail .octicon.octicon-git-commit{margin-top:2px}.repository.view.issue .ui.segment.metas{margin-top:-3px}.repository.view.issue .ui.participants img{margin-top:5px;margin-right:5px}.repository .comment.form .ui.comments{margin-top:-12px;max-width:100%}.repository .comment.form .content .field:first-child{clear:none}.repository .comment.form .content .form:after,.repository .comment.form .content .form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository .comment.form .content .form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository .comment.form .content .form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository .comment.form .content .form:after{border-right-color:#fff}.repository .comment.form .content .tab.segment{border:none;padding:0;padding-top:10px}.repository .comment.form .content textarea{height:200px;font-family:Consolas,monospace}.repository .label.list{list-style:none;padding-top:15px}.repository .label.list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .label.list .item a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .label.list .item a:hover{color:#000}.repository .label.list .item a.open-issues{margin-right:30px}.repository .label.list .item .ui.label{font-size:1em}.repository .milestone.list{list-style:none;padding-top:15px}.repository .milestone.list>.item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .milestone.list>.item>a{padding-top:5px;padding-right:10px;color:#000}.repository .milestone.list>.item>a:hover{color:#4078c0}.repository .milestone.list>.item .ui.progress{width:40%;padding:0;border:0;margin:0}.repository .milestone.list>.item .ui.progress .bar{height:20px}.repository .milestone.list>.item .meta{color:#999;padding-top:5px}.repository .milestone.list>.item .meta .issue-stats .octicon{padding-left:5px}.repository .milestone.list>.item .meta .overdue{color:red}.repository .milestone.list>.item .operate{margin-top:-15px}.repository .milestone.list>.item .operate>a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .milestone.list>.item .operate>a:hover{color:#000}.repository .milestone.list>.item .content{padding-top:10px}.repository.new.milestone textarea{height:200px}.repository.new.milestone #deadline{width:150px}.repository.compare.pull .choose.branch .octicon{padding-right:10px}.repository.compare.pull .comment.form .content:after,.repository.compare.pull .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.compare.pull .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.compare.pull .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.compare.pull .comment.form .content:after{border-right-color:#fff}.repository .filter.dropdown .menu{margin-top:1px!important}.repository.commits .header .search input{font-weight:400;padding:5px 10px}.repository #commits-table thead th:first-of-type{padding-left:15px}.repository #commits-table thead .sha{width:140px}.repository #commits-table thead .shatd{text-align:center}.repository #commits-table td.sha .sha.label{margin:0}.repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n){background-color:rgba(0,0,0,.02)!important}.repository #commits-table td.sha .sha.label.isSigned,.repository #repo-files-table .sha.label.isSigned{border:1px solid #BBB}.repository #commits-table td.sha .sha.label.isSigned .detail.icon,.repository #repo-files-table .sha.label.isSigned .detail.icon{background:#FAFAFA;margin:-6px -10px -4px 0;padding:5px 3px 5px 6px;border-left:1px solid #BBB;border-top-left-radius:0;border-bottom-left-radius:0}.repository #commits-table td.sha .sha.label.isSigned.isVerified,.repository #repo-files-table .sha.label.isSigned.isVerified{border:1px solid #21BA45;background:#21BA4518}.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon{border-left:1px solid #21BA4580}.repository .diff-detail-box{margin:15px 0;line-height:30px}.repository .diff-detail-box ol{clear:both;padding-left:0;margin-top:5px;margin-bottom:28px}.repository .diff-detail-box ol li{list-style:none;padding-bottom:4px;margin-bottom:4px;border-bottom:1px dashed #DDD;padding-left:6px}.repository .diff-detail-box span.status{display:inline-block;width:12px;height:12px;margin-right:8px;vertical-align:middle}.repository .diff-detail-box span.status.modify{background-color:#f0db88}.repository .diff-detail-box span.status.add{background-color:#b4e2b4}.repository .diff-detail-box span.status.del{background-color:#e9aeae}.repository .diff-detail-box span.status.rename{background-color:#dad8ff}.repository .diff-detail-box .ui.right{margin-bottom:15px}.repository .diff-box .header{display:flex;align-items:center}.repository .diff-box .header .count{margin-right:12px;font-size:13px;flex:0 0 auto}.repository .diff-box .header .count .bar{background-color:#bd2c00;height:12px;width:40px;display:inline-block;margin:2px 4px 0 4px;vertical-align:text-top}.repository .diff-box .header .count .bar .add{background-color:#55a532;height:12px}.repository .diff-box .header .file{flex:1;color:#888;word-break:break-all}.repository .diff-box .header .button{margin:-5px 0 -5px 12px;padding:8px 10px;flex:0 0 auto}.repository .diff-file-box .header{background-color:#f7f7f7}.repository .diff-file-box .file-body.file-code .lines-num{text-align:right;color:#A7A7A7;background:#fafafa;width:1%;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;vertical-align:top}.repository .diff-file-box .file-body.file-code .lines-num span.fold{display:block;text-align:center}.repository .diff-file-box .file-body.file-code .lines-num-old{border-right:1px solid #DDD}.repository .diff-file-box .code-diff{font-size:12px}.repository .diff-file-box .code-diff td{padding:0;padding-left:10px;border-top:none}.repository .diff-file-box .code-diff pre{margin:0}.repository .diff-file-box .code-diff .lines-num{border-color:#d4d4d5;border-right-width:1px;border-right-style:solid;padding:0 5px}.repository .diff-file-box .code-diff tbody tr td.halfwidth{width:49%}.repository .diff-file-box .code-diff tbody tr td.tag-code,.repository .diff-file-box .code-diff tbody tr.tag-code td{background-color:#F0F0F0!important;border-color:#D2CECE!important;padding-top:8px;padding-bottom:8px}.repository .diff-file-box .code-diff tbody tr .removed-code{background-color:#f99}.repository .diff-file-box .code-diff tbody tr .added-code{background-color:#9f9}.repository .diff-file-box .code-diff-unified tbody tr.del-code td{background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-unified tbody tr.add-code td{background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split table,.repository .diff-file-box .code-diff-split tbody{width:100%}.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(2),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(4){background-color:#fafafa}.repository .diff-file-box .code-diff-split tbody tr td.del-code,.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(2){background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-split tbody tr td.add-code,.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(4){background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split tbody tr td:nth-child(3){border-left-width:1px;border-left-style:solid}.repository .diff-file-box.file-content{clear:right}.repository .diff-file-box.file-content img{max-width:100%;padding:5px 5px 0 5px}.repository .code-view{overflow:auto;overflow-x:auto;overflow-y:hidden}.repository .repo-search-result{padding-top:10px;padding-bottom:10px}.repository .repo-search-result .lines-num a{color:inherit}.repository.quickstart .guide .item{padding:1em}.repository.quickstart .guide .item small{font-weight:400}.repository.quickstart .guide .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository.quickstart .guide .ui.action.small.input{width:100%}.repository.quickstart .guide #repo-clone-url{border-radius:0;padding:5px 10px;font-size:1.2em}.repository.release #release-list{border-top:1px solid #DDD;margin-top:20px;padding-top:15px}.repository.release #release-list>li{list-style:none}.repository.release #release-list>li .detail,.repository.release #release-list>li .meta{padding-top:30px;padding-bottom:40px}.repository.release #release-list>li .meta{text-align:right;position:relative}.repository.release #release-list>li .meta .tag:not(.icon){display:block;margin-top:15px}.repository.release #release-list>li .meta .commit{display:block;margin-top:10px}.repository.release #release-list>li .detail{border-left:1px solid #DDD}.repository.release #release-list>li .detail .author img{margin-bottom:-3px}.repository.release #release-list>li .detail .download{margin-top:20px}.repository.release #release-list>li .detail .download>a .octicon{margin-left:5px;margin-right:5px}.repository.release #release-list>li .detail .download .list{padding-left:0;border-top:1px solid #eee}.repository.release #release-list>li .detail .download .list li{list-style:none;display:block;padding-top:8px;padding-bottom:8px;border-bottom:1px solid #eee}.repository.release #release-list>li .detail .dot{width:9px;height:9px;background-color:#ccc;z-index:999;position:absolute;display:block;left:-5px;top:40px;border-radius:6px;border:1px solid #FFF}.repository.new.release .target{min-width:500px}.repository.new.release .target #tag-name{margin-top:-4px}.repository.new.release .target .at{margin-left:-5px;margin-right:5px}.repository.new.release .target .dropdown.icon{margin:0;padding-top:3px}.repository.new.release .target .selection.dropdown{padding-top:10px;padding-bottom:10px}.repository.new.release .prerelease.field{margin-bottom:0}.repository.forks .list{margin-top:0}.repository.forks .list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px solid #DDD}.repository.forks .list .item .ui.avatar{float:left;margin-right:5px}.repository.forks .list .item .link{padding-top:5px}.repository.wiki.start .ui.segment{padding-top:70px;padding-bottom:100px}.repository.wiki.start .ui.segment .mega-octicon{font-size:48px}.repository.wiki.new .CodeMirror .CodeMirror-code{font-family:Consolas,monospace}.repository.wiki.new .CodeMirror .CodeMirror-code .cm-comment{background:inherit}.repository.wiki.new .editor-preview{background-color:#fff}.repository.wiki.view .choose.page{margin-top:-5px}.repository.wiki.view .ui.sub.header{text-transform:none}.repository.wiki.view>.markdown{padding:15px 30px}.repository.wiki.view>.markdown h1:first-of-type,.repository.wiki.view>.markdown h2:first-of-type,.repository.wiki.view>.markdown h3:first-of-type,.repository.wiki.view>.markdown h4:first-of-type,.repository.wiki.view>.markdown h5:first-of-type,.repository.wiki.view>.markdown h6:first-of-type{margin-top:0}@media only screen and (max-width:767px){.repository.wiki .dividing.header .stackable.grid .button{margin-top:2px;margin-bottom:2px}}.repository.settings.collaboration .collaborator.list{padding:0}.repository.settings.collaboration .collaborator.list>.item{margin:0;line-height:2em}.repository.settings.collaboration .collaborator.list>.item:not(:last-child){border-bottom:1px solid #DDD}.repository.settings.collaboration #repo-collab-form #search-user-box .results{left:7px}.repository.settings.collaboration #repo-collab-form .ui.button{margin-left:5px;margin-top:-3px}.repository.settings.branches .protected-branches .selection.dropdown{width:300px}.repository.settings.branches .protected-branches .item{border:1px solid #eaeaea;padding:10px 15px}.repository.settings.branches .protected-branches .item:not(:last-child){border-bottom:0}.repository.settings.branches .branch-protection .help{margin-left:26px;padding-top:0}.repository.settings.branches .branch-protection .fields{margin-left:20px;display:block}.repository.settings.branches .branch-protection .whitelist{margin-left:26px}.repository.settings.branches .branch-protection .whitelist .dropdown img{display:inline-block}.repository.settings.webhook .events .column{padding-bottom:0}.repository.settings.webhook .events .help{font-size:13px;margin-left:26px;padding-top:0}.repository .ui.attached.isSigned.isVerified:not(.positive){border-left:1px solid #A3C293;border-right:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified.top:not(.positive){border-top:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified:not(.positive):last-child{border-bottom:1px solid #A3C293}.repository .ui.segment.sub-menu{padding:7px;line-height:0}.repository .ui.segment.sub-menu .list{width:100%;display:flex}.repository .ui.segment.sub-menu .list .item{width:100%;border-radius:3px}.repository .ui.segment.sub-menu .list .item a{color:#000}.repository .ui.segment.sub-menu .list .item a:hover{color:#666}.repository .ui.segment.sub-menu .list .item.active{background:rgba(0,0,0,.05)}.repository .segment.reactions.dropdown .menu,.repository .select-reaction.dropdown .menu{right:0!important;left:auto!important}.repository .segment.reactions.dropdown .menu>.header,.repository .select-reaction.dropdown .menu>.header{margin:.75rem 0 .5rem}.repository .segment.reactions.dropdown .menu>.item,.repository .select-reaction.dropdown .menu>.item{float:left;padding:.5rem .5rem!important}.repository .segment.reactions.dropdown .menu>.item img.emoji,.repository .select-reaction.dropdown .menu>.item img.emoji{margin-right:0}.repository .segment.reactions{padding:.3em 1em}.repository .segment.reactions .ui.label{padding:.4em}.repository .segment.reactions .ui.label.disabled{cursor:default}.repository .segment.reactions .ui.label>img{height:1.5em!important}.repository .segment.reactions .select-reaction{float:none}.repository .segment.reactions .select-reaction:not(.active) a{display:none}.repository .segment.reactions:hover .select-reaction a{display:block}.user-cards .list{padding:0}.user-cards .list .item{list-style:none;width:32%;margin:10px 10px 10px 0;padding-bottom:14px;float:left}.user-cards .list .item .avatar{width:48px;height:48px;float:left;display:block;margin-right:10px}.user-cards .list .item .name{margin-top:0;margin-bottom:0;font-weight:400}.user-cards .list .item .meta{margin-top:5px}#search-repo-box .results .result .image,#search-user-box .results .result .image{float:left;margin-right:8px;width:2em;height:2em}#search-repo-box .results .result .content,#search-user-box .results .result .content{margin:6px 0}#issue-actions{display:none}.issue.list{list-style:none;padding-top:15px}.issue.list>.item{padding-top:15px;padding-bottom:10px;border-bottom:1px dashed #AAA}.issue.list>.item .title{color:#444;font-size:15px;font-weight:700;margin:0 6px}.issue.list>.item .title:hover{color:#000}.issue.list>.item .comment{padding-right:10px;color:#666}.issue.list>.item .desc{padding-top:5px;color:#999}.issue.list>.item .desc .checklist{padding-left:5px}.issue.list>.item .desc .checklist .progress-bar{margin-left:2px;width:80px;height:6px;display:inline-block;background-color:#eee;overflow:hidden;border-radius:3px;vertical-align:2px!important}.issue.list>.item .desc .checklist .progress-bar .progress{background-color:#ccc;display:block;height:100%}.issue.list>.item .desc a.milestone{padding-left:5px;color:#999!important}.issue.list>.item .desc a.milestone:hover{color:#000!important}.issue.list>.item .desc .assignee{margin-top:-5px;margin-right:5px}.issue.list>.item .desc .overdue{color:red}.page.buttons{padding-top:15px}.ui.form .dropzone{width:100%;margin-bottom:10px;border:2px dashed #0087F7;box-shadow:none!important}.ui.form .dropzone .dz-error-message{top:140px}.settings .content{margin-top:2px}.settings .content .segment,.settings .content>.header{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.settings .list>.item .green{color:#21BA45!important}.settings .list>.item:not(:first-child){border-top:1px solid #eaeaea;padding:1rem;margin:15px -1rem -1rem -1rem}.settings .list>.item>.mega-octicon{display:table-cell}.settings .list>.item>.mega-octicon+.content{display:table-cell;padding:0 0 0 .5em;vertical-align:top}.settings .list>.item .info{margin-top:10px}.settings .list>.item .info .tab.segment{border:none;padding:10px 0 0}.settings .list.key .meta{padding-top:5px;color:#666}.settings .list.email>.item:not(:first-child){min-height:60px}.settings .list.collaborator>.item{padding:0}.ui.vertical.menu .header.item{font-size:1.1em;background:#f0f0f0}.edit-label.modal .form .column,.new-label.segment .form .column{padding-right:0}.edit-label.modal .form .buttons,.new-label.segment .form .buttons{margin-left:auto;padding-top:15px}.edit-label.modal .form .color.picker.column,.new-label.segment .form .color.picker.column{width:auto}.edit-label.modal .form .color.picker.column .color-picker,.new-label.segment .form .color.picker.column .color-picker{height:35px;width:auto;padding-left:30px}.edit-label.modal .form .minicolors-swatch.minicolors-sprite,.new-label.segment .form .minicolors-swatch.minicolors-sprite{top:10px;left:10px;width:15px;height:15px}.edit-label.modal .form .precolors,.new-label.segment .form .precolors{padding-left:0;padding-right:0;margin:3px 10px auto 10px;width:120px}.edit-label.modal .form .precolors .color,.new-label.segment .form .precolors .color{float:left;width:15px;height:15px}#avatar-arrow:after,#avatar-arrow:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}#avatar-arrow:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}#avatar-arrow:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}#delete-repo-modal .ui.message,#transfer-repo-modal .ui.message{width:100%!important}.tab-size-1{tab-size:1!important;-moz-tab-size:1!important}.tab-size-2{tab-size:2!important;-moz-tab-size:2!important}.tab-size-3{tab-size:3!important;-moz-tab-size:3!important}.tab-size-4{tab-size:4!important;-moz-tab-size:4!important}.tab-size-5{tab-size:5!important;-moz-tab-size:5!important}.tab-size-6{tab-size:6!important;-moz-tab-size:6!important}.tab-size-7{tab-size:7!important;-moz-tab-size:7!important}.tab-size-8{tab-size:8!important;-moz-tab-size:8!important}.tab-size-9{tab-size:9!important;-moz-tab-size:9!important}.tab-size-10{tab-size:10!important;-moz-tab-size:10!important}.tab-size-11{tab-size:11!important;-moz-tab-size:11!important}.tab-size-12{tab-size:12!important;-moz-tab-size:12!important}.tab-size-13{tab-size:13!important;-moz-tab-size:13!important}.tab-size-14{tab-size:14!important;-moz-tab-size:14!important}.tab-size-15{tab-size:15!important;-moz-tab-size:15!important}.tab-size-16{tab-size:16!important;-moz-tab-size:16!important}.stats-table{display:table;width:100%}.stats-table .table-cell{display:table-cell}.stats-table .table-cell.tiny{height:.5em}tbody.commit-list{vertical-align:baseline}.commit-body{white-space:pre-wrap}@media only screen and (max-width:767px){.ui.stackable.menu.mobile--margin-between-items>.item{margin-top:5px;margin-bottom:5px}.ui.stackable.menu.mobile--no-negative-margins{margin-left:0;margin-right:0}}#topic_edit{margin-top:5px;display:none}#repo-topic{margin-top:5px}.CodeMirror{font:14px Consolas,"Liberation Mono",Menlo,Courier,monospace}.CodeMirror.cm-s-default{border-radius:3px;padding:0!important}.CodeMirror .cm-comment{background:inherit!important}.repository.file.editor .tab[data-tab=write]{padding:0!important}.repository.file.editor .tab[data-tab=write] .editor-toolbar{border:none!important}.repository.file.editor .tab[data-tab=write] .CodeMirror{border-left:none;border-right:none;border-bottom:none}.organization{padding-top:15px;padding-bottom:80px}.organization .head .ui.header .text{vertical-align:middle;font-size:1.6rem;margin-left:15px}.organization .head .ui.header .ui.right{margin-top:5px}.organization.new.org form{margin:auto}.organization.new.org form .ui.message{text-align:center}@media only screen and (min-width:768px){.organization.new.org form{width:800px!important}.organization.new.org form .header{padding-left:280px!important}.organization.new.org form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.organization.new.org form .help{margin-left:265px!important}.organization.new.org form .optional .title{margin-left:250px!important}.organization.new.org form input,.organization.new.org form textarea{width:50%!important}}@media only screen and (max-width:767px){.organization.new.org form .optional .title{margin-left:15px}.organization.new.org form .inline.field>label{display:block}}.organization.new.org form .header{padding-left:0!important;text-align:center}.organization.options input{min-width:300px}.organization.profile #org-avatar{width:100px;height:100px;margin-right:15px}.organization.profile #org-info .ui.header{font-size:36px;margin-bottom:0}.organization.profile #org-info .desc{font-size:16px;margin-bottom:10px}.organization.profile #org-info .meta .item{display:inline-block;margin-right:10px}.organization.profile #org-info .meta .item .icon{margin-right:5px}.organization.profile .ui.top.header .ui.right{margin-top:0}.organization.profile .teams .item{padding:10px 15px}.organization.profile .members .ui.avatar,.organization.teams .members .ui.avatar{width:48px;height:48px;margin-right:5px}.organization.invite #invite-box{margin:auto;margin-top:50px;width:500px!important}.organization.invite #invite-box #search-user-box input{margin-left:0;width:300px}.organization.invite #invite-box .ui.button{margin-left:5px;margin-top:-3px}.organization.members .list .item{margin-left:0;margin-right:0;border-bottom:1px solid #eee}.organization.members .list .item .ui.avatar{width:48px;height:48px}.organization.members .list .item .meta{line-height:24px}.organization.teams .detail .item{padding:10px 15px}.organization.teams .detail .item:not(:last-child){border-bottom:1px solid #eee}.organization.teams .members .item,.organization.teams .repositories .item{padding:10px 20px;line-height:32px}.organization.teams .members .item:not(:last-child),.organization.teams .repositories .item:not(:last-child){border-bottom:1px solid #DDD}.organization.teams .members .item .button,.organization.teams .repositories .item .button{padding:9px 10px}.organization.teams #add-member-form input,.organization.teams #add-repo-form input{margin-left:0}.organization.teams #add-member-form .ui.button,.organization.teams #add-repo-form .ui.button{margin-left:5px;margin-top:-3px}.user:not(.icon){padding-top:15px;padding-bottom:80px}.user.profile .ui.card .username{display:block}.user.profile .ui.card .extra.content{padding:0}.user.profile .ui.card .extra.content ul{margin:0;padding:0}.user.profile .ui.card .extra.content ul li{padding:10px;list-style:none}.user.profile .ui.card .extra.content ul li:not(:last-child){border-bottom:1px solid #eaeaea}.user.profile .ui.card .extra.content ul li .octicon{margin-left:1px;margin-right:5px}.user.profile .ui.card .extra.content ul li.follow .ui.button{width:100%}.user.profile .ui.repository.list{margin-top:25px}.user.followers .header.name{font-size:20px;line-height:24px;vertical-align:middle}.user.followers .follow .ui.button{padding:8px 15px}.user.notification .octicon{float:left;font-size:2em}.user.notification .content{float:left;margin-left:7px}.user.notification table form{display:inline-block}.user.notification table button{padding:3px 3px 3px 5px}.user.notification table tr{cursor:pointer}.user.notification .octicon.green{color:#21ba45}.user.notification .octicon.red{color:#d01919}.user.notification .octicon.purple{color:#a333c8}.user.notification .octicon.blue{color:#2185d0}.user.link-account:not(.icon){padding-top:15px;padding-bottom:5px}.user.settings .iconFloat{float:left}.dashboard{padding-top:15px;padding-bottom:80px}.dashboard.feeds .context.user.menu,.dashboard.issues .context.user.menu{z-index:101;min-width:200px}.dashboard.feeds .context.user.menu .ui.header,.dashboard.issues .context.user.menu .ui.header{font-size:1rem;text-transform:none}.dashboard.feeds .filter.menu .item,.dashboard.issues .filter.menu .item{text-align:left}.dashboard.feeds .filter.menu .item .text,.dashboard.issues .filter.menu .item .text{height:16px;vertical-align:middle}.dashboard.feeds .filter.menu .item .text.truncate,.dashboard.issues .filter.menu .item .text.truncate{width:85%}.dashboard.feeds .filter.menu .item .floating.label,.dashboard.issues .filter.menu .item .floating.label{top:7px;left:90%;width:15%}.dashboard.feeds .filter.menu .jump.item,.dashboard.issues .filter.menu .jump.item{margin:1px;padding-right:0}.dashboard.feeds .filter.menu .menu,.dashboard.issues .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}.dashboard.feeds .right.stackable.menu>.item.active,.dashboard.issues .right.stackable.menu>.item.active{color:#d9453d}.dashboard .dashboard-repos{margin:0 1px}.feeds .news>.ui.grid{margin-left:auto;margin-right:auto}.feeds .news .ui.avatar{margin-top:13px}.feeds .news p{line-height:1em}.feeds .news .time-since{font-size:13px}.feeds .news .issue.title{width:80%}.feeds .news .push.news .content ul{font-size:13px;list-style:none;padding-left:10px}.feeds .news .push.news .content ul img{margin-bottom:-2px}.feeds .news .push.news .content ul .text.truncate{width:80%;margin-bottom:-5px}.feeds .news .commit-id{font-family:Consolas,monospace}.feeds .news code{padding:1px;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px;word-break:break-all}.feeds .list .header .ui.label{margin-top:-4px;padding:4px 5px;font-weight:400}.feeds .list .header .plus.icon{margin-top:5px}.feeds .list ul{list-style:none;margin:0;padding-left:0}.feeds .list ul li:not(:last-child){border-bottom:1px solid #EAEAEA}.feeds .list ul li.private{background-color:#fcf8e9}.feeds .list ul li a{padding:6px 1.2em;display:block}.feeds .list ul li a .octicon{color:#888}.feeds .list ul li a .octicon.rear{font-size:15px}.feeds .list ul li a .star-num{font-size:12px}.feeds .list .repo-owner-name-list .item-name{max-width:70%;margin-bottom:-4px}.feeds .list #collaborative-repo-list .owner-and-repo{max-width:80%;margin-bottom:-5px}.feeds .list #collaborative-repo-list .owner-name{max-width:120px;margin-bottom:-5px}.admin{padding-top:15px;padding-bottom:80px}.admin .table.segment{padding:0;font-size:13px}.admin .table.segment:not(.striped){padding-top:5px}.admin .table.segment:not(.striped) thead th:last-child{padding-right:5px!important}.admin .table.segment th{padding-top:5px;padding-bottom:5px}.admin .table.segment:not(.select) td:first-of-type,.admin .table.segment:not(.select) th:first-of-type{padding-left:15px!important}.admin .ui.header,.admin .ui.segment{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.admin.user .email{max-width:200px}.admin dl.admin-dl-horizontal{padding:20px;margin:0}.admin dl.admin-dl-horizontal dd{margin-left:275px}.admin dl.admin-dl-horizontal dt{font-weight:bolder;float:left;width:285px;clear:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.admin.config #test-mail-btn{margin-left:5px}.explore{padding-top:15px;padding-bottom:80px}.explore .navbar{justify-content:center;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}.explore .navbar .octicon{width:16px;text-align:center;margin-right:5px}.ui.repository.list .item{padding-bottom:25px}.ui.repository.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.repository.list .item .ui.header{font-size:1.5rem;padding-bottom:10px}.ui.repository.list .item .ui.header .name{word-break:break-all}.ui.repository.list .item .ui.header .metas{color:#888;font-size:14px;font-weight:400}.ui.repository.list .item .ui.header .metas span:not(:last-child){margin-right:5px}.ui.repository.list .item .time{font-size:12px;color:grey}.ui.repository.branches .time{font-size:12px;color:grey}.ui.user.list .item{padding-bottom:25px}.ui.user.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.user.list .item .ui.avatar.image{width:40px;height:40px}.ui.user.list .item .description{margin-top:5px}.ui.user.list .item .description .octicon:not(:first-child){margin-left:5px}.ui.user.list .item .description a{color:#333}.ui.user.list .item .description a:hover{text-decoration:underline} \ No newline at end of file +.tribute-container{box-shadow:0 1px 3px 1px #c7c7c7}.tribute-container ul{background:#fff}.tribute-container li{padding:8px 12px;border-bottom:1px solid #dcdcdc}.tribute-container li img{display:inline-block;vertical-align:middle;width:28px;height:28px;margin-right:5px}.tribute-container li span.fullname{font-weight:400;font-size:.8rem;margin-left:3px}.tribute-container li.highlight,.tribute-container li:hover{background:#2185D0;color:#fff}.emoji{width:1.5em;height:1.5em;display:inline-block;background-size:contain}body{font-family:Lato,"Segoe UI","Microsoft YaHei",Arial,Helvetica,sans-serif!important;background-color:#fff;overflow-y:scroll;-webkit-font-smoothing:antialiased}img{border-radius:3px}.rounded{border-radius:.28571429rem!important}code,pre{font:12px Consolas,"Liberation Mono",Menlo,Courier,monospace}code.raw,pre.raw{padding:7px 12px;margin:10px 0;background-color:#f8f8f8;border:1px solid #ddd;border-radius:3px;font-size:13px;line-height:1.5;overflow:auto}code.wrap,pre.wrap{white-space:pre-wrap;-ms-word-break:break-all;word-break:break-all;overflow-wrap:break-word;word-wrap:break-word}.dont-break-out{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.full.height{padding:0;margin:0 0 -40px 0;min-height:100%}.following.bar{z-index:900;left:0;width:100%;margin:0}.following.bar.light{background-color:#fff;border-bottom:1px solid #DDD;box-shadow:0 2px 3px rgba(0,0,0,.04)}.following.bar .column .menu{margin-top:0}.following.bar .top.menu a.item.brand{padding-left:0}.following.bar .brand .ui.mini.image{width:30px}.following.bar .top.menu .dropdown.item.active,.following.bar .top.menu .dropdown.item:hover,.following.bar .top.menu a.item:hover{background-color:transparent}.following.bar .top.menu a.item:hover{color:rgba(0,0,0,.45)}.following.bar .top.menu .menu{z-index:900}.following.bar .octicon{margin-right:.75em}.following.bar .octicon.fitted{margin-right:0}.following.bar .searchbox{background-color:#f4f4f4!important}.following.bar .searchbox:focus{background-color:#e9e9e9!important}.following.bar .text .octicon{width:16px;text-align:center}@media only screen and (max-width:767px){.following.bar #navbar:not(.shown)>:not(:first-child){display:none}}.right.stackable.menu{margin-left:auto;display:flex;display:-ms-flexbox;-ms-flex-align:inherit;align-items:inherit;-ms-flex-direction:inherit;flex-direction:inherit}.ui.left{float:left}.ui.right{float:right}.ui.button,.ui.menu .item{-moz-user-select:auto;-ms-user-select:auto;-webkit-user-select:auto;user-select:auto}.ui.container.fluid.padded{padding:0 10px 0 10px}.ui.form .ui.button{font-weight:400}.ui.floating.label{z-index:10}.ui.menu,.ui.segment,.ui.vertical.menu{box-shadow:none}.ui .menu:not(.vertical) .item>.button.compact{padding:.58928571em 1.125em}.ui .menu:not(.vertical) .item>.button.small{font-size:.92857143rem}.ui.dropdown .menu>.item>.floating.label{z-index:11}.ui.dropdown .menu .menu>.item>.floating.label{z-index:21}.ui .text.red{color:#d95c5c!important}.ui .text.red a{color:#d95c5c!important}.ui .text.red a:hover{color:#E67777!important}.ui .text.blue{color:#428bca!important}.ui .text.blue a{color:#15c!important}.ui .text.blue a:hover{color:#428bca!important}.ui .text.black{color:#444}.ui .text.black:hover{color:#000}.ui .text.grey{color:#767676!important}.ui .text.grey a{color:#444!important}.ui .text.grey a:hover{color:#000!important}.ui .text.light.grey{color:#888!important}.ui .text.green{color:#6cc644!important}.ui .text.purple{color:#6e5494!important}.ui .text.yellow{color:#FBBD08!important}.ui .text.gold{color:#a1882b!important}.ui .text.left{text-align:left!important}.ui .text.right{text-align:right!important}.ui .text.small{font-size:.75em}.ui .text.normal{font-weight:400}.ui .text.bold{font-weight:700}.ui .text.italic{font-style:italic}.ui .text.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.ui .text.thin{font-weight:400}.ui .text.middle{vertical-align:middle}.ui .message{text-align:center}.ui .header>i+.content{padding-left:.75rem;vertical-align:middle}.ui .warning.header{background-color:#F9EDBE!important;border-color:#F0C36D}.ui .warning.segment{border-color:#F0C36D}.ui .info.segment{border:1px solid #c5d5dd}.ui .info.segment.top{background-color:#e6f1f6!important}.ui .info.segment.top h3,.ui .info.segment.top h4{margin-top:0}.ui .info.segment.top h3:last-child{margin-top:4px}.ui .info.segment.top>:last-child{margin-bottom:0}.ui .normal.header{font-weight:400}.ui .avatar.image{border-radius:3px}.ui .form .fake{display:none!important}.ui .form .sub.field{margin-left:25px}.ui .sha.label{font-family:Consolas,Menlo,Monaco,"Lucida Console",monospace;font-size:13px;padding:6px 10px 4px 10px;font-weight:400;margin:0 6px}.ui.status.buttons .octicon{margin-right:4px}.ui.inline.delete-button{padding:8px 15px;font-weight:400}.ui .background.red{background-color:#d95c5c!important}.ui .background.blue{background-color:#428bca!important}.ui .background.black{background-color:#444}.ui .background.grey{background-color:#767676!important}.ui .background.light.grey{background-color:#888!important}.ui .background.green{background-color:#6cc644!important}.ui .background.purple{background-color:#6e5494!important}.ui .background.yellow{background-color:#FBBD08!important}.ui .background.gold{background-color:#a1882b!important}.ui .branch-tag-choice{line-height:20px}.overflow.menu .items{max-height:300px;overflow-y:auto}.overflow.menu .items .item{position:relative;cursor:pointer;display:block;border:none;height:auto;border-top:none;line-height:1em;color:rgba(0,0,0,.8);padding:.71428571em 1.14285714em!important;font-size:1rem;text-transform:none;font-weight:400;box-shadow:none;-webkit-touch-callout:none}.overflow.menu .items .item.active{font-weight:700}.overflow.menu .items .item:hover{background:rgba(0,0,0,.05);color:rgba(0,0,0,.8);z-index:13}.scrolling.menu .item.selected{font-weight:700!important}footer{height:40px;background-color:#fff;border-top:1px solid #d6d6d6;clear:both;width:100%;color:#888}footer .container{padding-top:10px}footer .container .fa{width:16px;text-align:center;color:#428bca}footer .container .links>*{border-left:1px solid #d6d6d6;padding-left:8px;margin-left:5px}footer .container .links>:first-child{border-left:none}footer .ui.language .menu{max-height:500px;overflow-y:auto;margin-bottom:7px}.hide{display:none}.center{text-align:center}.img-1{width:2px!important;height:2px!important}.img-2{width:4px!important;height:4px!important}.img-3{width:6px!important;height:6px!important}.img-4{width:8px!important;height:8px!important}.img-5{width:10px!important;height:10px!important}.img-6{width:12px!important;height:12px!important}.img-7{width:14px!important;height:14px!important}.img-8{width:16px!important;height:16px!important}.img-9{width:18px!important;height:18px!important}.img-10{width:20px!important;height:20px!important}.img-11{width:22px!important;height:22px!important}.img-12{width:24px!important;height:24px!important}.img-13{width:26px!important;height:26px!important}.img-14{width:28px!important;height:28px!important}.img-15{width:30px!important;height:30px!important}.img-16{width:32px!important;height:32px!important}@media only screen and (min-width:768px){.mobile-only,.ui.button.mobile-only{display:none}.sr-mobile-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}}@media only screen and (max-width:767px){.not-mobile{display:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}@media only screen and (max-width:991px) and (min-width:768px){.ui.container{width:95%}}.hljs{background:inherit!important;padding:0!important}.ui.menu.new-menu{justify-content:center!important;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}@media only screen and (max-width:1200px){.ui.menu.new-menu{overflow-x:auto!important;justify-content:left!important;padding-bottom:5px}.ui.menu.new-menu::-webkit-scrollbar{height:8px;display:none}.ui.menu.new-menu:hover::-webkit-scrollbar{display:block}.ui.menu.new-menu::-webkit-scrollbar-track{background:rgba(0,0,0,.01)}.ui.menu.new-menu::-webkit-scrollbar-thumb{background:rgba(0,0,0,.2)}.ui.menu.new-menu:after{position:absolute;margin-top:-15px;display:block;background-image:linear-gradient(to right,rgba(255,255,255,0),#fff 100%);content:' ';right:0;height:53px;z-index:1000;width:60px;clear:none;visibility:visible}.ui.menu.new-menu a.item:last-child{padding-right:30px!important}}[v-cloak]{display:none!important}.repos-search{padding-bottom:0!important}.repos-filter{margin-top:0!important;border-bottom-width:0!important;margin-bottom:2px!important}.markdown:not(code){overflow:hidden;font-family:"Helvetica Neue",Helvetica,"Segoe UI",Arial,freesans,sans-serif;font-size:16px;line-height:1.6!important;word-wrap:break-word}.markdown:not(code).ui.segment{padding:3em}.markdown:not(code).file-view{padding:2em 2em 2em!important}.markdown:not(code)>:first-child{margin-top:0!important}.markdown:not(code)>:last-child{margin-bottom:0!important}.markdown:not(code) a:not([href]){color:inherit;text-decoration:none}.markdown:not(code) .absent{color:#c00}.markdown:not(code) .anchor{position:absolute;top:0;left:0;display:block;padding-right:6px;padding-left:30px;margin-left:-30px}.markdown:not(code) .anchor:focus{outline:0}.markdown:not(code) h1,.markdown:not(code) h2,.markdown:not(code) h3,.markdown:not(code) h4,.markdown:not(code) h5,.markdown:not(code) h6{position:relative;margin-top:1em;margin-bottom:16px;font-weight:700;line-height:1.4}.markdown:not(code) h1:first-of-type,.markdown:not(code) h2:first-of-type,.markdown:not(code) h3:first-of-type,.markdown:not(code) h4:first-of-type,.markdown:not(code) h5:first-of-type,.markdown:not(code) h6:first-of-type{margin-top:0!important}.markdown:not(code) h1 .octicon-link,.markdown:not(code) h2 .octicon-link,.markdown:not(code) h3 .octicon-link,.markdown:not(code) h4 .octicon-link,.markdown:not(code) h5 .octicon-link,.markdown:not(code) h6 .octicon-link{display:none;color:#000;vertical-align:middle}.markdown:not(code) h1:hover .anchor,.markdown:not(code) h2:hover .anchor,.markdown:not(code) h3:hover .anchor,.markdown:not(code) h4:hover .anchor,.markdown:not(code) h5:hover .anchor,.markdown:not(code) h6:hover .anchor{padding-left:8px;margin-left:-30px;text-decoration:none}.markdown:not(code) h1:hover .anchor .octicon-link,.markdown:not(code) h2:hover .anchor .octicon-link,.markdown:not(code) h3:hover .anchor .octicon-link,.markdown:not(code) h4:hover .anchor .octicon-link,.markdown:not(code) h5:hover .anchor .octicon-link,.markdown:not(code) h6:hover .anchor .octicon-link{display:inline-block}.markdown:not(code) h1 code,.markdown:not(code) h1 tt,.markdown:not(code) h2 code,.markdown:not(code) h2 tt,.markdown:not(code) h3 code,.markdown:not(code) h3 tt,.markdown:not(code) h4 code,.markdown:not(code) h4 tt,.markdown:not(code) h5 code,.markdown:not(code) h5 tt,.markdown:not(code) h6 code,.markdown:not(code) h6 tt{font-size:inherit}.markdown:not(code) h1{padding-bottom:.3em;font-size:2.25em;line-height:1.2;border-bottom:1px solid #eee}.markdown:not(code) h1 .anchor{line-height:1}.markdown:not(code) h2{padding-bottom:.3em;font-size:1.75em;line-height:1.225;border-bottom:1px solid #eee}.markdown:not(code) h2 .anchor{line-height:1}.markdown:not(code) h3{font-size:1.5em;line-height:1.43}.markdown:not(code) h3 .anchor{line-height:1.2}.markdown:not(code) h4{font-size:1.25em}.markdown:not(code) h4 .anchor{line-height:1.2}.markdown:not(code) h5{font-size:1em}.markdown:not(code) h5 .anchor{line-height:1.1}.markdown:not(code) h6{font-size:1em;color:#777}.markdown:not(code) h6 .anchor{line-height:1.1}.markdown:not(code) blockquote,.markdown:not(code) dl,.markdown:not(code) ol,.markdown:not(code) p,.markdown:not(code) pre,.markdown:not(code) table,.markdown:not(code) ul{margin-top:0;margin-bottom:16px}.markdown:not(code) blockquote{margin-left:0}.markdown:not(code) hr{height:4px;padding:0;margin:16px 0;background-color:#e7e7e7;border:0 none}.markdown:not(code) ol,.markdown:not(code) ul{padding-left:2em}.markdown:not(code) ol.no-list,.markdown:not(code) ul.no-list{padding:0;list-style-type:none}.markdown:not(code) ol ol,.markdown:not(code) ol ul,.markdown:not(code) ul ol,.markdown:not(code) ul ul{margin-top:0;margin-bottom:0}.markdown:not(code) ol ol,.markdown:not(code) ul ol{list-style-type:lower-roman}.markdown:not(code) li>p{margin-top:0}.markdown:not(code) dl{padding:0}.markdown:not(code) dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:700}.markdown:not(code) dl dd{padding:0 16px;margin-bottom:16px}.markdown:not(code) blockquote{padding:0 15px;color:#777;border-left:4px solid #ddd}.markdown:not(code) blockquote>:first-child{margin-top:0}.markdown:not(code) blockquote>:last-child{margin-bottom:0}.markdown:not(code) table{width:auto;overflow:auto;word-break:normal;word-break:keep-all}.markdown:not(code) table th{font-weight:700}.markdown:not(code) table td,.markdown:not(code) table th{padding:6px 13px!important;border:1px solid #ddd!important}.markdown:not(code) table tr{background-color:#fff;border-top:1px solid #ccc}.markdown:not(code) table tr:nth-child(2n){background-color:#f8f8f8}.markdown:not(code) img{max-width:100%;box-sizing:border-box}.markdown:not(code) .emoji{max-width:none}.markdown:not(code) span.frame{display:block;overflow:hidden}.markdown:not(code) span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid #ddd}.markdown:not(code) span.frame span img{display:block;float:left}.markdown:not(code) span.frame span span{display:block;padding:5px 0 0;clear:both;color:#333}.markdown:not(code) span.align-center{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown:not(code) span.align-center span img{margin:0 auto;text-align:center}.markdown:not(code) span.align-right{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown:not(code) span.align-right span img{margin:0;text-align:right}.markdown:not(code) span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown:not(code) span.float-left span{margin:13px 0 0}.markdown:not(code) span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown:not(code) span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown:not(code) code,.markdown:not(code) tt{padding:0;padding-top:.2em;padding-bottom:.2em;margin:0;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px}.markdown:not(code) code:after,.markdown:not(code) code:before,.markdown:not(code) tt:after,.markdown:not(code) tt:before{letter-spacing:-.2em;content:"\00a0"}.markdown:not(code) code br,.markdown:not(code) tt br{display:none}.markdown:not(code) del code{text-decoration:inherit}.markdown:not(code) pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:0 0;border:0}.markdown:not(code) .highlight{margin-bottom:16px}.markdown:not(code) .highlight pre,.markdown:not(code) pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f7f7f7;border-radius:3px}.markdown:not(code) .highlight pre{margin-bottom:0;word-break:normal}.markdown:not(code) pre{word-wrap:normal}.markdown:not(code) pre code,.markdown:not(code) pre tt{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown:not(code) pre code:after,.markdown:not(code) pre code:before,.markdown:not(code) pre tt:after,.markdown:not(code) pre tt:before{content:normal}.markdown:not(code) kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#555;vertical-align:middle;background-color:#fcfcfc;border:solid 1px #ccc;border-bottom-color:#bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb}.markdown:not(code) input[type=checkbox]{vertical-align:middle!important}.markdown:not(code) .csv-data td,.markdown:not(code) .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown:not(code) .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:#fff;border:0}.markdown:not(code) .csv-data tr{border-top:0}.markdown:not(code) .csv-data th{font-weight:700;background:#f8f8f8;border-top:0}.markdown:not(code) .ui.list .list,.markdown:not(code) ol.ui.list ol,.markdown:not(code) ul.ui.list ul{padding-left:2em}.home{padding-bottom:80px}.home .logo{max-width:220px}.home .hero h1,.home .hero h2{font-family:'PT Sans Narrow',sans-serif,'Microsoft YaHei'}@media only screen and (max-width:767px){.home .hero h1{font-size:3.5em}.home .hero h2{font-size:2em}}@media only screen and (min-width:768px){.home .hero h1{font-size:5.5em}.home .hero h2{font-size:3em}}.home .hero .octicon{color:#5aa509;font-size:40px;width:50px}.home .hero.header{font-size:20px}.home p.large{font-size:16px}.home .stackable{padding-top:30px}.home a{color:#5aa509}.signup{padding-top:15px;padding-bottom:80px}.install{padding-top:45px;padding-bottom:80px}.install form label{text-align:right;width:320px!important}.install form input{width:35%!important}.install form .field{text-align:left}.install form .field .help{margin-left:335px!important}.install form .field.optional .title{margin-left:38%}.install .ui .checkbox{margin-left:40%!important}.install .ui .checkbox label{width:auto!important}.form .help{color:#999;padding-top:.6em;padding-bottom:.6em;display:inline-block}.ui.attached.header{background:#f0f0f0}.ui.attached.header .right{margin-top:-5px}.ui.attached.header .right .button{padding:8px 10px;font-weight:400}#create-page-form form{margin:auto}#create-page-form form .ui.message{text-align:center}@media only screen and (min-width:768px){#create-page-form form{width:800px!important}#create-page-form form .header{padding-left:280px!important}#create-page-form form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}#create-page-form form .help{margin-left:265px!important}#create-page-form form .optional .title{margin-left:250px!important}#create-page-form form input,#create-page-form form textarea{width:50%!important}}@media only screen and (max-width:767px){#create-page-form form .optional .title{margin-left:15px}#create-page-form form .inline.field>label{display:block}}.signin .oauth2 div{display:inline-block}.signin .oauth2 div p{margin:10px 5px 0 0;float:left}.signin .oauth2 a{margin-right:3px}.signin .oauth2 a:last-child{margin-right:0}.signin .oauth2 img{width:32px;height:32px}.signin .oauth2 img.openidConnect{width:auto}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{margin:auto}.user.activate form .ui.message,.user.forgot.password form .ui.message,.user.reset.password form .ui.message,.user.signin form .ui.message,.user.signup form .ui.message{text-align:center}@media only screen and (min-width:768px){.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:800px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:280px!important}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.user.activate form .help,.user.forgot.password form .help,.user.reset.password form .help,.user.signin form .help,.user.signup form .help{margin-left:265px!important}.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:250px!important}.user.activate form input,.user.activate form textarea,.user.forgot.password form input,.user.forgot.password form textarea,.user.reset.password form input,.user.reset.password form textarea,.user.signin form input,.user.signin form textarea,.user.signup form input,.user.signup form textarea{width:50%!important}}@media only screen and (max-width:767px){.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:15px}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{display:block}}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:700px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:0!important;text-align:center}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{width:200px!important}.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{margin:auto}.repository.new.fork form .ui.message,.repository.new.migrate form .ui.message,.repository.new.repo form .ui.message{text-align:center}@media only screen and (min-width:768px){.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{width:800px!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:280px!important}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.repository.new.fork form .help,.repository.new.migrate form .help,.repository.new.repo form .help{margin-left:265px!important}.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:250px!important}.repository.new.fork form input,.repository.new.fork form textarea,.repository.new.migrate form input,.repository.new.migrate form textarea,.repository.new.repo form input,.repository.new.repo form textarea{width:50%!important}}@media only screen and (max-width:767px){.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:15px}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{display:block}}.repository.new.fork form .dropdown .dropdown.icon,.repository.new.migrate form .dropdown .dropdown.icon,.repository.new.repo form .dropdown .dropdown.icon{margin-top:-7px!important}.repository.new.fork form .dropdown .text,.repository.new.migrate form .dropdown .text,.repository.new.repo form .dropdown .text{margin-right:0!important}.repository.new.fork form .dropdown .text i,.repository.new.migrate form .dropdown .text i,.repository.new.repo form .dropdown .text i{margin-right:0!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:0!important;text-align:center}.repository.new.repo .ui.form .selection.dropdown:not(.owner){width:50%!important}@media only screen and (min-width:768px){.repository.new.repo .ui.form #auto-init{margin-left:265px!important}}.new.webhook form .help{margin-left:25px}.new.webhook .events.fields .column{padding-left:40px}.githook textarea{font-family:monospace}.repository{padding-top:15px;padding-bottom:80px}.repository .header-grid{padding-top:5px;padding-bottom:5px}.repository .header-grid .ui.compact.menu{margin-left:1rem}.repository .header-grid .ui.header{margin-top:0}.repository .header-grid .mega-octicon{width:30px;font-size:30px}.repository .header-grid .ui.huge.breadcrumb{font-weight:400;font-size:1.7rem}.repository .header-grid .fork-flag{margin-left:38px;margin-top:3px;display:block;font-size:12px;white-space:nowrap}.repository .header-grid .octicon.octicon-repo-forked{margin-top:-1px;font-size:15px}.repository .header-grid .button{margin-top:2px;margin-bottom:2px}.repository .tabs .navbar{justify-content:initial}.repository .navbar{display:flex;justify-content:space-between}.repository .navbar .ui.label{margin-top:-2px;margin-left:7px;padding:3px 5px}.repository .owner.dropdown{min-width:40%!important}.repository #file-buttons{margin-left:auto!important;font-weight:400}.repository #file-buttons .ui.button{padding:8px 10px;font-weight:400}.repository .metas .menu{max-height:300px;overflow-x:auto}.repository .metas .ui.list .hide{display:none!important}.repository .metas .ui.list .item{padding:0}.repository .metas .ui.list .label.color{padding:0 8px;margin-right:5px}.repository .metas .ui.list a{margin:2px 0}.repository .metas .ui.list a .text{color:#444}.repository .metas .ui.list a .text:hover{color:#000}.repository .header-wrapper{background-color:#FAFAFA;margin-top:-15px;padding-top:15px}.repository .header-wrapper .ui.tabs.divider{border-bottom:none}.repository .header-wrapper .ui.tabular .octicon{margin-right:5px}.repository .filter.menu .label.color{border-radius:3px;margin-left:15px;padding:0 8px}.repository .filter.menu .octicon{float:left;margin:5px -7px 0 -5px;width:16px}.repository .filter.menu .text{margin-left:.9em}.repository .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}.repository .filter.menu .dropdown.item{margin:1px;padding-right:0}.repository .select-label .item{max-width:250px;overflow:hidden;text-overflow:ellipsis}.repository .select-label .desc{padding-left:16px}.repository .ui.tabs.container{margin-top:14px;margin-bottom:0}.repository .ui.tabs.container .ui.menu{border-bottom:none}.repository .ui.tabs.divider{margin-top:0;margin-bottom:20px}.repository #clone-panel{width:350px}.repository #clone-panel input{border-radius:0;padding:5px 10px}.repository #clone-panel .clone.button{font-size:13px;padding:0 5px}.repository #clone-panel .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository #clone-panel .icon.button{padding:0 10px}.repository #clone-panel .dropdown .menu{right:0!important;left:auto!important}.repository.file.list .repo-description{display:flex;justify-content:space-between;align-items:center}.repository.file.list #repo-desc{font-size:1.2em}.repository.file.list .choose.reference .header .icon{font-size:1.4em}.repository.file.list .repo-path .divider,.repository.file.list .repo-path .section{display:inline}.repository.file.list #file-buttons{font-weight:400}.repository.file.list #file-buttons .ui.button{padding:8px 10px;font-weight:400}.repository.file.list #repo-files-table thead th{padding-top:8px;padding-bottom:5px;font-weight:400}.repository.file.list #repo-files-table thead th:first-child{display:block;position:relative;width:325%}.repository.file.list #repo-files-table thead .ui.avatar{margin-bottom:5px}.repository.file.list #repo-files-table tbody .octicon{margin-left:3px;margin-right:5px;color:#777}.repository.file.list #repo-files-table tbody .octicon.octicon-mail-reply{margin-right:10px}.repository.file.list #repo-files-table tbody .octicon.octicon-file-directory,.repository.file.list #repo-files-table tbody .octicon.octicon-file-submodule,.repository.file.list #repo-files-table tbody .octicon.octicon-file-symlink-directory{color:#1e70bf}.repository.file.list #repo-files-table td{padding-top:8px;padding-bottom:8px}.repository.file.list #repo-files-table td.message .isSigned{cursor:default}.repository.file.list #repo-files-table tr:hover{background-color:#ffE}.repository.file.list #repo-files-table .jumpable-path{color:#888}.repository.file.list .non-diff-file-content .header .icon{font-size:1em}.repository.file.list .non-diff-file-content .header .file-actions{margin-top:0;margin-bottom:-5px;padding-left:20px}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon{display:inline-block;padding:5px;margin-left:5px;line-height:1;color:#767676;vertical-align:middle;background:0 0;border:0;outline:0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon:hover{color:#4078c0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon-danger:hover{color:#bd2c00}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon.disabled{color:#bbb;cursor:default}.repository.file.list .non-diff-file-content .header .file-actions #delete-file-form{display:inline-block}.repository.file.list .non-diff-file-content .view-raw{padding:5px}.repository.file.list .non-diff-file-content .view-raw *{max-width:100%}.repository.file.list .non-diff-file-content .view-raw img{padding:5px 5px 0 5px}.repository.file.list .non-diff-file-content .plain-text{padding:1em 2em 1em 2em}.repository.file.list .non-diff-file-content .code-view *{font-size:12px;font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;line-height:20px}.repository.file.list .non-diff-file-content .code-view table{width:100%}.repository.file.list .non-diff-file-content .code-view .lines-num{vertical-align:top;text-align:right;color:#999;background:#f5f5f5;width:1%;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}.repository.file.list .non-diff-file-content .code-view .lines-num span{line-height:20px;padding:0 10px;cursor:pointer;display:block}.repository.file.list .non-diff-file-content .code-view .lines-code,.repository.file.list .non-diff-file-content .code-view .lines-num{padding:0}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs,.repository.file.list .non-diff-file-content .code-view .lines-code ol,.repository.file.list .non-diff-file-content .code-view .lines-code pre,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs,.repository.file.list .non-diff-file-content .code-view .lines-num ol,.repository.file.list .non-diff-file-content .code-view .lines-num pre{background-color:#fff;margin:0;padding:0!important}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-code ol li,.repository.file.list .non-diff-file-content .code-view .lines-code pre li,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-num ol li,.repository.file.list .non-diff-file-content .code-view .lines-num pre li{display:block;width:100%}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-code ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-code pre li.active,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-num ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-num pre li.active{background:#ffd}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-code ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-code pre li:before,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-num ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-num pre li:before{content:' '}.repository.file.list .non-diff-file-content .code-view .active{background:#ffd}.repository.file.list .sidebar{padding-left:0}.repository.file.list .sidebar .octicon{width:16px}.repository.file.editor .treepath{width:100%}.repository.file.editor .treepath input{vertical-align:middle;box-shadow:rgba(0,0,0,.0745098) 0 1px 2px inset;width:inherit;padding:7px 8px;margin-right:5px}.repository.file.editor .tabular.menu .octicon{margin-right:5px}.repository.file.editor .commit-form-wrapper{padding-left:64px}.repository.file.editor .commit-form-wrapper .commit-avatar{float:left;margin-left:-64px;width:3em;height:auto}.repository.file.editor .commit-form-wrapper .commit-form{position:relative;padding:15px;margin-bottom:10px;border:1px solid #ddd;border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form:after,.repository.file.editor .commit-form-wrapper .commit-form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.file.editor .commit-form-wrapper .commit-form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#fff}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .branch-name{display:inline-block;padding:3px 6px;font:12px Consolas,"Liberation Mono",Menlo,Courier,monospace;color:rgba(0,0,0,.65);background-color:rgba(209,227,237,.45);border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input{position:relative;margin-left:25px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input input{width:240px!important;padding-left:26px!important}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .octicon-git-branch{position:absolute;top:9px;left:10px;color:#b0c4ce}.repository.options #interval{width:100px!important;min-width:100px}.repository.options .danger .item{padding:20px 15px}.repository.options .danger .ui.divider{margin:0}.repository.new.issue .comment.form .comment .avatar{width:3em}.repository.new.issue .comment.form .content{margin-left:4em}.repository.new.issue .comment.form .content:after,.repository.new.issue .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.new.issue .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.new.issue .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.new.issue .comment.form .content:after{border-right-color:#fff}.repository.new.issue .comment.form .content .markdown{font-size:14px}.repository.new.issue .comment.form .metas{min-width:220px}.repository.new.issue .comment.form .metas .filter.menu{max-height:300px;overflow-x:auto}.repository.view.issue .title{padding-bottom:0!important}.repository.view.issue .title h1{font-weight:300;font-size:2.3rem;margin-bottom:5px}.repository.view.issue .title h1 .ui.input{font-size:.5em;vertical-align:top;width:50%;min-width:600px}.repository.view.issue .title h1 .ui.input input{font-size:1.5em;padding:6px 10px}.repository.view.issue .title .index{font-weight:300;color:#aaa;letter-spacing:-1px}.repository.view.issue .title .label{margin-right:10px}.repository.view.issue .title .edit-zone{margin-top:10px}.repository.view.issue .pull-desc code{color:#0166E6}.repository.view.issue .pull.tabular.menu{margin-bottom:10px}.repository.view.issue .pull.tabular.menu .octicon{margin-right:5px}.repository.view.issue .pull.tab.segment{border:none;padding:0;padding-top:10px;box-shadow:none;background-color:inherit}.repository.view.issue .pull .merge.box .avatar{margin-left:10px;margin-top:10px}.repository.view.issue .comment-list:before{display:block;content:"";position:absolute;margin-top:12px;margin-bottom:14px;top:0;bottom:0;left:96px;width:2px;background-color:#f3f3f3;z-index:-1}.repository.view.issue .comment-list .comment .avatar{width:3em}.repository.view.issue .comment-list .comment .tag{color:#767676;margin-top:3px;padding:2px 5px;font-size:12px;border:1px solid rgba(0,0,0,.1);border-radius:3px}.repository.view.issue .comment-list .comment .actions .item{float:left}.repository.view.issue .comment-list .comment .actions .item.tag{margin-right:5px}.repository.view.issue .comment-list .comment .actions .item.action{margin-top:6px;margin-left:10px}.repository.view.issue .comment-list .comment .content{margin-left:4em}.repository.view.issue .comment-list .comment .content>.header{font-weight:400;padding:auto 15px;position:relative;color:#767676;background-color:#f7f7f7;border-bottom:1px solid #eee;border-top-left-radius:3px;border-top-right-radius:3px}.repository.view.issue .comment-list .comment .content>.header:after,.repository.view.issue .comment-list .comment .content>.header:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.view.issue .comment-list .comment .content>.header:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.view.issue .comment-list .comment .content>.header:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.view.issue .comment-list .comment .content>.header .text{max-width:78%;padding-top:10px;padding-bottom:10px}.repository.view.issue .comment-list .comment .content .markdown{font-size:14px}.repository.view.issue .comment-list .comment .content .no-content{color:#767676;font-style:italic}.repository.view.issue .comment-list .comment .content>.bottom.segment{background:#f3f4f5}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.images::after{clear:both;content:' ';display:block}.repository.view.issue .comment-list .comment .content>.bottom.segment a{display:block;float:left;margin:5px;padding:5px;height:150px;border:solid 1px #eee;border-radius:3px;max-width:150px;background-color:#fff}.repository.view.issue .comment-list .comment .content>.bottom.segment a:before{content:' ';display:inline-block;height:100%;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.image{max-height:100%;width:auto;margin:0;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image{font-size:128px;color:#000}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image:hover{color:#000}.repository.view.issue .comment-list .comment .ui.form .field:first-child{clear:none}.repository.view.issue .comment-list .comment .ui.form .tab.segment{border:none;padding:0;padding-top:10px}.repository.view.issue .comment-list .comment .ui.form textarea{height:200px;font-family:Consolas,monospace}.repository.view.issue .comment-list .comment .edit.buttons{margin-top:10px}.repository.view.issue .comment-list .event{position:relative;margin:15px 0 15px 79px;padding-left:25px}.repository.view.issue .comment-list .event .octicon{width:30px;float:left;text-align:center}.repository.view.issue .comment-list .event .octicon.octicon-circle-slash{margin-top:5px;margin-left:-34.5px;font-size:20px;color:#bd2c00}.repository.view.issue .comment-list .event .octicon.octicon-primitive-dot{margin-left:-28.5px;margin-right:-1px;font-size:30px;color:#6cc644}.repository.view.issue .comment-list .event .octicon.octicon-bookmark{margin-top:3px;margin-left:-31px;margin-right:-1px;font-size:25px}.repository.view.issue .comment-list .event .detail{font-size:.9rem;margin-top:5px;margin-left:35px}.repository.view.issue .comment-list .event .detail .octicon.octicon-git-commit{margin-top:2px}.repository.view.issue .ui.segment.metas{margin-top:-3px}.repository.view.issue .ui.participants img{margin-top:5px;margin-right:5px}.repository .comment.form .ui.comments{margin-top:-12px;max-width:100%}.repository .comment.form .content .field:first-child{clear:none}.repository .comment.form .content .form:after,.repository .comment.form .content .form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository .comment.form .content .form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository .comment.form .content .form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository .comment.form .content .form:after{border-right-color:#fff}.repository .comment.form .content .tab.segment{border:none;padding:0;padding-top:10px}.repository .comment.form .content textarea{height:200px;font-family:Consolas,monospace}.repository .label.list{list-style:none;padding-top:15px}.repository .label.list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .label.list .item a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .label.list .item a:hover{color:#000}.repository .label.list .item a.open-issues{margin-right:30px}.repository .label.list .item .ui.label{font-size:1em}.repository .milestone.list{list-style:none;padding-top:15px}.repository .milestone.list>.item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .milestone.list>.item>a{padding-top:5px;padding-right:10px;color:#000}.repository .milestone.list>.item>a:hover{color:#4078c0}.repository .milestone.list>.item .ui.progress{width:40%;padding:0;border:0;margin:0}.repository .milestone.list>.item .ui.progress .bar{height:20px}.repository .milestone.list>.item .meta{color:#999;padding-top:5px}.repository .milestone.list>.item .meta .issue-stats .octicon{padding-left:5px}.repository .milestone.list>.item .meta .overdue{color:red}.repository .milestone.list>.item .operate{margin-top:-15px}.repository .milestone.list>.item .operate>a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .milestone.list>.item .operate>a:hover{color:#000}.repository .milestone.list>.item .content{padding-top:10px}.repository.new.milestone textarea{height:200px}.repository.new.milestone #deadline{width:150px}.repository.compare.pull .choose.branch .octicon{padding-right:10px}.repository.compare.pull .comment.form .content:after,.repository.compare.pull .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.compare.pull .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.compare.pull .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.compare.pull .comment.form .content:after{border-right-color:#fff}.repository .filter.dropdown .menu{margin-top:1px!important}.repository.commits .header .search input{font-weight:400;padding:5px 10px}.repository #commits-table thead th:first-of-type{padding-left:15px}.repository #commits-table thead .sha{width:140px}.repository #commits-table thead .shatd{text-align:center}.repository #commits-table td.sha .sha.label{margin:0}.repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n){background-color:rgba(0,0,0,.02)!important}.repository #commits-table td.sha .sha.label.isSigned,.repository #repo-files-table .sha.label.isSigned{border:1px solid #BBB}.repository #commits-table td.sha .sha.label.isSigned .detail.icon,.repository #repo-files-table .sha.label.isSigned .detail.icon{background:#FAFAFA;margin:-6px -10px -4px 0;padding:5px 3px 5px 6px;border-left:1px solid #BBB;border-top-left-radius:0;border-bottom-left-radius:0}.repository #commits-table td.sha .sha.label.isSigned.isVerified,.repository #repo-files-table .sha.label.isSigned.isVerified{border:1px solid #21BA45;background:#21BA4518}.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon{border-left:1px solid #21BA4580}.repository .diff-detail-box{margin:15px 0;line-height:30px}.repository .diff-detail-box ol{clear:both;padding-left:0;margin-top:5px;margin-bottom:28px}.repository .diff-detail-box ol li{list-style:none;padding-bottom:4px;margin-bottom:4px;border-bottom:1px dashed #DDD;padding-left:6px}.repository .diff-detail-box span.status{display:inline-block;width:12px;height:12px;margin-right:8px;vertical-align:middle}.repository .diff-detail-box span.status.modify{background-color:#f0db88}.repository .diff-detail-box span.status.add{background-color:#b4e2b4}.repository .diff-detail-box span.status.del{background-color:#e9aeae}.repository .diff-detail-box span.status.rename{background-color:#dad8ff}.repository .diff-detail-box .ui.right{margin-bottom:15px}.repository .diff-box .header{display:flex;align-items:center}.repository .diff-box .header .count{margin-right:12px;font-size:13px;flex:0 0 auto}.repository .diff-box .header .count .bar{background-color:#bd2c00;height:12px;width:40px;display:inline-block;margin:2px 4px 0 4px;vertical-align:text-top}.repository .diff-box .header .count .bar .add{background-color:#55a532;height:12px}.repository .diff-box .header .file{flex:1;color:#888;word-break:break-all}.repository .diff-box .header .button{margin:-5px 0 -5px 12px;padding:8px 10px;flex:0 0 auto}.repository .diff-file-box .header{background-color:#f7f7f7}.repository .diff-file-box .file-body.file-code .lines-num{text-align:right;color:#A7A7A7;background:#fafafa;width:1%;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;vertical-align:top}.repository .diff-file-box .file-body.file-code .lines-num span.fold{display:block;text-align:center}.repository .diff-file-box .file-body.file-code .lines-num-old{border-right:1px solid #DDD}.repository .diff-file-box .code-diff{font-size:12px}.repository .diff-file-box .code-diff td{padding:0;padding-left:10px;border-top:none}.repository .diff-file-box .code-diff pre{margin:0}.repository .diff-file-box .code-diff .lines-num{border-color:#d4d4d5;border-right-width:1px;border-right-style:solid;padding:0 5px}.repository .diff-file-box .code-diff tbody tr td.halfwidth{width:49%}.repository .diff-file-box .code-diff tbody tr td.tag-code,.repository .diff-file-box .code-diff tbody tr.tag-code td{background-color:#F0F0F0!important;border-color:#D2CECE!important;padding-top:8px;padding-bottom:8px}.repository .diff-file-box .code-diff tbody tr .removed-code{background-color:#f99}.repository .diff-file-box .code-diff tbody tr .added-code{background-color:#9f9}.repository .diff-file-box .code-diff-unified tbody tr.del-code td{background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-unified tbody tr.add-code td{background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split table,.repository .diff-file-box .code-diff-split tbody{width:100%}.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(2),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(4){background-color:#fafafa}.repository .diff-file-box .code-diff-split tbody tr td.del-code,.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(2){background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-split tbody tr td.add-code,.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(4){background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split tbody tr td:nth-child(3){border-left-width:1px;border-left-style:solid}.repository .diff-file-box.file-content{clear:right}.repository .diff-file-box.file-content img{max-width:100%;padding:5px 5px 0 5px}.repository .code-view{overflow:auto;overflow-x:auto;overflow-y:hidden}.repository .repo-search-result{padding-top:10px;padding-bottom:10px}.repository .repo-search-result .lines-num a{color:inherit}.repository.quickstart .guide .item{padding:1em}.repository.quickstart .guide .item small{font-weight:400}.repository.quickstart .guide .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository.quickstart .guide .ui.action.small.input{width:100%}.repository.quickstart .guide #repo-clone-url{border-radius:0;padding:5px 10px;font-size:1.2em}.repository.release #release-list{border-top:1px solid #DDD;margin-top:20px;padding-top:15px}.repository.release #release-list>li{list-style:none}.repository.release #release-list>li .detail,.repository.release #release-list>li .meta{padding-top:30px;padding-bottom:40px}.repository.release #release-list>li .meta{text-align:right;position:relative}.repository.release #release-list>li .meta .tag:not(.icon){display:block;margin-top:15px}.repository.release #release-list>li .meta .commit{display:block;margin-top:10px}.repository.release #release-list>li .detail{border-left:1px solid #DDD}.repository.release #release-list>li .detail .author img{margin-bottom:-3px}.repository.release #release-list>li .detail .download{margin-top:20px}.repository.release #release-list>li .detail .download>a .octicon{margin-left:5px;margin-right:5px}.repository.release #release-list>li .detail .download .list{padding-left:0;border-top:1px solid #eee}.repository.release #release-list>li .detail .download .list li{list-style:none;display:block;padding-top:8px;padding-bottom:8px;border-bottom:1px solid #eee}.repository.release #release-list>li .detail .dot{width:9px;height:9px;background-color:#ccc;z-index:999;position:absolute;display:block;left:-5px;top:40px;border-radius:6px;border:1px solid #FFF}.repository.new.release .target{min-width:500px}.repository.new.release .target #tag-name{margin-top:-4px}.repository.new.release .target .at{margin-left:-5px;margin-right:5px}.repository.new.release .target .dropdown.icon{margin:0;padding-top:3px}.repository.new.release .target .selection.dropdown{padding-top:10px;padding-bottom:10px}.repository.new.release .prerelease.field{margin-bottom:0}.repository.forks .list{margin-top:0}.repository.forks .list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px solid #DDD}.repository.forks .list .item .ui.avatar{float:left;margin-right:5px}.repository.forks .list .item .link{padding-top:5px}.repository.wiki.start .ui.segment{padding-top:70px;padding-bottom:100px}.repository.wiki.start .ui.segment .mega-octicon{font-size:48px}.repository.wiki.new .CodeMirror .CodeMirror-code{font-family:Consolas,monospace}.repository.wiki.new .CodeMirror .CodeMirror-code .cm-comment{background:inherit}.repository.wiki.new .editor-preview{background-color:#fff}.repository.wiki.view .choose.page{margin-top:-5px}.repository.wiki.view .ui.sub.header{text-transform:none}.repository.wiki.view>.markdown{padding:15px 30px}.repository.wiki.view>.markdown h1:first-of-type,.repository.wiki.view>.markdown h2:first-of-type,.repository.wiki.view>.markdown h3:first-of-type,.repository.wiki.view>.markdown h4:first-of-type,.repository.wiki.view>.markdown h5:first-of-type,.repository.wiki.view>.markdown h6:first-of-type{margin-top:0}@media only screen and (max-width:767px){.repository.wiki .dividing.header .stackable.grid .button{margin-top:2px;margin-bottom:2px}}.repository.settings.collaboration .collaborator.list{padding:0}.repository.settings.collaboration .collaborator.list>.item{margin:0;line-height:2em}.repository.settings.collaboration .collaborator.list>.item:not(:last-child){border-bottom:1px solid #DDD}.repository.settings.collaboration #repo-collab-form #search-user-box .results{left:7px}.repository.settings.collaboration #repo-collab-form .ui.button{margin-left:5px;margin-top:-3px}.repository.settings.branches .protected-branches .selection.dropdown{width:300px}.repository.settings.branches .protected-branches .item{border:1px solid #eaeaea;padding:10px 15px}.repository.settings.branches .protected-branches .item:not(:last-child){border-bottom:0}.repository.settings.branches .branch-protection .help{margin-left:26px;padding-top:0}.repository.settings.branches .branch-protection .fields{margin-left:20px;display:block}.repository.settings.branches .branch-protection .whitelist{margin-left:26px}.repository.settings.branches .branch-protection .whitelist .dropdown img{display:inline-block}.repository.settings.webhook .events .column{padding-bottom:0}.repository.settings.webhook .events .help{font-size:13px;margin-left:26px;padding-top:0}.repository .ui.attached.isSigned.isVerified:not(.positive){border-left:1px solid #A3C293;border-right:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified.top:not(.positive){border-top:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified:not(.positive):last-child{border-bottom:1px solid #A3C293}.repository .ui.segment.sub-menu{padding:7px;line-height:0}.repository .ui.segment.sub-menu .list{width:100%;display:flex}.repository .ui.segment.sub-menu .list .item{width:100%;border-radius:3px}.repository .ui.segment.sub-menu .list .item a{color:#000}.repository .ui.segment.sub-menu .list .item a:hover{color:#666}.repository .ui.segment.sub-menu .list .item.active{background:rgba(0,0,0,.05)}.repository .segment.reactions.dropdown .menu,.repository .select-reaction.dropdown .menu{right:0!important;left:auto!important}.repository .segment.reactions.dropdown .menu>.header,.repository .select-reaction.dropdown .menu>.header{margin:.75rem 0 .5rem}.repository .segment.reactions.dropdown .menu>.item,.repository .select-reaction.dropdown .menu>.item{float:left;padding:.5rem .5rem!important}.repository .segment.reactions.dropdown .menu>.item img.emoji,.repository .select-reaction.dropdown .menu>.item img.emoji{margin-right:0}.repository .segment.reactions{padding:.3em 1em}.repository .segment.reactions .ui.label{padding:.4em}.repository .segment.reactions .ui.label.disabled{cursor:default}.repository .segment.reactions .ui.label>img{height:1.5em!important}.repository .segment.reactions .select-reaction{float:none}.repository .segment.reactions .select-reaction:not(.active) a{display:none}.repository .segment.reactions:hover .select-reaction a{display:block}.user-cards .list{padding:0}.user-cards .list .item{list-style:none;width:32%;margin:10px 10px 10px 0;padding-bottom:14px;float:left}.user-cards .list .item .avatar{width:48px;height:48px;float:left;display:block;margin-right:10px}.user-cards .list .item .name{margin-top:0;margin-bottom:0;font-weight:400}.user-cards .list .item .meta{margin-top:5px}#search-repo-box .results .result .image,#search-user-box .results .result .image{float:left;margin-right:8px;width:2em;height:2em}#search-repo-box .results .result .content,#search-user-box .results .result .content{margin:6px 0}#issue-actions{display:none}.issue.list{list-style:none;padding-top:15px}.issue.list>.item{padding-top:15px;padding-bottom:10px;border-bottom:1px dashed #AAA}.issue.list>.item .title{color:#444;font-size:15px;font-weight:700;margin:0 6px}.issue.list>.item .title:hover{color:#000}.issue.list>.item .comment{padding-right:10px;color:#666}.issue.list>.item .desc{padding-top:5px;color:#999}.issue.list>.item .desc .checklist{padding-left:5px}.issue.list>.item .desc .checklist .progress-bar{margin-left:2px;width:80px;height:6px;display:inline-block;background-color:#eee;overflow:hidden;border-radius:3px;vertical-align:2px!important}.issue.list>.item .desc .checklist .progress-bar .progress{background-color:#ccc;display:block;height:100%}.issue.list>.item .desc a.milestone{padding-left:5px;color:#999!important}.issue.list>.item .desc a.milestone:hover{color:#000!important}.issue.list>.item .desc .assignee{margin-top:-5px;margin-right:5px}.issue.list>.item .desc .overdue{color:red}.page.buttons{padding-top:15px}.ui.form .dropzone{width:100%;margin-bottom:10px;border:2px dashed #0087F7;box-shadow:none!important}.ui.form .dropzone .dz-error-message{top:140px}.settings .content{margin-top:2px}.settings .content .segment,.settings .content>.header{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.settings .list>.item .green{color:#21BA45!important}.settings .list>.item:not(:first-child){border-top:1px solid #eaeaea;padding:1rem;margin:15px -1rem -1rem -1rem}.settings .list>.item>.mega-octicon{display:table-cell}.settings .list>.item>.mega-octicon+.content{display:table-cell;padding:0 0 0 .5em;vertical-align:top}.settings .list>.item .info{margin-top:10px}.settings .list>.item .info .tab.segment{border:none;padding:10px 0 0}.settings .list.key .meta{padding-top:5px;color:#666}.settings .list.email>.item:not(:first-child){min-height:60px}.settings .list.collaborator>.item{padding:0}.ui.vertical.menu .header.item{font-size:1.1em;background:#f0f0f0}.edit-label.modal .form .column,.new-label.segment .form .column{padding-right:0}.edit-label.modal .form .buttons,.new-label.segment .form .buttons{margin-left:auto;padding-top:15px}.edit-label.modal .form .color.picker.column,.new-label.segment .form .color.picker.column{width:auto}.edit-label.modal .form .color.picker.column .color-picker,.new-label.segment .form .color.picker.column .color-picker{height:35px;width:auto;padding-left:30px}.edit-label.modal .form .minicolors-swatch.minicolors-sprite,.new-label.segment .form .minicolors-swatch.minicolors-sprite{top:10px;left:10px;width:15px;height:15px}.edit-label.modal .form .precolors,.new-label.segment .form .precolors{padding-left:0;padding-right:0;margin:3px 10px auto 10px;width:120px}.edit-label.modal .form .precolors .color,.new-label.segment .form .precolors .color{float:left;width:15px;height:15px}#avatar-arrow:after,#avatar-arrow:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}#avatar-arrow:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}#avatar-arrow:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}#delete-repo-modal .ui.message,#transfer-repo-modal .ui.message{width:100%!important}.tab-size-1{tab-size:1!important;-moz-tab-size:1!important}.tab-size-2{tab-size:2!important;-moz-tab-size:2!important}.tab-size-3{tab-size:3!important;-moz-tab-size:3!important}.tab-size-4{tab-size:4!important;-moz-tab-size:4!important}.tab-size-5{tab-size:5!important;-moz-tab-size:5!important}.tab-size-6{tab-size:6!important;-moz-tab-size:6!important}.tab-size-7{tab-size:7!important;-moz-tab-size:7!important}.tab-size-8{tab-size:8!important;-moz-tab-size:8!important}.tab-size-9{tab-size:9!important;-moz-tab-size:9!important}.tab-size-10{tab-size:10!important;-moz-tab-size:10!important}.tab-size-11{tab-size:11!important;-moz-tab-size:11!important}.tab-size-12{tab-size:12!important;-moz-tab-size:12!important}.tab-size-13{tab-size:13!important;-moz-tab-size:13!important}.tab-size-14{tab-size:14!important;-moz-tab-size:14!important}.tab-size-15{tab-size:15!important;-moz-tab-size:15!important}.tab-size-16{tab-size:16!important;-moz-tab-size:16!important}.stats-table{display:table;width:100%}.stats-table .table-cell{display:table-cell}.stats-table .table-cell.tiny{height:.5em}tbody.commit-list{vertical-align:baseline}.commit-body{white-space:pre-wrap}@media only screen and (max-width:767px){.ui.stackable.menu.mobile--margin-between-items>.item{margin-top:5px;margin-bottom:5px}.ui.stackable.menu.mobile--no-negative-margins{margin-left:0;margin-right:0}}#topic_edit{margin-top:5px;display:none}#repo-topic{margin-top:5px}.CodeMirror{font:14px Consolas,"Liberation Mono",Menlo,Courier,monospace}.CodeMirror.cm-s-default{border-radius:3px;padding:0!important}.CodeMirror .cm-comment{background:inherit!important}.repository.file.editor .tab[data-tab=write]{padding:0!important}.repository.file.editor .tab[data-tab=write] .editor-toolbar{border:none!important}.repository.file.editor .tab[data-tab=write] .CodeMirror{border-left:none;border-right:none;border-bottom:none}.organization{padding-top:15px;padding-bottom:80px}.organization .head .ui.header .text{vertical-align:middle;font-size:1.6rem;margin-left:15px}.organization .head .ui.header .ui.right{margin-top:5px}.organization.new.org form{margin:auto}.organization.new.org form .ui.message{text-align:center}@media only screen and (min-width:768px){.organization.new.org form{width:800px!important}.organization.new.org form .header{padding-left:280px!important}.organization.new.org form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.organization.new.org form .help{margin-left:265px!important}.organization.new.org form .optional .title{margin-left:250px!important}.organization.new.org form input,.organization.new.org form textarea{width:50%!important}}@media only screen and (max-width:767px){.organization.new.org form .optional .title{margin-left:15px}.organization.new.org form .inline.field>label{display:block}}.organization.new.org form .header{padding-left:0!important;text-align:center}.organization.options input{min-width:300px}.organization.profile #org-avatar{width:100px;height:100px;margin-right:15px}.organization.profile #org-info .ui.header{font-size:36px;margin-bottom:0}.organization.profile #org-info .desc{font-size:16px;margin-bottom:10px}.organization.profile #org-info .meta .item{display:inline-block;margin-right:10px}.organization.profile #org-info .meta .item .icon{margin-right:5px}.organization.profile .ui.top.header .ui.right{margin-top:0}.organization.profile .teams .item{padding:10px 15px}.organization.profile .members .ui.avatar,.organization.teams .members .ui.avatar{width:48px;height:48px;margin-right:5px}.organization.invite #invite-box{margin:auto;margin-top:50px;width:500px!important}.organization.invite #invite-box #search-user-box input{margin-left:0;width:300px}.organization.invite #invite-box .ui.button{margin-left:5px;margin-top:-3px}.organization.members .list .item{margin-left:0;margin-right:0;border-bottom:1px solid #eee}.organization.members .list .item .ui.avatar{width:48px;height:48px}.organization.members .list .item .meta{line-height:24px}.organization.teams .detail .item{padding:10px 15px}.organization.teams .detail .item:not(:last-child){border-bottom:1px solid #eee}.organization.teams .members .item,.organization.teams .repositories .item{padding:10px 20px;line-height:32px}.organization.teams .members .item:not(:last-child),.organization.teams .repositories .item:not(:last-child){border-bottom:1px solid #DDD}.organization.teams .members .item .button,.organization.teams .repositories .item .button{padding:9px 10px}.organization.teams #add-member-form input,.organization.teams #add-repo-form input{margin-left:0}.organization.teams #add-member-form .ui.button,.organization.teams #add-repo-form .ui.button{margin-left:5px;margin-top:-3px}.user:not(.icon){padding-top:15px;padding-bottom:80px}.user.profile .ui.card .username{display:block}.user.profile .ui.card .extra.content{padding:0}.user.profile .ui.card .extra.content ul{margin:0;padding:0}.user.profile .ui.card .extra.content ul li{padding:10px;list-style:none}.user.profile .ui.card .extra.content ul li:not(:last-child){border-bottom:1px solid #eaeaea}.user.profile .ui.card .extra.content ul li .octicon{margin-left:1px;margin-right:5px}.user.profile .ui.card .extra.content ul li.follow .ui.button{width:100%}.user.profile .ui.repository.list{margin-top:25px}.user.followers .header.name{font-size:20px;line-height:24px;vertical-align:middle}.user.followers .follow .ui.button{padding:8px 15px}.user.notification .octicon{float:left;font-size:2em}.user.notification .content{float:left;margin-left:7px}.user.notification table form{display:inline-block}.user.notification table button{padding:3px 3px 3px 5px}.user.notification table tr{cursor:pointer}.user.notification .octicon.green{color:#21ba45}.user.notification .octicon.red{color:#d01919}.user.notification .octicon.purple{color:#a333c8}.user.notification .octicon.blue{color:#2185d0}.user.link-account:not(.icon){padding-top:15px;padding-bottom:5px}.user.settings .iconFloat{float:left}.dashboard{padding-top:15px;padding-bottom:80px}.dashboard.feeds .context.user.menu,.dashboard.issues .context.user.menu{z-index:101;min-width:200px}.dashboard.feeds .context.user.menu .ui.header,.dashboard.issues .context.user.menu .ui.header{font-size:1rem;text-transform:none}.dashboard.feeds .filter.menu .item,.dashboard.issues .filter.menu .item{text-align:left}.dashboard.feeds .filter.menu .item .text,.dashboard.issues .filter.menu .item .text{height:16px;vertical-align:middle}.dashboard.feeds .filter.menu .item .text.truncate,.dashboard.issues .filter.menu .item .text.truncate{width:85%}.dashboard.feeds .filter.menu .item .floating.label,.dashboard.issues .filter.menu .item .floating.label{top:7px;left:90%;width:15%}.dashboard.feeds .filter.menu .jump.item,.dashboard.issues .filter.menu .jump.item{margin:1px;padding-right:0}.dashboard.feeds .filter.menu .menu,.dashboard.issues .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}.dashboard.feeds .right.stackable.menu>.item.active,.dashboard.issues .right.stackable.menu>.item.active{color:#d9453d}.dashboard .dashboard-repos{margin:0 1px}.feeds .news>.ui.grid{margin-left:auto;margin-right:auto}.feeds .news .ui.avatar{margin-top:13px}.feeds .news p{line-height:1em}.feeds .news .time-since{font-size:13px}.feeds .news .issue.title{width:80%}.feeds .news .push.news .content ul{font-size:13px;list-style:none;padding-left:10px}.feeds .news .push.news .content ul img{margin-bottom:-2px}.feeds .news .push.news .content ul .text.truncate{width:80%;margin-bottom:-5px}.feeds .news .commit-id{font-family:Consolas,monospace}.feeds .news code{padding:1px;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px;word-break:break-all}.feeds .list .header .ui.label{margin-top:-4px;padding:4px 5px;font-weight:400}.feeds .list .header .plus.icon{margin-top:5px}.feeds .list ul{list-style:none;margin:0;padding-left:0}.feeds .list ul li:not(:last-child){border-bottom:1px solid #EAEAEA}.feeds .list ul li.private{background-color:#fcf8e9}.feeds .list ul li a{padding:6px 1.2em;display:block}.feeds .list ul li a .octicon{color:#888}.feeds .list ul li a .octicon.rear{font-size:15px}.feeds .list ul li a .star-num{font-size:12px}.feeds .list .repo-owner-name-list .item-name{max-width:70%;margin-bottom:-4px}.feeds .list #collaborative-repo-list .owner-and-repo{max-width:80%;margin-bottom:-5px}.feeds .list #collaborative-repo-list .owner-name{max-width:120px;margin-bottom:-5px}.admin{padding-top:15px;padding-bottom:80px}.admin .table.segment{padding:0;font-size:13px}.admin .table.segment:not(.striped){padding-top:5px}.admin .table.segment:not(.striped) thead th:last-child{padding-right:5px!important}.admin .table.segment th{padding-top:5px;padding-bottom:5px}.admin .table.segment:not(.select) td:first-of-type,.admin .table.segment:not(.select) th:first-of-type{padding-left:15px!important}.admin .ui.header,.admin .ui.segment{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.admin.user .email{max-width:200px}.admin dl.admin-dl-horizontal{padding:20px;margin:0}.admin dl.admin-dl-horizontal dd{margin-left:275px}.admin dl.admin-dl-horizontal dt{font-weight:bolder;float:left;width:285px;clear:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.admin.config #test-mail-btn{margin-left:5px}.explore{padding-top:15px;padding-bottom:80px}.explore .navbar{justify-content:center;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}.explore .navbar .octicon{width:16px;text-align:center;margin-right:5px}.ui.repository.list .item{padding-bottom:25px}.ui.repository.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.repository.list .item .ui.header{font-size:1.5rem;padding-bottom:10px}.ui.repository.list .item .ui.header .name{word-break:break-all}.ui.repository.list .item .ui.header .metas{color:#888;font-size:14px;font-weight:400}.ui.repository.list .item .ui.header .metas span:not(:last-child){margin-right:5px}.ui.repository.list .item .time{font-size:12px;color:grey}.ui.repository.branches .time{font-size:12px;color:grey}.ui.user.list .item{padding-bottom:25px}.ui.user.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.user.list .item .ui.avatar.image{width:40px;height:40px}.ui.user.list .item .description{margin-top:5px}.ui.user.list .item .description .octicon:not(:first-child){margin-left:5px}.ui.user.list .item .description a{color:#333}.ui.user.list .item .description a:hover{text-decoration:underline} \ No newline at end of file diff --git a/public/js/index.js b/public/js/index.js index e98a3fe6d..823dd8766 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -2336,8 +2336,10 @@ function initTopicbar() { }).done(function() { editDiv.hide(); viewDiv.show(); + }).fail(function(xhr) { + alert(xhr.responseJSON.message) }) - }) + }); $('#topic_edit .dropdown').dropdown({ allowAdditions: true, diff --git a/public/less/_markdown.less b/public/less/_markdown.less index 9e2734e42..458016d71 100644 --- a/public/less/_markdown.less +++ b/public/less/_markdown.less @@ -5,6 +5,10 @@ line-height: 1.6 !important; word-wrap: break-word; + &.ui.segment { + padding: 3em; + } + &.file-view { padding: 2em 2em 2em !important; } diff --git a/public/swagger.v1.json b/public/swagger.v1.json index 2c263ef1f..9fd790281 100644 --- a/public/swagger.v1.json +++ b/public/swagger.v1.json @@ -321,9 +321,13 @@ "operationId": "renderMarkdownRaw", "parameters": [ { - "type": "string", + "description": "Request body to render", "name": "body", - "in": "body" + "in": "body", + "required": true, + "schema": { + "type": "string" + } } ], "responses": { @@ -448,6 +452,15 @@ ], "summary": "List an organization's webhooks", "operationId": "orgListHooks", + "parameters": [ + { + "type": "string", + "description": "name of the organization", + "name": "org", + "in": "path", + "required": true + } + ], "responses": { "200": { "$ref": "#/responses/HookList" @@ -468,6 +481,15 @@ ], "summary": "Create a hook", "operationId": "orgCreateHook", + "parameters": [ + { + "type": "string", + "description": "name of the organization", + "name": "org", + "in": "path", + "required": true + } + ], "responses": { "201": { "$ref": "#/responses/Hook" @@ -485,6 +507,22 @@ ], "summary": "Get a hook", "operationId": "orgGetHook", + "parameters": [ + { + "type": "string", + "description": "name of the organization", + "name": "org", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "id of the hook to get", + "name": "id", + "in": "path", + "required": true + } + ], "responses": { "200": { "$ref": "#/responses/Hook" @@ -500,6 +538,22 @@ ], "summary": "Delete a hook", "operationId": "orgDeleteHook", + "parameters": [ + { + "type": "string", + "description": "name of the organization", + "name": "org", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "id of the hook to delete", + "name": "id", + "in": "path", + "required": true + } + ], "responses": { "204": { "$ref": "#/responses/empty" @@ -518,6 +572,22 @@ ], "summary": "Update a hook", "operationId": "orgEditHook", + "parameters": [ + { + "type": "string", + "description": "name of the organization", + "name": "org", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "id of the hook to update", + "name": "id", + "in": "path", + "required": true + } + ], "responses": { "200": { "$ref": "#/responses/Hook" @@ -994,7 +1064,7 @@ } } }, - "/repos/{owner}/{repo}/archive/{filepath}": { + "/repos/{owner}/{repo}/archive/{archive}": { "get": { "produces": [ "application/json" @@ -1530,6 +1600,47 @@ } } }, + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Delete a hook in a repository", + "operationId": "repoDeleteHook", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "id of the hook to delete", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/empty" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + }, "patch": { "produces": [ "application/json" @@ -1554,6 +1665,13 @@ "in": "path", "required": true }, + { + "type": "integer", + "description": "index of the hook", + "name": "id", + "in": "path", + "required": true + }, { "name": "body", "in": "body", @@ -1825,6 +1943,100 @@ } } }, + "/repos/{owner}/{repo}/issues/{id}/times": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "issue" + ], + "summary": "List an issue's tracked times", + "operationId": "issueTrackedTimes", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "index of the issue", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/TrackedTimeList" + } + } + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "issue" + ], + "summary": "Add a tracked time to a issue", + "operationId": "issueAddTime", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "index of the issue to add tracked time to", + "name": "id", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/AddTimeOption" + } + } + ], + "responses": { + "200": { + "$ref": "#/responses/TrackedTime" + }, + "400": { + "$ref": "#/responses/error" + }, + "403": { + "$ref": "#/responses/error" + } + } + } + }, "/repos/{owner}/{repo}/issues/{index}": { "get": { "produces": [ @@ -1941,7 +2153,7 @@ { "type": "integer", "description": "index of the issue", - "name": "id", + "name": "index", "in": "path", "required": true }, @@ -1988,7 +2200,7 @@ { "type": "integer", "description": "index of the issue", - "name": "id", + "name": "index", "in": "path", "required": true }, @@ -1996,7 +2208,7 @@ "name": "body", "in": "body", "schema": { - "$ref": "#/definitions/CreateIssueOption" + "$ref": "#/definitions/CreateIssueCommentOption" } } ], @@ -2332,100 +2544,6 @@ } } }, - "/repos/{owner}/{repo}/issues/{index}/times": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "issue" - ], - "summary": "List an issue's tracked times", - "operationId": "issueTrackedTimes", - "parameters": [ - { - "type": "string", - "description": "owner of the repo", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the repo", - "name": "repo", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "index of the issue", - "name": "repo", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/TrackedTimeList" - } - } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "issue" - ], - "summary": "Add a tracked time to a issue", - "operationId": "issueAddTime", - "parameters": [ - { - "type": "string", - "description": "owner of the repo", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the repo", - "name": "repo", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "index of the issue to add tracked time to", - "name": "id", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "schema": { - "$ref": "#/definitions/AddTimeOption" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/TrackedTime" - }, - "400": { - "$ref": "#/responses/error" - }, - "403": { - "$ref": "#/responses/error" - } - } - } - }, "/repos/{owner}/{repo}/keys": { "get": { "produces": [ @@ -2781,7 +2899,7 @@ "issue" ], "summary": "Get all of a repository's milestones", - "operationId": "issueGetMilestones", + "operationId": "issueGetMilestonesList", "parameters": [ { "type": "string", @@ -2796,13 +2914,6 @@ "name": "repo", "in": "path", "required": true - }, - { - "type": "integer", - "description": "id of the milestone to get", - "name": "id", - "in": "path", - "required": true } ], "responses": { @@ -2863,6 +2974,29 @@ ], "summary": "Get a milestone", "operationId": "issueGetMilestone", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "id of the milestone", + "name": "id", + "in": "path", + "required": true + } + ], "responses": { "200": { "$ref": "#/responses/Milestone" @@ -2893,7 +3027,7 @@ { "type": "integer", "description": "id of the milestone to delete", - "name": "body", + "name": "id", "in": "path", "required": true } @@ -2931,6 +3065,13 @@ "in": "path", "required": true }, + { + "type": "integer", + "description": "id of the milestone", + "name": "id", + "in": "path", + "required": true + }, { "name": "body", "in": "body", @@ -3979,7 +4120,7 @@ } } }, - "/repos/{owner}/{repo}/times/{tracker}": { + "/repos/{owner}/{repo}/times/{user}": { "get": { "produces": [ "application/json" @@ -4019,49 +4160,6 @@ } } }, - "/repos/{user}/{repo}/hooks/{id}": { - "delete": { - "produces": [ - "application/json" - ], - "tags": [ - "repository" - ], - "summary": "Delete a hook in a repository", - "operationId": "repoDeleteHook", - "parameters": [ - { - "type": "string", - "description": "owner of the repo", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the repo", - "name": "repo", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "id of the hook to delete", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "$ref": "#/responses/empty" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - } - }, "/repositories/{id}": { "get": { "produces": [ @@ -4238,7 +4336,7 @@ "organization" ], "summary": "Remove a team member", - "operationId": "orgAddTeamMember", + "operationId": "orgRemoveTeamMember", "parameters": [ { "type": "integer", @@ -4297,7 +4395,7 @@ "organization" ], "summary": "Add a repository to a team", - "operationId": "orgAddTeamMember", + "operationId": "orgAddTeamRepository", "parameters": [ { "type": "integer", @@ -4336,7 +4434,7 @@ "organization" ], "summary": "Remove a repository from a team", - "operationId": "orgAddTeamMember", + "operationId": "orgRemoveTeamRepository", "parameters": [ { "type": "integer", @@ -4379,10 +4477,10 @@ "operationId": "topicSearch", "parameters": [ { - "type": "integer", - "description": "id of the repo to get", - "name": "keyword", - "in": "path", + "type": "string", + "description": "keywords to search", + "name": "q", + "in": "query", "required": true } ], @@ -4509,7 +4607,7 @@ } } }, - "/user/following/{followee}": { + "/user/following/{username}": { "get": { "tags": [ "user" @@ -4520,7 +4618,7 @@ { "type": "string", "description": "username of followed user", - "name": "followee", + "name": "username", "in": "path", "required": true } @@ -4533,9 +4631,7 @@ "$ref": "#/responses/notFound" } } - } - }, - "/user/following/{username}": { + }, "put": { "tags": [ "user" @@ -5301,6 +5397,15 @@ ], "summary": "List the authenticated user's access tokens", "operationId": "userGetTokens", + "parameters": [ + { + "type": "string", + "description": "username of user", + "name": "username", + "in": "path", + "required": true + } + ], "responses": { "200": { "$ref": "#/responses/AccessTokenList" @@ -5323,8 +5428,10 @@ { "type": "string", "x-go-name": "Name", - "name": "name", - "in": "query" + "description": "username of user", + "name": "username", + "in": "path", + "required": true } ], "responses": { @@ -7612,9 +7719,6 @@ "description": "SearchResults", "schema": { "$ref": "#/definitions/SearchResults" - }, - "headers": { - "body": {} } }, "ServerVersion": { @@ -7721,40 +7825,6 @@ "description": "parameterBodies", "schema": { "$ref": "#/definitions/EditAttachmentOptions" - }, - "headers": { - "AddCollaboratorOption": {}, - "AddTimeOption": {}, - "CreateEmailOption": {}, - "CreateForkOption": {}, - "CreateHookOption": {}, - "CreateIssueCommentOption": {}, - "CreateIssueOption": {}, - "CreateKeyOption": {}, - "CreateLabelOption": {}, - "CreateMilestoneOption": {}, - "CreateOrgOption": {}, - "CreatePullRequestOption": {}, - "CreateReleaseOption": {}, - "CreateRepoOption": {}, - "CreateStatusOption": {}, - "CreateTeamOption": {}, - "CreateUserOption": {}, - "DeleteEmailOption": {}, - "EditAttachmentOptions": {}, - "EditHookOption": {}, - "EditIssueCommentOption": {}, - "EditIssueOption": {}, - "EditLabelOption": {}, - "EditMilestoneOption": {}, - "EditOrgOption": {}, - "EditPullRequestOption": {}, - "EditReleaseOption": {}, - "EditTeamOption": {}, - "EditUserOption": {}, - "IssueLabelsOption": {}, - "MarkdownOption": {}, - "MigrateRepoForm": {} } }, "redirect": { @@ -7794,24 +7864,16 @@ }, "security": [ { - "BasicAuth": [ - "[]" - ] + "BasicAuth": [] }, { - "Token": [ - "[]" - ] + "Token": [] }, { - "AccessToken": [ - "[]" - ] + "AccessToken": [] }, { - "AuthorizationHeaderToken": [ - "[]" - ] + "AuthorizationHeaderToken": [] } ] } \ No newline at end of file diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index eec55cac6..5007a0d56 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -20,10 +20,10 @@ // - text/html // // Security: -// - BasicAuth: [] -// - Token: [] -// - AccessToken: [] -// - AuthorizationHeaderToken: [] +// - BasicAuth : +// - Token : +// - AccessToken : +// - AuthorizationHeaderToken : // // SecurityDefinitions: // BasicAuth: diff --git a/routers/api/v1/misc/markdown.go b/routers/api/v1/misc/markdown.go index fd7d1489b..633dff98e 100644 --- a/routers/api/v1/misc/markdown.go +++ b/routers/api/v1/misc/markdown.go @@ -62,9 +62,12 @@ func MarkdownRaw(ctx *context.APIContext) { // --- // summary: Render raw markdown as HTML // parameters: - // - name: body - // in: body - // type: string + // - name: body + // in: body + // description: Request body to render + // required: true + // schema: + // type: string // consumes: // - text/plain // produces: diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go index 0a77d795e..83d2a5e5b 100644 --- a/routers/api/v1/org/hook.go +++ b/routers/api/v1/org/hook.go @@ -20,6 +20,12 @@ func ListHooks(ctx *context.APIContext) { // summary: List an organization's webhooks // produces: // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true // responses: // "200": // "$ref": "#/responses/HookList" @@ -43,6 +49,17 @@ func GetHook(ctx *context.APIContext) { // summary: Get a hook // produces: // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // - name: id + // in: path + // description: id of the hook to get + // type: integer + // required: true // responses: // "200": // "$ref": "#/responses/Hook" @@ -64,9 +81,17 @@ func CreateHook(ctx *context.APIContext, form api.CreateHookOption) { // - application/json // produces: // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true // responses: // "201": // "$ref": "#/responses/Hook" + + //TODO in body params if !utils.CheckCreateHookOption(ctx, &form) { return } @@ -82,9 +107,22 @@ func EditHook(ctx *context.APIContext, form api.EditHookOption) { // - application/json // produces: // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // - name: id + // in: path + // description: id of the hook to update + // type: integer + // required: true // responses: // "200": // "$ref": "#/responses/Hook" + + //TODO in body params hookID := ctx.ParamsInt64(":id") utils.EditOrgHook(ctx, &form, hookID) } @@ -96,6 +134,17 @@ func DeleteHook(ctx *context.APIContext) { // summary: Delete a hook // produces: // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // - name: id + // in: path + // description: id of the hook to delete + // type: integer + // required: true // responses: // "204": // "$ref": "#/responses/empty" diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 408d8921d..b8dd026bf 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -227,7 +227,7 @@ func AddTeamMember(ctx *context.APIContext) { // RemoveTeamMember api for remove one member from a team func RemoveTeamMember(ctx *context.APIContext) { - // swagger:operation DELETE /teams/{id}/members/{username} organization orgAddTeamMember + // swagger:operation DELETE /teams/{id}/members/{username} organization orgRemoveTeamMember // --- // summary: Remove a team member // produces: @@ -306,7 +306,7 @@ func getRepositoryByParams(ctx *context.APIContext) *models.Repository { // AddTeamRepository api for adding a repository to a team func AddTeamRepository(ctx *context.APIContext) { - // swagger:operation PUT /teams/{id}/repos/{org}/{repo} organization orgAddTeamMember + // swagger:operation PUT /teams/{id}/repos/{org}/{repo} organization orgAddTeamRepository // --- // summary: Add a repository to a team // produces: @@ -350,7 +350,7 @@ func AddTeamRepository(ctx *context.APIContext) { // RemoveTeamRepository api for removing a repository from a team func RemoveTeamRepository(ctx *context.APIContext) { - // swagger:operation DELETE /teams/{id}/repos/{org}/{repo} organization orgAddTeamMember + // swagger:operation DELETE /teams/{id}/repos/{org}/{repo} organization orgRemoveTeamRepository // --- // summary: Remove a repository from a team // description: This does not delete the repository, it only removes the diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index a3c8309ac..610247bc2 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -64,7 +64,7 @@ func GetRawFile(ctx *context.APIContext) { // GetArchive get archive of a repository func GetArchive(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/archive/{filepath} repository repoGetArchive + // swagger:operation GET /repos/{owner}/{repo}/archive/{archive} repository repoGetArchive // --- // summary: Get an archive of a repository // produces: diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index e412a7f1f..1f121dfc3 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -189,6 +189,11 @@ func EditHook(ctx *context.APIContext, form api.EditHookOption) { // description: name of the repo // type: string // required: true + // - name: id + // in: path + // description: index of the hook + // type: integer + // required: true // - name: body // in: body // schema: @@ -202,7 +207,7 @@ func EditHook(ctx *context.APIContext, form api.EditHookOption) { // DeleteHook delete a hook of a repository func DeleteHook(ctx *context.APIContext) { - // swagger:operation DELETE /repos/{user}/{repo}/hooks/{id} repository repoDeleteHook + // swagger:operation DELETE /repos/{owner}/{repo}/hooks/{id} repository repoDeleteHook // --- // summary: Delete a hook in a repository // produces: diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 211d8045a..7be39166d 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -1,4 +1,5 @@ // Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2018 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -165,7 +166,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { // "$ref": "#/responses/Issue" var deadlineUnix util.TimeStamp - if form.Deadline != nil { + if form.Deadline != nil && ctx.Repo.IsWriter() { deadlineUnix = util.TimeStamp(form.Deadline.Unix()) } @@ -178,15 +179,22 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { DeadlineUnix: deadlineUnix, } - // Get all assignee IDs - assigneeIDs, err := models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees) - if err != nil { - if models.IsErrUserNotExist(err) { - ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err)) - } else { - ctx.Error(500, "AddAssigneeByName", err) + var assigneeIDs = make([]int64, 0) + var err error + if ctx.Repo.IsWriter() { + issue.MilestoneID = form.Milestone + assigneeIDs, err = models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err)) + } else { + ctx.Error(500, "AddAssigneeByName", err) + } + return } - return + } else { + // setting labels is not allowed if user is not a writer + form.Labels = make([]int64, 0) } if err := models.NewIssue(ctx.Repo.Repository, issue, form.Labels, assigneeIDs, nil); err != nil { diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 2865ea916..af952a070 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -31,7 +31,7 @@ func ListIssueComments(ctx *context.APIContext) { // description: name of the repo // type: string // required: true - // - name: id + // - name: index // in: path // description: index of the issue // type: integer @@ -139,7 +139,7 @@ func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOpti // description: name of the repo // type: string // required: true - // - name: id + // - name: index // in: path // description: index of the issue // type: integer @@ -147,7 +147,7 @@ func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOpti // - name: body // in: body // schema: - // "$ref": "#/definitions/CreateIssueOption" + // "$ref": "#/definitions/CreateIssueCommentOption" // responses: // "201": // "$ref": "#/responses/Comment" diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index 1111d672a..9be9fee91 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -21,7 +21,7 @@ func trackedTimesToAPIFormat(trackedTimes []*models.TrackedTime) []*api.TrackedT // ListTrackedTimes list all the tracked times of an issue func ListTrackedTimes(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/times issue issueTrackedTimes + // swagger:operation GET /repos/{owner}/{repo}/issues/{id}/times issue issueTrackedTimes // --- // summary: List an issue's tracked times // produces: @@ -37,7 +37,7 @@ func ListTrackedTimes(ctx *context.APIContext) { // description: name of the repo // type: string // required: true - // - name: repo + // - name: id // in: path // description: index of the issue // type: integer @@ -70,7 +70,7 @@ func ListTrackedTimes(ctx *context.APIContext) { // AddTime adds time manual to the given issue func AddTime(ctx *context.APIContext, form api.AddTimeOption) { - // swagger:operation Post /repos/{owner}/{repo}/issues/{index}/times issue issueAddTime + // swagger:operation Post /repos/{owner}/{repo}/issues/{id}/times issue issueAddTime // --- // summary: Add a tracked time to a issue // consumes: @@ -132,7 +132,7 @@ func AddTime(ctx *context.APIContext, form api.AddTimeOption) { // ListTrackedTimesByUser lists all tracked times of the user func ListTrackedTimesByUser(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/times/{tracker} user userTrackedTimes + // swagger:operation GET /repos/{owner}/{repo}/times/{user} user userTrackedTimes // --- // summary: List a user's tracked times in a repo // produces: diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go index 3953b0c3c..a138cb7a6 100644 --- a/routers/api/v1/repo/milestone.go +++ b/routers/api/v1/repo/milestone.go @@ -16,30 +16,7 @@ import ( // ListMilestones list all the milestones for a repository func ListMilestones(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/milestones/{id} issue issueGetMilestone - // --- - // summary: Get a milestone - // produces: - // - application/json - // responses: - // "200": - // "$ref": "#/responses/Milestone" - milestones, err := models.GetMilestonesByRepoID(ctx.Repo.Repository.ID) - if err != nil { - ctx.Error(500, "GetMilestonesByRepoID", err) - return - } - - apiMilestones := make([]*api.Milestone, len(milestones)) - for i := range milestones { - apiMilestones[i] = milestones[i].APIFormat() - } - ctx.JSON(200, &apiMilestones) -} - -// GetMilestone get a milestone for a repository -func GetMilestone(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/milestones issue issueGetMilestones + // swagger:operation GET /repos/{owner}/{repo}/milestones issue issueGetMilestonesList // --- // summary: Get all of a repository's milestones // produces: @@ -55,6 +32,29 @@ func GetMilestone(ctx *context.APIContext) { // description: name of the repo // type: string // required: true + // responses: + // "200": + // "$ref": "#/responses/MilestoneList" + milestones, err := models.GetMilestonesByRepoID(ctx.Repo.Repository.ID) + if err != nil { + ctx.Error(500, "GetMilestonesByRepoID", err) + return + } + + apiMilestones := make([]*api.Milestone, len(milestones)) + for i := range milestones { + apiMilestones[i] = milestones[i].APIFormat() + } + ctx.JSON(200, &apiMilestones) +} + +// GetMilestone get a milestone for a repository +func GetMilestone(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/milestones/{id} issue issueGetMilestone + // --- + // summary: Get a milestone + // produces: + // - application/json // parameters: // - name: owner // in: path @@ -68,12 +68,12 @@ func GetMilestone(ctx *context.APIContext) { // required: true // - name: id // in: path - // description: id of the milestone to get + // description: id of the milestone // type: integer // required: true // responses: // "200": - // "$ref": "#/responses/MilestoneList" + // "$ref": "#/responses/Milestone" milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) if err != nil { if models.IsErrMilestoneNotExist(err) { @@ -152,6 +152,11 @@ func EditMilestone(ctx *context.APIContext, form api.EditMilestoneOption) { // description: name of the repo // type: string // required: true + // - name: id + // in: path + // description: id of the milestone + // type: integer + // required: true // - name: body // in: body // schema: @@ -202,7 +207,7 @@ func DeleteMilestone(ctx *context.APIContext) { // description: name of the repo // type: string // required: true - // - name: body + // - name: id // in: path // description: id of the milestone to delete // type: integer diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 9f64dda61..ccfe0440c 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -508,13 +508,13 @@ func TopicSearch(ctx *context.Context) { // --- // summary: search topics via keyword // produces: - // - application/json + // - application/json // parameters: - // - name: keyword - // in: path - // description: id of the repo to get - // type: integer - // required: true + // - name: q + // in: query + // description: keywords to search + // required: true + // type: string // responses: // "200": // "$ref": "#/responses/Repository" diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 8d9363fe9..4f2461f46 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -15,55 +15,87 @@ import ( // parameterBodies // swagger:response parameterBodies type swaggerParameterBodies struct { + // in:body AddCollaboratorOption api.AddCollaboratorOption + // in:body CreateEmailOption api.CreateEmailOption + // in:body DeleteEmailOption api.DeleteEmailOption + // in:body CreateHookOption api.CreateHookOption - EditHookOption api.EditHookOption + // in:body + EditHookOption api.EditHookOption + // in:body CreateIssueOption api.CreateIssueOption - EditIssueOption api.EditIssueOption + // in:body + EditIssueOption api.EditIssueOption + // in:body CreateIssueCommentOption api.CreateIssueCommentOption - EditIssueCommentOption api.EditIssueCommentOption + // in:body + EditIssueCommentOption api.EditIssueCommentOption + // in:body IssueLabelsOption api.IssueLabelsOption + // in:body CreateKeyOption api.CreateKeyOption + // in:body CreateLabelOption api.CreateLabelOption - EditLabelOption api.EditLabelOption + // in:body + EditLabelOption api.EditLabelOption + // in:body MarkdownOption api.MarkdownOption + // in:body CreateMilestoneOption api.CreateMilestoneOption - EditMilestoneOption api.EditMilestoneOption + // in:body + EditMilestoneOption api.EditMilestoneOption + // in:body CreateOrgOption api.CreateOrgOption - EditOrgOption api.EditOrgOption + // in:body + EditOrgOption api.EditOrgOption + // in:body CreatePullRequestOption api.CreatePullRequestOption - EditPullRequestOption api.EditPullRequestOption + // in:body + EditPullRequestOption api.EditPullRequestOption + // in:body CreateReleaseOption api.CreateReleaseOption - EditReleaseOption api.EditReleaseOption + // in:body + EditReleaseOption api.EditReleaseOption + // in:body CreateRepoOption api.CreateRepoOption + // in:body CreateForkOption api.CreateForkOption + // in:body CreateStatusOption api.CreateStatusOption + // in:body CreateTeamOption api.CreateTeamOption - EditTeamOption api.EditTeamOption + // in:body + EditTeamOption api.EditTeamOption + // in:body AddTimeOption api.AddTimeOption + // in:body CreateUserOption api.CreateUserOption - EditUserOption api.EditUserOption + // in:body + EditUserOption api.EditUserOption + // in:body MigrateRepoForm auth.MigrateRepoForm + // in:body EditAttachmentOptions api.EditAttachmentOptions } diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index 97837dfc2..9decc8f58 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -102,6 +102,7 @@ type swaggerResponseWatchInfo struct { // SearchResults // swagger:response SearchResults type swaggerResponseSearchResults struct { + // in:body Body api.SearchResults `json:"body"` } diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index e1f75de68..fc4118649 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -18,6 +18,12 @@ func ListAccessTokens(ctx *context.APIContext) { // summary: List the authenticated user's access tokens // produces: // - application/json + // parameters: + // - name: username + // in: path + // description: username of user + // type: string + // required: true // responses: // "200": // "$ref": "#/responses/AccessTokenList" @@ -46,6 +52,12 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption // - application/json // produces: // - application/json + // parameters: + // - name: username + // in: path + // description: username of user + // type: string + // required: true // responses: // "200": // "$ref": "#/responses/AccessToken" diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index 49d13cc38..284b7323c 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -119,11 +119,11 @@ func checkUserFollowing(ctx *context.APIContext, u *models.User, followID int64) // CheckMyFollowing whether the given user is followed by the authenticated user func CheckMyFollowing(ctx *context.APIContext) { - // swagger:operation GET /user/following/{followee} user userCurrentCheckFollowing + // swagger:operation GET /user/following/{username} user userCurrentCheckFollowing // --- // summary: Check whether a user is followed by the authenticated user // parameters: - // - name: followee + // - name: username // in: path // description: username of followed user // type: string diff --git a/routers/home.go b/routers/home.go index 5bb353c7e..0aa907658 100644 --- a/routers/home.go +++ b/routers/home.go @@ -42,6 +42,10 @@ func Home(ctx *context.Context) { user.Dashboard(ctx) } return + // Check non-logged users landing page. + } else if setting.LandingPageURL != setting.LandingPageHome { + ctx.Redirect(setting.AppSubURL + string(setting.LandingPageURL)) + return } // Check auto-login. diff --git a/routers/org/teams.go b/routers/org/teams.go index d894c8661..87bfb8596 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -182,7 +182,14 @@ func NewTeamPost(ctx *context.Context, form auth.CreateTeamForm) { Authorize: models.ParseAccessMode(form.Permission), } if t.Authorize < models.AccessModeAdmin { - t.UnitTypes = form.Units + var units = make([]*models.TeamUnit, 0, len(form.Units)) + for _, tp := range form.Units { + units = append(units, &models.TeamUnit{ + OrgID: ctx.Org.Organization.ID, + Type: tp, + }) + } + t.Units = units } ctx.Data["Team"] = t @@ -264,9 +271,17 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) { } t.Description = form.Description if t.Authorize < models.AccessModeAdmin { - t.UnitTypes = form.Units + var units = make([]models.TeamUnit, 0, len(form.Units)) + for _, tp := range form.Units { + units = append(units, models.TeamUnit{ + OrgID: t.OrgID, + TeamID: t.ID, + Type: tp, + }) + } + models.UpdateTeamUnits(t, units) } else { - t.UnitTypes = nil + models.UpdateTeamUnits(t, nil) } if ctx.HasError() { diff --git a/routers/repo/topic.go b/routers/repo/topic.go index 2a43d53ff..63fcf793f 100644 --- a/routers/repo/topic.go +++ b/routers/repo/topic.go @@ -12,8 +12,8 @@ import ( "code.gitea.io/gitea/modules/log" ) -// TopicPost response for creating repository -func TopicPost(ctx *context.Context) { +// TopicsPost response for creating repository +func TopicsPost(ctx *context.Context) { if ctx.User == nil { ctx.JSON(403, map[string]interface{}{ "message": "Only owners could change the topics.", @@ -27,6 +27,37 @@ func TopicPost(ctx *context.Context) { topics = strings.Split(topicsStr, ",") } + invalidTopics := make([]string, 0) + i := 0 + for _, topic := range topics { + topic = strings.TrimSpace(strings.ToLower(topic)) + // ignore empty string + if len(topic) > 0 { + topics[i] = topic + i++ + } + if !models.ValidateTopic(topic) { + invalidTopics = append(invalidTopics, topic) + } + } + topics = topics[:i] + + if len(topics) > 25 { + ctx.JSON(422, map[string]interface{}{ + "invalidTopics": topics[:0], + "message": ctx.Tr("repo.topic.count_prompt"), + }) + return + } + + if len(invalidTopics) > 0 { + ctx.JSON(422, map[string]interface{}{ + "invalidTopics": invalidTopics, + "message": ctx.Tr("repo.topic.format_prompt"), + }) + return + } + err := models.SaveTopics(ctx.Repo.Repository.ID, topics...) if err != nil { log.Error(2, "SaveTopics failed: %v", err) diff --git a/routers/repo/view.go b/routers/repo/view.go index d2efe3009..4f1deeae4 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -105,7 +105,9 @@ func renderDirectory(ctx *context.Context, treeLink string) { ctx.Data["FileContent"] = string(markup.Render(readmeFile.Name(), buf, treeLink, ctx.Repo.Repository.ComposeMetas())) } else { ctx.Data["IsRenderedHTML"] = true - ctx.Data["FileContent"] = string(bytes.Replace(buf, []byte("\n"), []byte(`
`), -1)) + ctx.Data["FileContent"] = strings.Replace( + gotemplate.HTMLEscapeString(string(buf)), "\n", `
`, -1, + ) } } } @@ -208,7 +210,9 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.Data["FileContent"] = string(markup.Render(blob.Name(), buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas())) } else if readmeExist { ctx.Data["IsRenderedHTML"] = true - ctx.Data["FileContent"] = string(bytes.Replace(buf, []byte("\n"), []byte(`
`), -1)) + ctx.Data["FileContent"] = strings.Replace( + gotemplate.HTMLEscapeString(string(buf)), "\n", `
`, -1, + ) } else { // Building code view blocks with line number on server side. var fileContent string diff --git a/routers/repo/webhook.go b/routers/repo/webhook.go index 63450ed88..53c1afe66 100644 --- a/routers/repo/webhook.go +++ b/routers/repo/webhook.go @@ -210,7 +210,7 @@ func GogsHooksNewPost(ctx *context.Context, form auth.NewGogshookForm) { Secret: form.Secret, HookEvent: ParseHookEvent(form.WebhookForm), IsActive: form.Active, - HookTaskType: models.GITEA, + HookTaskType: models.GOGS, OrgID: orCtx.OrgID, } if err := w.UpdateEvent(); err != nil { diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 15b91f159..1eefbf1b6 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -486,7 +486,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/:id", repo.WebHooksEdit) m.Post("/:id/test", repo.TestWebhook) m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) - m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) + m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) @@ -626,7 +626,7 @@ func RegisterRoutes(m *macaron.Macaron) { }, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeReleases)) m.Group("/:username/:reponame", func() { - m.Post("/topics", repo.TopicPost) + m.Post("/topics", repo.TopicsPost) }, context.RepoAssignment(), reqRepoAdmin) m.Group("/:username/:reponame", func() { diff --git a/routers/user/auth.go b/routers/user/auth.go index 9a59f52db..317b4af3b 100644 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "github.com/go-macaron/captcha" "github.com/markbates/goth" @@ -474,7 +475,7 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR return setting.AppSubURL + "/" } - if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 { + if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 && !util.IsExternalURL(redirectTo) { ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL) if obeyRedirect { ctx.RedirectToFirst(redirectTo) diff --git a/routers/user/home.go b/routers/user/home.go index 693b04b02..1a79da191 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -203,7 +203,11 @@ func Issues(ctx *context.Context) { return } } else { - userRepoIDs, err = ctxUser.GetAccessRepoIDs() + unitType := models.UnitTypeIssues + if isPullList { + unitType = models.UnitTypePullRequests + } + userRepoIDs, err = ctxUser.GetAccessRepoIDs(unitType) if err != nil { ctx.ServerError("ctxUser.GetAccessRepoIDs", err) return diff --git a/routers/user/home_test.go b/routers/user/home_test.go index a9b146b76..8a3d9b9f5 100644 --- a/routers/user/home_test.go +++ b/routers/user/home_test.go @@ -26,8 +26,8 @@ func TestIssues(t *testing.T) { Issues(ctx) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) - assert.EqualValues(t, map[int64]int64{1: 1, 2: 1}, ctx.Data["Counts"]) + assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"]) assert.EqualValues(t, true, ctx.Data["IsShowClosed"]) assert.Len(t, ctx.Data["Issues"], 1) - assert.Len(t, ctx.Data["Repos"], 2) + assert.Len(t, ctx.Data["Repos"], 1) } diff --git a/routers/user/setting/account.go b/routers/user/setting/account.go index 966d96aed..bcf602c5e 100644 --- a/routers/user/setting/account.go +++ b/routers/user/setting/account.go @@ -24,12 +24,7 @@ func Account(ctx *context.Context) { ctx.Data["PageIsSettingsAccount"] = true ctx.Data["Email"] = ctx.User.Email - emails, err := models.GetEmailAddresses(ctx.User.ID) - if err != nil { - ctx.ServerError("GetEmailAddresses", err) - return - } - ctx.Data["Emails"] = emails + loadAccountData(ctx) ctx.HTML(200, tplSettingsAccount) } @@ -40,6 +35,8 @@ func AccountPost(ctx *context.Context, form auth.ChangePasswordForm) { ctx.Data["PageIsSettingsAccount"] = true if ctx.HasError() { + loadAccountData(ctx) + ctx.HTML(200, tplSettingsAccount) return } @@ -85,15 +82,9 @@ func EmailPost(ctx *context.Context, form auth.AddEmailForm) { return } - // Add Email address. - emails, err := models.GetEmailAddresses(ctx.User.ID) - if err != nil { - ctx.ServerError("GetEmailAddresses", err) - return - } - ctx.Data["Emails"] = emails - if ctx.HasError() { + loadAccountData(ctx) + ctx.HTML(200, tplSettingsAccount) return } @@ -105,6 +96,8 @@ func EmailPost(ctx *context.Context, form auth.AddEmailForm) { } if err := models.AddEmailAddress(email); err != nil { if models.IsErrEmailAlreadyUsed(err) { + loadAccountData(ctx) + ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsAccount, &form) return } @@ -149,6 +142,8 @@ func DeleteAccount(ctx *context.Context) { if _, err := models.UserSignIn(ctx.User.Name, ctx.Query("password")); err != nil { if models.IsErrUserNotExist(err) { + loadAccountData(ctx) + ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_password"), tplSettingsAccount, nil) } else { ctx.ServerError("UserSignIn", err) @@ -172,3 +167,12 @@ func DeleteAccount(ctx *context.Context) { ctx.Redirect(setting.AppSubURL + "/") } } + +func loadAccountData(ctx *context.Context) { + emails, err := models.GetEmailAddresses(ctx.User.ID) + if err != nil { + ctx.ServerError("GetEmailAddresses", err) + return + } + ctx.Data["Emails"] = emails +} diff --git a/routers/user/setting/applications.go b/routers/user/setting/applications.go index f292b65d7..ac7252469 100644 --- a/routers/user/setting/applications.go +++ b/routers/user/setting/applications.go @@ -22,12 +22,7 @@ func Applications(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsApplications"] = true - tokens, err := models.ListAccessTokens(ctx.User.ID) - if err != nil { - ctx.ServerError("ListAccessTokens", err) - return - } - ctx.Data["Tokens"] = tokens + loadApplicationsData(ctx) ctx.HTML(200, tplSettingsApplications) } @@ -38,12 +33,8 @@ func ApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) { ctx.Data["PageIsSettingsApplications"] = true if ctx.HasError() { - tokens, err := models.ListAccessTokens(ctx.User.ID) - if err != nil { - ctx.ServerError("ListAccessTokens", err) - return - } - ctx.Data["Tokens"] = tokens + loadApplicationsData(ctx) + ctx.HTML(200, tplSettingsApplications) return } @@ -75,3 +66,12 @@ func DeleteApplication(ctx *context.Context) { "redirect": setting.AppSubURL + "/user/settings/applications", }) } + +func loadApplicationsData(ctx *context.Context) { + tokens, err := models.ListAccessTokens(ctx.User.ID) + if err != nil { + ctx.ServerError("ListAccessTokens", err) + return + } + ctx.Data["Tokens"] = tokens +} diff --git a/routers/user/setting/keys.go b/routers/user/setting/keys.go index ef986ef8c..c62b117a7 100644 --- a/routers/user/setting/keys.go +++ b/routers/user/setting/keys.go @@ -23,19 +23,7 @@ func Keys(ctx *context.Context) { ctx.Data["PageIsSettingsKeys"] = true ctx.Data["DisableSSH"] = setting.SSH.Disabled - keys, err := models.ListPublicKeys(ctx.User.ID) - if err != nil { - ctx.ServerError("ListPublicKeys", err) - return - } - ctx.Data["Keys"] = keys - - gpgkeys, err := models.ListGPGKeys(ctx.User.ID) - if err != nil { - ctx.ServerError("ListGPGKeys", err) - return - } - ctx.Data["GPGKeys"] = gpgkeys + loadKeysData(ctx) ctx.HTML(200, tplSettingsKeys) } @@ -45,21 +33,9 @@ func KeysPost(ctx *context.Context, form auth.AddKeyForm) { ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsKeys"] = true - keys, err := models.ListPublicKeys(ctx.User.ID) - if err != nil { - ctx.ServerError("ListPublicKeys", err) - return - } - ctx.Data["Keys"] = keys - - gpgkeys, err := models.ListGPGKeys(ctx.User.ID) - if err != nil { - ctx.ServerError("ListGPGKeys", err) - return - } - ctx.Data["GPGKeys"] = gpgkeys - if ctx.HasError() { + loadKeysData(ctx) + ctx.HTML(200, tplSettingsKeys) return } @@ -73,9 +49,13 @@ func KeysPost(ctx *context.Context, form auth.AddKeyForm) { ctx.Flash.Error(ctx.Tr("form.invalid_gpg_key", err.Error())) ctx.Redirect(setting.AppSubURL + "/user/settings/keys") case models.IsErrGPGKeyIDAlreadyUsed(err): + loadKeysData(ctx) + ctx.Data["Err_Content"] = true ctx.RenderWithErr(ctx.Tr("settings.gpg_key_id_used"), tplSettingsKeys, &form) case models.IsErrGPGNoEmailFound(err): + loadKeysData(ctx) + ctx.Data["Err_Content"] = true ctx.RenderWithErr(ctx.Tr("settings.gpg_no_key_email_found"), tplSettingsKeys, &form) default: @@ -103,9 +83,13 @@ func KeysPost(ctx *context.Context, form auth.AddKeyForm) { ctx.Data["HasSSHError"] = true switch { case models.IsErrKeyAlreadyExist(err): + loadKeysData(ctx) + ctx.Data["Err_Content"] = true ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplSettingsKeys, &form) case models.IsErrKeyNameAlreadyUsed(err): + loadKeysData(ctx) + ctx.Data["Err_Title"] = true ctx.RenderWithErr(ctx.Tr("settings.ssh_key_name_used"), tplSettingsKeys, &form) default: @@ -147,3 +131,19 @@ func DeleteKey(ctx *context.Context) { "redirect": setting.AppSubURL + "/user/settings/keys", }) } + +func loadKeysData(ctx *context.Context) { + keys, err := models.ListPublicKeys(ctx.User.ID) + if err != nil { + ctx.ServerError("ListPublicKeys", err) + return + } + ctx.Data["Keys"] = keys + + gpgkeys, err := models.ListGPGKeys(ctx.User.ID) + if err != nil { + ctx.ServerError("ListGPGKeys", err) + return + } + ctx.Data["GPGKeys"] = gpgkeys +} diff --git a/routers/user/setting/profile.go b/routers/user/setting/profile.go index cf222d002..22511ab89 100644 --- a/routers/user/setting/profile.go +++ b/routers/user/setting/profile.go @@ -32,6 +32,7 @@ const ( func Profile(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsProfile"] = true + ctx.HTML(200, tplSettingsProfile) } diff --git a/routers/user/setting/security.go b/routers/user/setting/security.go index 860730303..862e4413c 100644 --- a/routers/user/setting/security.go +++ b/routers/user/setting/security.go @@ -22,6 +22,30 @@ func Security(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsSecurity"] = true + if ctx.Query("openid.return_to") != "" { + settingsOpenIDVerify(ctx) + return + } + + loadSecurityData(ctx) + + ctx.HTML(200, tplSettingsSecurity) +} + +// DeleteAccountLink delete a single account link +func DeleteAccountLink(ctx *context.Context) { + if _, err := models.RemoveAccountLink(ctx.User, ctx.QueryInt64("loginSourceID")); err != nil { + ctx.Flash.Error("RemoveAccountLink: " + err.Error()) + } else { + ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success")) + } + + ctx.JSON(200, map[string]interface{}{ + "redirect": setting.AppSubURL + "/user/settings/security", + }) +} + +func loadSecurityData(ctx *context.Context) { enrolled := true _, err := models.GetTwoFactorByUID(ctx.User.ID) if err != nil { @@ -71,30 +95,10 @@ func Security(ctx *context.Context) { } ctx.Data["AccountLinks"] = sources - if ctx.Query("openid.return_to") != "" { - settingsOpenIDVerify(ctx) - return - } - openid, err := models.GetUserOpenIDs(ctx.User.ID) if err != nil { ctx.ServerError("GetUserOpenIDs", err) return } ctx.Data["OpenIDs"] = openid - - ctx.HTML(200, tplSettingsSecurity) -} - -// DeleteAccountLink delete a single account link -func DeleteAccountLink(ctx *context.Context) { - if _, err := models.RemoveAccountLink(ctx.User, ctx.QueryInt64("loginSourceID")); err != nil { - ctx.Flash.Error("RemoveAccountLink: " + err.Error()) - } else { - ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success")) - } - - ctx.JSON(200, map[string]interface{}{ - "redirect": setting.AppSubURL + "/user/settings/security", - }) } diff --git a/routers/user/setting/security_openid.go b/routers/user/setting/security_openid.go index c98dc2cda..6813765f6 100644 --- a/routers/user/setting/security_openid.go +++ b/routers/user/setting/security_openid.go @@ -19,12 +19,8 @@ func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { ctx.Data["PageIsSettingsSecurity"] = true if ctx.HasError() { - openid, err := models.GetUserOpenIDs(ctx.User.ID) - if err != nil { - ctx.ServerError("GetUserOpenIDs", err) - return - } - ctx.Data["OpenIDs"] = openid + loadSecurityData(ctx) + ctx.HTML(200, tplSettingsSecurity) return } @@ -37,6 +33,8 @@ func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { id, err := openid.Normalize(form.Openid) if err != nil { + loadSecurityData(ctx) + ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &form) return } @@ -53,6 +51,8 @@ func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { // Check that the OpenID is not already used for _, obj := range oids { if obj.URI == id { + loadSecurityData(ctx) + ctx.RenderWithErr(ctx.Tr("form.openid_been_used", id), tplSettingsSecurity, &form) return } @@ -61,6 +61,8 @@ func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { redirectTo := setting.AppURL + "user/settings/security" url, err := openid.RedirectURL(id, redirectTo, setting.AppURL) if err != nil { + loadSecurityData(ctx) + ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &form) return } @@ -73,13 +75,6 @@ func settingsOpenIDVerify(ctx *context.Context) { fullURL := setting.AppURL + ctx.Req.Request.URL.String()[1:] log.Trace("Full URL: " + fullURL) - oids, err := models.GetUserOpenIDs(ctx.User.ID) - if err != nil { - ctx.ServerError("GetUserOpenIDs", err) - return - } - ctx.Data["OpenIDs"] = oids - id, err := openid.Verify(fullURL) if err != nil { ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &auth.AddOpenIDForm{ diff --git a/templates/admin/monitor.tmpl b/templates/admin/monitor.tmpl index ceca29146..6cc927d68 100644 --- a/templates/admin/monitor.tmpl +++ b/templates/admin/monitor.tmpl @@ -49,8 +49,8 @@ {{.PID}} {{.Description}} - {{.Start.FormatLong}} - {{TimeSinceUnix .Start $.Lang}} + {{DateFmtLong .Start}} + {{TimeSince .Start $.Lang}} {{end}} diff --git a/templates/org/team/new.tmpl b/templates/org/team/new.tmpl index ec1a3dd72..12cdd697c 100644 --- a/templates/org/team/new.tmpl +++ b/templates/org/team/new.tmpl @@ -57,7 +57,7 @@ {{range $t, $unit := $.Units}}
- + {{$.i18n.Tr $unit.DescKey}}
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 6d1d9edf8..6f54cb9f6 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -32,7 +32,7 @@
{{if .CanBeForked}}