Merge branch 'master' into api-usage

This commit is contained in:
Lunny Xiao 2018-06-25 13:32:25 +08:00 committed by GitHub
commit 66757ff2e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 1544 additions and 1014 deletions

View File

@ -4,7 +4,7 @@ workspace:
clone: clone:
git: git:
image: plugins/git:1 image: plugins/git:next
depth: 50 depth: 50
tags: true tags: true
@ -255,6 +255,18 @@ pipeline:
when: when:
event: [ push, tag ] 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: release:
image: plugins/s3:1 image: plugins/s3:1
pull: true pull: true

View File

@ -0,0 +1,46 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type="manifest" name="export">
<service name="gitea" type="service" version="1">
<create_default_instance enabled="false"/>
<dependency name="network" grouping="require_all" restart_on="refresh" type="service">
<service_fmri value="svc:/milestone/network:default"/>
</dependency>
<dependency name="filesystem" grouping="require_all" restart_on="refresh" type="service">
<service_fmri value="svc:/system/filesystem/local"/>
</dependency>
<exec_method
type="method"
name="start"
exec="/opt/local/bin/gitea web"
timeout_seconds="60">
<method_context>
<method_credential user="git" group="git" />
<method_environment>
<envvar name='GITEA_WORK_DIR' value='/opt/local/share/gitea'/>
<envvar name='GITEA_CUSTOM' value='/opt/local/etc/gitea'/>
<envvar name='HOME' value='/var/db/gitea'/>
<envvar name='PATH' value='/opt/local/bin:${PATH}'/>
<envvar name='USER' value='git'/>
</method_environment>
</method_context>
</exec_method>
<exec_method type="method" name="stop" exec=":kill" timeout_seconds="60"/>
<property_group name="application" type="application"></property_group>
<property_group name="startd" type="framework">
<propval name="duration" type="astring" value="child"/>
<propval name="ignore_error" type="astring" value="core,signal"/>
</property_group>
<template>
<common_name>
<loctext xml:lang="C">A painless, self-hosted Git service</loctext>
</common_name>
</template>
</service>
</service_bundle>

View File

@ -601,9 +601,9 @@ ko-KR = ko
[U2F] [U2F]
; Two Factor authentication with security keys ; Two Factor authentication with security keys
; https://developers.yubico.com/U2F/App_ID.html ; 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 ; 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 ; Extension mapping to highlight class
; e.g. .toml=ini ; e.g. .toml=ini

View File

@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
/usr/sbin/update-ca-certificates
if [ ! -d /data/git/.ssh ]; then if [ ! -d /data/git/.ssh ]; then
mkdir -p /data/git/.ssh mkdir -p /data/git/.ssh
chmod 700 /data/git/.ssh chmod 700 /data/git/.ssh

View File

@ -27,659 +27,93 @@ _Symbols used in table:_
* _✘ - unsupported_ * _✘ - unsupported_
<table border="1" cellpadding="4"> #### General Features
<thead>
<tr> | Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
<td>Feature</td> |---------|-------|------|-----------|-----------|-----------|-----------|--------------|
<td>Gitea</td> | Open source and free | ✓ | ✓ | ✘| ✓ | ✘ | ✘ | ✓ |
<td>Gogs</td> | Low resource usage (RAM/CPU) | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ |
<td>GitHub EE</td> | Multiple database support | ✓ | ✓ | ✘ | | | ✓ | ✓ |
<td>GitLab CE</td> | Multiple OS support | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ |
<td>GitLab EE</td> | Easy upgrade process | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ |
<td>BitBucket</td> | Markdown support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td>RhodeCode CE</td> | Static Git-powered pages | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
</tr> | Integrated Git-powered wiki | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ |
</thead> | Deploy Tokens | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
<tbody> | Repository Tokens with write rights | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✓ |
<tr> | Built-in Container Registry | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
<td>Open source and free</td> | External git mirroring | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
<td></td> | FIDO U2F (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
<td></td> | Built-in CI/CD | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
<td></td> | Subgroups: groups within groups | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✓ |
<td></td>
<td></td> #### Code management
<td></td>
<td></td> | Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
</tr> |---------|-------|------|-----------|-----------|-----------|-----------|--------------|
<tr> | Repository topics | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
<td>Issue tracker</td> | Repository code search | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td></td> | Global code search | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td></td> | Git LFS 2.0 | ✓ | ✘ | ✓ | ✓ | ✓ | | ✓ |
<td></td> | Group Milestones | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
<td></td> | Granular user roles (Code, Issues, Wiki etc) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
<td></td> | Verified Committer | ✘ | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
<td></td> | GPG Signed Commits | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td></td> | Reject unsigned commits | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✓ |
</tr> | Repository Activity page | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
<tr> | Branch manager | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td>Pull/Merge requests</td> | Create new branches | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
<td></td> | Web code editor | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td></td> | Commit graph | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td></td>
<td></td> #### Issue Tracker
<td></td>
<td></td> | Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
<td></td> |---------|-------|------|-----------|-----------|-----------|-----------|--------------|
</tr> | Issue tracker | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ |
<tr> | Issue templates | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
<td>Squash merging</td> | Labels | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
<td></td> | Time tracking | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
<td></td> | Multiple assignees for issues | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
<td></td> | Related issues | ✘ | ✘ | | ✘ | ✓ | ✘ | ✘ |
<td></td> | Confidential issues | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
<td></td> | Comment reactions | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
<td></td> | Lock Discussion | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
<td></td> | Batch issue handling | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
</tr> | Issue Boards | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
<tr> | Create new branches from issues | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
<td>Rebase merging</td> | Issue search | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
<td></td> | Global issue search | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
<td></td>
<td></td> #### Pull/Merge requests
<td></td>
<td></td> | Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
<td></td> |---------|-------|------|-----------|-----------|-----------|-----------|--------------|
<td></td> | Pull/Merge requests | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
</tr> | Squash merging | ✓ | ✘ | ✓ | ✘ | ✓ | ✓ | ✓ |
<tr> | Rebase merging | ✓ | ✓ | ✓ | ✘ | | ✘ | ✓ |
<td>Pull/Merge request inline comments</td> | Pull/Merge request inline comments | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td></td> | Pull/Merge request approval | ✘ | ✘ | | ✓ | ✓ | ✓ | ✓ |
<td></td> | Merge conflict resolution | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
<td></td> | Restrict push and merge access to certain users | ✓ | ✘ | ✓ | | ✓ | ✓ | ✓ |
<td></td> | Revert specific commits or a merge request | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
<td></td> | Pull/Merge requests templates | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
<td></td> | Cherry-picking changes | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
<td></td>
</tr>
<tr> #### 3rd-party integrations
<td>Pull/Merge request approval</td>
<td></td> | Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
<td></td> |---------|-------|------|-----------|-----------|-----------|-----------|--------------|
<td></td> | Webhook support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td></td> | Custom Git Hooks | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td></td> | AD / LDAP integration | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td></td> | Multiple LDAP / AD server support | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
<td></td> | LDAP user synchronization | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
</tr> | OpenId Connect support | ✓ | ✘ | ✓ | ✓ | ✓ | ? | ✘ |
<tr> | OAuth 2.0 integration (external authorization) | ✓ | ✘ | | ✓ | ✓ | ? | ✓ |
<td>Merge conflict resolution</td> | Act as OAuth 2.0 provider | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
<td></td> | Two factor authentication (2FA) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ |
<td></td> | Mattermost/Slack integration | ✓ | ✓ | | ✓ | ✓ | | ✓ |
<td></td> | Discord integration | ✓ | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ |
<td></td> | External CI/CD status display | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Restrict push and merge access to certain users</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Markdown support</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Issues and pull/merge requests templates</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Revert specific commits or a merge request</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Labels</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Time tracking</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Multiple assignees for issues</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Related issues</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Confidential issues</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Comment reactions</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Lock Discussion</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Batch issue handling</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Issue Boards</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Create new branches from issues</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Commit graph</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Web code editor</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Branch manager</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Create new branches</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Repository topics</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Repository code search</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Global code search</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Issue search</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Global issue search</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Git LFS 2.0</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Integrated Git-powered wiki</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Static Git-powered pages</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Group Milestones</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Granular user roles (Code, Issues, Wiki etc)</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Cherry-picking changes</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>GPG Signed Commits</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Reject unsigned commits</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Verified Committer</td>
<td></td>
<td></td>
<td>?</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Subgroups: groups within groups</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Custom Git Hooks</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Repository Activity page</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Deploy Tokens</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Repository Tokens with write rights</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Easy upgrade process</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Built-in Container Registry</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>External git mirroring</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>AD / LDAP integration</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Multiple LDAP / AD server support</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>LDAP user synchronization</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>OpenId Connect support</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>?</td>
<td></td>
</tr>
<tr>
<td>OAuth 2.0 integration (external authorization)</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>?</td>
<td></td>
</tr>
<tr>
<td>Act as OAuth 2.0 provider</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Two factor authentication (2FA)</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>FIDO U2F (2FA)</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Webhook support</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Mattermost/Slack integration</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Discord integration</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Built-in CI/CD</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>External CI/CD status display</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Multiple database support</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Multiple OS support</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Low resource usage (RAM/CPU)</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>

View File

@ -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`. * `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. * `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. * `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 # Customization

View File

@ -67,9 +67,9 @@ func TestAPISearchRepo(t *testing.T) {
expectedResults expectedResults
}{ }{
{name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50", expectedResults: expectedResults{ {name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50", expectedResults: expectedResults{
nil: {count: 15}, nil: {count: 16},
user: {count: 15}, user: {count: 16},
user2: {count: 15}}, user2: {count: 16}},
}, },
{name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10", expectedResults: expectedResults{ {name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10", expectedResults: expectedResults{
nil: {count: 10}, nil: {count: 10},

View File

@ -22,8 +22,12 @@ func TestAccessLevel(t *testing.T) {
user1 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) user1 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
user2 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) user2 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User)
repo1 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 2, IsPrivate: false}).(*Repository) // A public repository owned by User 2
repo2 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 3, IsPrivate: true}).(*Repository) 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) level, err := AccessLevel(user1.ID, repo1)
assert.NoError(t, err) assert.NoError(t, err)
@ -47,8 +51,12 @@ func TestHasAccess(t *testing.T) {
user1 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) user1 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
user2 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) user2 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User)
repo1 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 2, IsPrivate: false}).(*Repository) // A public repository owned by User 2
repo2 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 3, IsPrivate: true}).(*Repository) 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 { for _, accessMode := range accessModes {
has, err := HasAccess(user1.ID, repo1, accessMode) has, err := HasAccess(user1.ID, repo1, accessMode)

View File

@ -351,7 +351,7 @@
is_mirror: true is_mirror: true
num_forks: 1 num_forks: 1
is_fork: false is_fork: false
- -
id: 29 id: 29
fork_id: 27 fork_id: 27
@ -365,7 +365,7 @@
num_closed_pulls: 0 num_closed_pulls: 0
is_mirror: false is_mirror: false
is_fork: true is_fork: true
- -
id: 30 id: 30
fork_id: 28 fork_id: 28
@ -389,3 +389,14 @@
num_forks: 0 num_forks: 0
num_issues: 0 num_issues: 0
is_mirror: false 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

View File

@ -4,9 +4,8 @@
lower_name: owners lower_name: owners
name: Owners name: Owners
authorize: 4 # owner authorize: 4 # owner
num_repos: 2 num_repos: 3
num_members: 1 num_members: 1
unit_types: '[1,2,3,4,5,6,7]'
- -
id: 2 id: 2
@ -16,7 +15,6 @@
authorize: 2 # write authorize: 2 # write
num_repos: 1 num_repos: 1
num_members: 2 num_members: 2
unit_types: '[1,2,3,4,5,6,7]'
- -
id: 3 id: 3
@ -26,7 +24,6 @@
authorize: 4 # owner authorize: 4 # owner
num_repos: 0 num_repos: 0
num_members: 1 num_members: 1
unit_types: '[1,2,3,4,5,6,7]'
- -
id: 4 id: 4
@ -36,7 +33,6 @@
authorize: 4 # owner authorize: 4 # owner
num_repos: 0 num_repos: 0
num_members: 1 num_members: 1
unit_types: '[1,2,3,4,5,6,7]'
- -
id: 5 id: 5
@ -46,7 +42,6 @@
authorize: 4 # owner authorize: 4 # owner
num_repos: 2 num_repos: 2
num_members: 2 num_members: 2
unit_types: '[1,2,3,4,5,6,7]'
- -
id: 6 id: 6
@ -56,4 +51,3 @@
authorize: 4 # owner authorize: 4 # owner
num_repos: 2 num_repos: 2
num_members: 1 num_members: 1
unit_types: '[1,2,3,4,5,6,7]'

View File

@ -33,9 +33,15 @@
org_id: 19 org_id: 19
team_id: 6 team_id: 6
repo_id: 27 repo_id: 27
- -
id: 7 id: 7
org_id: 19 org_id: 19
team_id: 6 team_id: 6
repo_id: 28 repo_id: 28
-
id: 8
org_id: 3
team_id: 1
repo_id: 32

View File

@ -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

View File

@ -45,7 +45,7 @@
is_admin: false is_admin: false
avatar: avatar3 avatar: avatar3
avatar_email: user3@example.com avatar_email: user3@example.com
num_repos: 2 num_repos: 3
num_members: 2 num_members: 2
num_teams: 2 num_teams: 2

View File

@ -71,3 +71,15 @@ func getIssueWatchers(e Engine, issueID int64) (watches []*IssueWatch, err error
Find(&watches) Find(&watches)
return 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
}

View File

@ -186,6 +186,12 @@ var migrations = []Migration{
NewMigration("add u2f", addU2FReg), NewMigration("add u2f", addU2FReg),
// v66 -> v67 // v66 -> v67
NewMigration("add login source id column for public_key table", addLoginSourceIDToPublicKeyTable), 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 // Migrate database to current version

View File

@ -25,10 +25,15 @@ func removeCommitsUnitType(x *xorm.Engine) (err error) {
Created time.Time `xorm:"-"` Created time.Time `xorm:"-"`
} }
type Team struct {
ID int64
UnitTypes []int `xorm:"json"`
}
// Update team unit types // Update team unit types
const batchSize = 100 const batchSize = 100
for start := 0; ; start += batchSize { 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 { if err := x.Limit(batchSize, start).Find(&teams); err != nil {
return err return err
} }
@ -36,7 +41,7 @@ func removeCommitsUnitType(x *xorm.Engine) (err error) {
break break
} }
for _, team := range teams { for _, team := range teams {
ut := make([]models.UnitType, 0, len(team.UnitTypes)) ut := make([]int, 0, len(team.UnitTypes))
for _, u := range team.UnitTypes { for _, u := range team.UnitTypes {
if u < V16UnitTypeCommits { if u < V16UnitTypeCommits {
ut = append(ut, u) ut = append(ut, u)

158
models/migrations/v67.go Normal file
View File

@ -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()
}

160
models/migrations/v68.go Normal file
View File

@ -0,0 +1,160 @@
// 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 (
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"github.com/go-xorm/xorm"
)
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"`
}
sess := x.NewSession()
defer sess.Close()
const batchSize = 100
touchedRepo := make(map[int64]struct{})
topics := make([]*Topic, 0, batchSize)
delTopicIDs := make([]int64, 0, batchSize)
ids := make([]int64, 0, 30)
if err := sess.Begin(); err != nil {
return err
}
log.Info("Validating existed topics...")
for start := 0; ; start += batchSize {
topics = topics[:0]
if err := sess.Asc("id").Limit(batchSize, start).Find(&topics); err != nil {
return err
}
if len(topics) == 0 {
break
}
for _, topic := range topics {
if models.ValidateTopic(topic.Name) {
continue
}
topic.Name = strings.Replace(strings.TrimSpace(strings.ToLower(topic.Name)), " ", "-", -1)
if err := sess.Table("repo_topic").Cols("repo_id").
Where("topic_id = ?", topic.ID).Find(&ids); err != nil {
return err
}
for _, id := range ids {
touchedRepo[id] = struct{}{}
}
if models.ValidateTopic(topic.Name) {
log.Info("Updating topic: id = %v, name = %v", topic.ID, topic.Name)
if _, err := sess.Table("topic").ID(topic.ID).
Update(&Topic{Name: topic.Name}); err != nil {
return err
}
} else {
delTopicIDs = append(delTopicIDs, topic.ID)
}
}
}
log.Info("Deleting incorrect topics...")
for start := 0; ; start += batchSize {
if (start + batchSize) < len(delTopicIDs) {
ids = delTopicIDs[start:(start + batchSize)]
} else {
ids = delTopicIDs[start:]
}
log.Info("Deleting 'repo_topic' rows for topics with ids = %v", ids)
if _, err := sess.In("topic_id", ids).Delete(&models.RepoTopic{}); err != nil {
return err
}
log.Info("Deleting topics with id = %v", ids)
if _, err := sess.In("id", ids).Delete(&Topic{}); err != nil {
return err
}
if len(ids) < batchSize {
break
}
}
repoTopics := make([]*models.RepoTopic, 0, batchSize)
delRepoTopics := make([]*models.RepoTopic, 0, batchSize)
tmpRepoTopics := make([]*models.RepoTopic, 0, 30)
log.Info("Checking the number of topics in the repositories...")
for start := 0; ; start += batchSize {
repoTopics = repoTopics[:0]
if err := sess.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 = tmpRepoTopics[:0]
if err := sess.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])
}
}
}
log.Info("Deleting superfluous topics for repositories (more than 25 topics)...")
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(&models.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
}
}
topicNames := make([]string, 0, 30)
log.Info("Updating repositories 'topics' fields...")
for repoID := range touchedRepo {
if err := sess.Table("topic").Cols("name").
Join("INNER", "repo_topic", "topic.id = repo_topic.topic_id").
Where("repo_topic.repo_id = ?", repoID).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(&models.Repository{Topics: topicNames}); err != nil {
return err
}
}
if err := sess.Commit(); err != nil {
return err
}
return nil
}

80
models/migrations/v69.go Normal file
View File

@ -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()
}

View File

@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -121,6 +122,7 @@ func init() {
new(Reaction), new(Reaction),
new(IssueAssignees), new(IssueAssignees),
new(U2FRegistration), new(U2FRegistration),
new(TeamUnit),
) )
gonicNames := []string{"SSL", "UID"} gonicNames := []string{"SSL", "UID"}
@ -184,6 +186,18 @@ func parsePostgreSQLHostPort(info string) (string, string) {
return host, port 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) { func parseMSSQLHostPort(info string) (string, string) {
host, port := "127.0.0.1", "1433" host, port := "127.0.0.1", "1433"
if strings.Contains(info, ":") { if strings.Contains(info, ":") {
@ -214,14 +228,7 @@ func getEngine() (*xorm.Engine, error) {
DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param) DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param)
} }
case "postgres": case "postgres":
host, port := parsePostgreSQLHostPort(DbCfg.Host) connStr = getPostgreSQLConnectionString(DbCfg.Host, DbCfg.User, DbCfg.Passwd, DbCfg.Name, Param, DbCfg.SSLMode)
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)
}
case "mssql": case "mssql":
host, port := parseMSSQLHostPort(DbCfg.Host) 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) connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, DbCfg.Name, DbCfg.User, DbCfg.Passwd)

View File

@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved. // 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // 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) 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)
}
}

View File

@ -119,7 +119,17 @@ func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthor
} }
} }
issue.loadRepo(e)
for _, watch := range watches { 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 { if err := notifyUser(watch.UserID); err != nil {
return err return err
} }

View File

@ -154,12 +154,26 @@ func CreateOrganization(org, owner *User) (err error) {
Name: ownerTeamName, Name: ownerTeamName,
Authorize: AccessModeOwner, Authorize: AccessModeOwner,
NumMembers: 1, NumMembers: 1,
UnitTypes: allRepUnitTypes,
} }
if _, err = sess.Insert(t); err != nil { if _, err = sess.Insert(t); err != nil {
return fmt.Errorf("insert owner team: %v", err) 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{ if _, err = sess.Insert(&TeamUser{
UID: owner.ID, UID: owner.ID,
OrgID: org.ID, OrgID: org.ID,
@ -238,6 +252,7 @@ func deleteOrg(e *xorm.Session, u *User) error {
&Team{OrgID: u.ID}, &Team{OrgID: u.ID},
&OrgUser{OrgID: u.ID}, &OrgUser{OrgID: u.ID},
&TeamUser{OrgID: u.ID}, &TeamUser{OrgID: u.ID},
&TeamUnit{OrgID: u.ID},
); err != nil { ); err != nil {
return fmt.Errorf("deleteBeans: %v", err) return fmt.Errorf("deleteBeans: %v", err)
} }

View File

@ -1,3 +1,4 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -10,7 +11,6 @@ import (
"strings" "strings"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
) )
@ -28,15 +28,16 @@ type Team struct {
Members []*User `xorm:"-"` Members []*User `xorm:"-"`
NumRepos int NumRepos int
NumMembers 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) getUnits(e Engine) (err error) {
func (t *Team) GetUnitTypes() []UnitType { if t.Units != nil {
if len(t.UnitTypes) == 0 { return nil
return allRepUnitTypes
} }
return t.UnitTypes
t.Units, err = getUnitsByTeamID(e, t.ID)
return err
} }
// HasWriteAccess returns true if team has at least write level access mode. // 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 { if err = watchRepo(e, teamUser.UID, repo.ID, false); err != nil {
return err 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 return nil
@ -209,11 +215,12 @@ func (t *Team) RemoveRepository(repoID int64) error {
// UnitEnabled returns if the team has the given unit type enabled // UnitEnabled returns if the team has the given unit type enabled
func (t *Team) UnitEnabled(tp UnitType) bool { func (t *Team) UnitEnabled(tp UnitType) bool {
if len(t.UnitTypes) == 0 { if err := t.getUnits(x); err != nil {
return true 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 return true
} }
} }
@ -270,6 +277,17 @@ func NewTeam(t *Team) (err error) {
return err 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. // Update organization number of teams.
if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil { if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
sess.Rollback() sess.Rollback()
@ -374,11 +392,34 @@ func DeleteTeam(t *Team) error {
return err return err
} }
if err := t.getMembers(sess); err != nil {
return err
}
// Delete all accesses. // Delete all accesses.
for _, repo := range t.Repos { for _, repo := range t.Repos {
if err := repo.recalculateTeamAccesses(sess, t.ID); err != nil { if err := repo.recalculateTeamAccesses(sess, t.ID); err != nil {
return err 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 // Delete team-repo
@ -396,6 +437,13 @@ func DeleteTeam(t *Team) error {
return err return err
} }
// Delete team-unit.
if _, err := sess.
Where("team_id=?", t.ID).
Delete(new(TeamUnit)); err != nil {
return err
}
// Delete team. // Delete team.
if _, err := sess.ID(t.ID).Delete(new(Team)); err != nil { if _, err := sess.ID(t.ID).Delete(new(Team)); err != nil {
return err return err
@ -518,6 +566,10 @@ func AddTeamMember(team *Team, userID int64) error {
if err := repo.recalculateTeamAccesses(sess, 0); err != nil { if err := repo.recalculateTeamAccesses(sess, 0); err != nil {
return err return err
} }
if err = watchRepo(sess, userID, repo.ID, true); err != nil {
return err
}
} }
return sess.Commit() 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 { if err := repo.recalculateTeamAccesses(e, 0); err != nil {
return err 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. // 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). And("team_repo.repo_id = ?", repoID).
Find(&teams) 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()
}

View File

@ -489,8 +489,8 @@ func TestAccessibleReposEnv_CountRepos(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, expectedCount, count) assert.EqualValues(t, expectedCount, count)
} }
testSuccess(2, 2) testSuccess(2, 3)
testSuccess(4, 1) testSuccess(4, 2)
} }
func TestAccessibleReposEnv_RepoIDs(t *testing.T) { func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
@ -503,8 +503,8 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, expectedRepoIDs, repoIDs) assert.Equal(t, expectedRepoIDs, repoIDs)
} }
testSuccess(2, 1, 100, []int64{3, 5}) testSuccess(2, 1, 100, []int64{3, 5, 32})
testSuccess(4, 0, 100, []int64{3}) testSuccess(4, 0, 100, []int64{3, 32})
} }
func TestAccessibleReposEnv_Repos(t *testing.T) { func TestAccessibleReposEnv_Repos(t *testing.T) {
@ -522,8 +522,8 @@ func TestAccessibleReposEnv_Repos(t *testing.T) {
} }
assert.Equal(t, expectedRepos, repos) assert.Equal(t, expectedRepos, repos)
} }
testSuccess(2, []int64{3, 5}) testSuccess(2, []int64{3, 5, 32})
testSuccess(4, []int64{3}) testSuccess(4, []int64{3, 32})
} }
func TestAccessibleReposEnv_MirrorRepos(t *testing.T) { func TestAccessibleReposEnv_MirrorRepos(t *testing.T) {

View File

@ -365,22 +365,14 @@ func (repo *Repository) getUnitsByUserID(e Engine, userID int64, isAdmin bool) (
return err 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 // unique
var newRepoUnits = make([]*RepoUnit, 0, len(repo.Units)) var newRepoUnits = make([]*RepoUnit, 0, len(repo.Units))
for _, u := range repo.Units { for _, u := range repo.Units {
if _, ok := allTypes[u.Type]; ok { for _, team := range teams {
newRepoUnits = append(newRepoUnits, u) if team.UnitEnabled(u.Type) {
newRepoUnits = append(newRepoUnits, u)
break
}
} }
} }
@ -1851,6 +1843,9 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
if _, err = sess.In("issue_id", issueIDs).Delete(&Reaction{}); err != nil { if _, err = sess.In("issue_id", issueIDs).Delete(&Reaction{}); err != nil {
return err return err
} }
if _, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{}); err != nil {
return err
}
attachments := make([]*Attachment, 0, 5) attachments := make([]*Attachment, 0, 5)
if err = sess. if err = sess.

View File

@ -172,5 +172,14 @@ func (repo *Repository) DeleteCollaboration(uid int64) (err error) {
return err 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() return sess.Commit()
} }

View File

@ -147,10 +147,10 @@ func TestSearchRepositoryByName(t *testing.T) {
count: 14}, count: 14},
{name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", {name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true}, opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true},
count: 15}, count: 16},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", {name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true},
count: 19}, count: 20},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName", {name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true},
count: 13}, count: 13},
@ -159,7 +159,7 @@ func TestSearchRepositoryByName(t *testing.T) {
count: 11}, count: 11},
{name: "AllPublic/PublicRepositoriesOfOrganization", {name: "AllPublic/PublicRepositoriesOfOrganization",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse},
count: 15}, count: 16},
} }
for _, testCase := range testCases { for _, testCase := range testCases {

View File

@ -109,6 +109,23 @@ func notifyWatchers(e Engine, act *Action) error {
act.ID = 0 act.ID = 0
act.UserID = watches[i].UserID 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 { if _, err = e.InsertOne(act); err != nil {
return fmt.Errorf("insert new action: %v", err) return fmt.Errorf("insert new action: %v", err)
} }

View File

@ -6,6 +6,7 @@ package models
import ( import (
"fmt" "fmt"
"regexp"
"strings" "strings"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
@ -20,6 +21,8 @@ func init() {
) )
} }
var topicPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*$`)
// Topic represents a topic of repositories // Topic represents a topic of repositories
type Topic struct { type Topic struct {
ID int64 ID int64
@ -51,6 +54,11 @@ func (err ErrTopicNotExist) Error() string {
return fmt.Sprintf("topic is not exist [name: %s]", err.Name) 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 // GetTopicByName retrieves topic by name
func GetTopicByName(name string) (*Topic, error) { func GetTopicByName(name string) (*Topic, error) {
var topic Topic var topic Topic
@ -182,6 +190,13 @@ func SaveTopics(repoID int64, topicNames ...string) error {
} }
} }
topicNames = topicNames[:0]
if err := sess.Table("topic").Cols("name").
Join("INNER", "repo_topic", "topic.id = repo_topic.topic_id").
Where("repo_topic.repo_id = ?", repoID).Find(&topicNames); err != nil {
return err
}
if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{ if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{
Topics: topicNames, Topics: topicNames,
}); err != nil { }); err != nil {

View File

@ -55,3 +55,16 @@ func TestAddTopic(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 2, len(topics)) 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"))
}

View File

@ -546,28 +546,46 @@ func (u *User) GetRepositories(page, pageSize int) (err error) {
return err return err
} }
// GetRepositoryIDs returns repositories IDs where user owned // GetRepositoryIDs returns repositories IDs where user owned and has unittypes
func (u *User) GetRepositoryIDs() ([]int64, error) { func (u *User) GetRepositoryIDs(units ...UnitType) ([]int64, error) {
var ids []int64 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 // GetOrgRepositoryIDs returns repositories IDs where user's team owned and has unittypes
func (u *User) GetOrgRepositoryIDs() ([]int64, error) { func (u *User) GetOrgRepositoryIDs(units ...UnitType) ([]int64, error) {
var ids []int64 var ids []int64
return ids, x.Table("repository").
sess := x.Table("repository").
Cols("repository.id"). 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) GroupBy("repository.id").Find(&ids)
} }
// GetAccessRepoIDs returns all repositories IDs where user's or user is a team member organizations // GetAccessRepoIDs returns all repositories IDs where user's or user is a team member organizations
func (u *User) GetAccessRepoIDs() ([]int64, error) { func (u *User) GetAccessRepoIDs(units ...UnitType) ([]int64, error) {
ids, err := u.GetRepositoryIDs() ids, err := u.GetRepositoryIDs(units...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ids2, err := u.GetOrgRepositoryIDs() ids2, err := u.GetOrgRepositoryIDs(units...)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -159,3 +159,25 @@ func BenchmarkHashPassword(b *testing.B) {
u.HashPassword(pass) 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)
}

View File

@ -83,12 +83,12 @@ host=Host
user=Benutzername user=Benutzername
password=Passwort password=Passwort
db_name=Datenbankname db_name=Datenbankname
db_helper=Hinweis für MySQL-Benutzer: Bitte verwende das InnoDB Speichersubsystem und den Zeichensatz "utf8_general_ci". db_helper=Hinweis für MySQL-Benutzer: Bitte verwende das InnoDB-Speichersubsystem und den Zeichensatz „utf8_general_ci“.
ssl_mode=SSL ssl_mode=SSL
path=Pfad path=Pfad
sqlite_helper=Der Dateipfad zur SQLite3- oder TiDB-Datenbank. <br>Bitte verwende einen absoluten Pfad, wenn Gitea als Service gestartet wird. sqlite_helper=Der Dateipfad zur SQLite3- oder TiDB-Datenbank. <br>Bitte verwende einen absoluten Pfad, wenn Gitea als Service gestartet wird.
err_empty_db_path=Der SQLite3 oder TiDB Datenbankpfad darf nicht leer sein. 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_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. 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. 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. app_name_helper=Du kannst hier den Namen deines Unternehmens eingeben.
repo_path=Repository-Verzeichnis repo_path=Repository-Verzeichnis
repo_path_helper=Remote-Git-Repositories werden in diesem Verzeichnis gespeichert. 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. 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=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. 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. 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. ssh_port_helper=Der Port deines SSH-Servers. Leer lassen um SSH zu deaktivieren.
http_port=Gitea HTTP-Listen-Port http_port=Gitea-HTTP-Listen-Port
http_port_helper=Port unter dem der Gitea Web Server laufen soll. http_port_helper=Port, unter dem der Gitea-Webserver laufen soll.
app_url=Gitea Basis-URL app_url=Gitea-Basis-URL
app_url_helper=Adresse für HTTP(S)-Klon-URLs und E-Mail-Benachrichtigungen. app_url_helper=Adresse für HTTP(S)-Klon-URLs und E-Mail-Benachrichtigungen.
log_root_path=Logdateipfad log_root_path=Logdateipfad
log_root_path_helper=Log-Dateien werden in diesem Verzeichnis gespeichert. 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_host=SMTP-Server
smtp_from=E-Mail senden als 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" <email@example.com>'-Format ein. smtp_from_helper=E-Mail-Adresse, die von Gitea genutzt werden soll. Bitte gib die E-Mail-Adresse im '"Name" <email@example.com>'-Format ein.
mailer_user=SMTP Benutzername mailer_user=SMTP-Benutzername
mailer_password=SMTP Passwort mailer_password=SMTP-Passwort
register_confirm=E-Mail-Bestätigung benötigt zum Registrieren register_confirm=E-Mail-Bestätigung benötigt zum Registrieren
mail_notify=E-Mail-Benachrichtigungen aktivieren mail_notify=E-Mail-Benachrichtigungen aktivieren
server_service_title=Sonstige Server- und Drittserviceeinstellungen 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=Registrierung deaktivieren
disable_registration_popup=Registrierung neuer Benutzer deaktivieren. Nur Administratoren werden neue Benutzerkonten anlegen können. 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. 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_signin_popup=Benutzeranmeldung via OpenID aktivieren.
openid_signup=OpenID Selbstregistrierung aktivieren openid_signup=OpenID-Selbstregistrierung aktivieren
openid_signup_popup=OpenID-basierte Selbstregistrierung aktivieren. openid_signup_popup=OpenID-basierte Selbstregistrierung aktivieren.
enable_captcha=CAPTCHA aktivieren enable_captcha=CAPTCHA aktivieren
enable_captcha_popup=Captcha-Eingabe bei der Registrierung erforderlich. enable_captcha_popup=Captcha-Eingabe bei der Registrierung erforderlich.
@ -147,10 +147,10 @@ confirm_password=Passwort bestätigen
admin_email=E-Mail-Adresse admin_email=E-Mail-Adresse
install_btn_confirm=Gitea installieren install_btn_confirm=Gitea installieren
test_git_failed=Fehler beim Test des 'git' Kommandos: %v 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_db_setting=Datenbankeinstellungen sind ungültig: %v
invalid_repo_path=Repository-Verzeichnis ist 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 save_config_failed=Fehler beim Speichern der Konfiguration: %v
invalid_admin_setting=Administrator-Konto Einstellungen sind ungültig: %v invalid_admin_setting=Administrator-Konto Einstellungen sind ungültig: %v
install_success=Willkommen! Danke, dass du Gitea gewählt hast. Viel Spaß! 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=Zeiterfassung standardmäßig aktivieren
default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Repositories aktivieren. default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Repositories aktivieren.
no_reply_address=Versteckte E-Mail-Domain 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] [home]
uname_holder=E-Mail-Adresse oder Benutzername uname_holder=E-Mail-Adresse oder Benutzername
@ -225,9 +225,9 @@ login_userpass=Anmelden
login_openid=OpenID login_openid=OpenID
openid_connect_submit=Verbinden openid_connect_submit=Verbinden
openid_connect_title=Mit bestehendem Konto 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_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. 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. 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 Content=Inhalt
require_error=` darf nicht leer sein.` require_error=` darf nicht leer sein.`
alpha_dash_error=` sollte nur Buchstaben, Zahlen, Bindestriche ('-') und Unterstriche ('_') 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` alpha_dash_dot_error=` sollte nur Buchstaben, Zahlen, Bindestriche („-“), Unterstriche („_“) und Punkte („.“) enthalten.`
git_ref_name_error=` muss ein wohlgeformter Git-Referenzname sein.` git_ref_name_error=` muss ein wohlgeformter Git-Referenzname sein.`
size_error=` muss die Größe %s haben.` size_error=` muss die Größe %s haben.`
min_size_error=` muss mindestens %s Zeichen enthalten.` 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_name_been_taken=Der Teamname ist bereits vergeben.
team_no_units_error=Das Team muss auf mindestens einen Bereich Zugriff haben. team_no_units_error=Das Team muss auf mindestens einen Bereich Zugriff haben.
email_been_used=Die E-Mail-Adresse wird bereits verwendet. 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. username_password_incorrect=Benutzername oder Passwort ist falsch.
enterred_invalid_repo_name=Der eingegebenen Repository-Name 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. enterred_invalid_password=Das eingegebene Passwort ist falsch.
user_not_exist=Dieser Benutzer ist nicht vorhanden. 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. 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 invalid_ssh_key=Dein SSH-Key kann nicht überprüft werden: %s
@ -349,7 +349,7 @@ continue=Weiter
cancel=Abbrechen cancel=Abbrechen
language=Sprache 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 federated_avatar_lookup=Suche nach föderierten Profilbildern
enable_custom_avatar=Benutzerdefiniertes Profilbild benutzen enable_custom_avatar=Benutzerdefiniertes Profilbild benutzen
choose_new_avatar=Neues Profilbild auswählen choose_new_avatar=Neues Profilbild auswählen
@ -364,7 +364,7 @@ new_password=Neues Passwort
retype_new_password=Neues Passwort erneut eingeben retype_new_password=Neues Passwort erneut eingeben
password_incorrect=Das aktuelle Passwort ist falsch. password_incorrect=Das aktuelle Passwort ist falsch.
change_password_success=Dein Passwort wurde aktualisiert. Bitte verwende dieses beim nächsten Einloggen. 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 emails=E-Mail-Adressen
manage_emails=E-Mail-Adressen verwalten 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_new_openid=Neue OpenID-URI hinzufügen
add_email=E-Mail-Adresse hinzufügen add_email=E-Mail-Adresse hinzufügen
add_openid=OpenID-URI 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_email_success=Die neue E-Mail-Addresse wurde hinzugefügt.
add_openid_success=Die neue OpenID-Adresse wurde hinzugefügt. add_openid_success=Die neue OpenID-Adresse wurde hinzugefügt.
keep_email_private=E-Mail-Adresse verbergen 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 manage_gpg_keys=GPG-Schlüssel verwalten
add_key=Schlüssel hinzufügen 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. 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. 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=<strong>Brauchst du Hilfe?</strong> Hier ist Githubs Anleitung zum <a href="%s">Erzeugen von SSH-Schlüsseln</a> oder <a href="%s">Lösen einfacher SSH-Probleme</a>. ssh_helper=<strong>Brauchst du Hilfe?</strong> Hier ist GitHubs Anleitung zum <a href="%s">Erzeugen von SSH-Schlüsseln</a> oder zum <a href="%s">Lösen einfacher SSH-Probleme</a>.
gpg_helper=<strong>Brauchst du Hilfe?</strong> Hier ist GitHubs Anleitung <a href="%s">über GPG</a>. gpg_helper=<strong>Brauchst du Hilfe?</strong> Hier ist GitHubs Anleitung <a href="%s">über GPG</a>.
add_new_key=SSH-Schlüssel hinzufügen add_new_key=SSH-Schlüssel hinzufügen
add_new_gpg_key=GPG-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_id=Schlüssel-ID
key_name=Schlüsselname key_name=Schlüsselname
key_content=Inhalt key_content=Inhalt
add_key_success=Der SSH-Schlüssel "%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. add_gpg_key_success=Der GPG-Key „%s“ wurde hinzugefügt.
delete_key=Entfernen delete_key=Entfernen
ssh_key_deletion=SSH-Schlüssel entfernen ssh_key_deletion=SSH-Schlüssel entfernen
gpg_key_deletion=GPG-Schlüssel entfernen gpg_key_deletion=GPG-Schlüssel entfernen
@ -511,10 +511,10 @@ create_repo=Repository erstellen
default_branch=Standardbranch default_branch=Standardbranch
mirror_prune=Entfernen mirror_prune=Entfernen
mirror_prune_desc=Entferne veraltete remote-tracking Referenzen 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_interval_invalid=Das Spiegel-Intervall ist ungültig.
mirror_address=Klonen via URL 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 mirror_last_synced=Zuletzt synchronisiert
watchers=Beobachter watchers=Beobachter
stargazers=Favorisiert von stargazers=Favorisiert von
@ -523,7 +523,7 @@ pick_reaction=Wähle eine Reaktion
reactions_more=und %d weitere reactions_more=und %d weitere
form.reach_limit_of_creation=Du hast bereits dein Limit von %d Repositories erreicht. 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. form.name_pattern_not_allowed='%s' ist nicht erlaubt für Repository-Namen.
need_auth=Authentifizierung zum Klonen benötigt need_auth=Authentifizierung zum Klonen benötigt
@ -588,7 +588,7 @@ editor.edit_file=Datei bearbeiten
editor.preview_changes=Vorschau der Änderungen editor.preview_changes=Vorschau der Änderungen
editor.cannot_edit_non_text_files=Binärdateien können nicht im Webinterface bearbeitet werden. editor.cannot_edit_non_text_files=Binärdateien können nicht im Webinterface bearbeitet werden.
editor.edit_this_file=Datei bearbeiten 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.fork_before_edit=Du musst dieses Repository forken, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen.
editor.delete_this_file=Datei löschen editor.delete_this_file=Datei löschen
editor.must_have_write_access=Du benötigst Schreibzugriff, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. 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.or=oder
editor.cancel_lower=Abbrechen editor.cancel_lower=Abbrechen
editor.commit_changes=Änderungen committen editor.commit_changes=Änderungen committen
editor.add_tmpl='%s/<filename>' hinzufügen editor.add_tmpl=„%s/<filename>“ hinzufügen
editor.add='%s' hinzufügen editor.add=„%s“ hinzufügen
editor.update='%s' ändern editor.update=„%s“ ändern
editor.delete='%s' löschen editor.delete=„%s“ löschen
editor.commit_message_desc=Eine ausführlichere (optionale) Beschreibung hinzufügen… editor.commit_message_desc=Eine ausführlichere (optionale) Beschreibung hinzufügen…
editor.commit_directly_to_this_branch=Direkt in die <strong class="branch-name">%s</strong>-Branch einchecken. editor.commit_directly_to_this_branch=Direkt in den Branch „<strong class="branch-name">%s</strong> einchecken.
editor.create_new_branch=Einen <strong>neue Branch</strong> für diesen Commit erstellen und einen Pull Request starten. editor.create_new_branch=Einen <strong>neuen Branch</strong> für diesen Commit erstellen und einen Pull Request starten.
editor.new_branch_name_desc=Neuer Branchname… editor.new_branch_name_desc=Neuer Branchname…
editor.cancel=Abbrechen editor.cancel=Abbrechen
editor.filename_cannot_be_empty=Der Dateiname darf nicht leer sein. editor.filename_cannot_be_empty=Der Dateiname darf nicht leer sein.
editor.branch_already_exists=Branch '%s' existiert bereits in diesem Repository. 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.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.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_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. <a target="_blank" rel="noopener" href="%s">Hier klicken</a> um die Änderungen anzusehen, oder <strong>Änderungen erneut comitten</strong> um sie zu überschreiben. editor.file_changed_while_editing=Der Inhalt der Datei hat sich seit dem Beginn der Bearbeitung geändert. <a target="_blank" rel="noopener" href="%s">Hier klicken</a>, um die Änderungen anzusehen, oder <strong>Änderungen erneut comitten</strong>, um sie zu überschreiben.
editor.file_already_exists=Eine Datei mit dem Namen '%s' ist bereits in diesem Repository vorhanden. 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.no_changes_to_show=Keine Änderungen vorhanden.
editor.fail_to_update_file=Fehler beim Ändern/Erstellen der Datei '%s'. Fehler: %v editor.fail_to_update_file=Fehler beim Ändern/Erstellen der Datei '%s'. Fehler: %v
editor.add_subdir=Verzeichnis erstellen… 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.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.commits=Commits
commits.search=Commits durchsuchen… commits.search=Commits durchsuchen…
commits.find=Suchen commits.find=Suchen
@ -633,7 +633,7 @@ commits.date=Datum
commits.older=Älter commits.older=Älter
commits.newer=Neuer commits.newer=Neuer
commits.signed_by=Signiert von 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=Externe Issues
ext_issues.desc=Link zu externem Issuetracker. ext_issues.desc=Link zu externem Issuetracker.
@ -658,7 +658,7 @@ issues.new_label_placeholder=Labelname
issues.new_label_desc_placeholder=Beschreibung issues.new_label_desc_placeholder=Beschreibung
issues.create_label=Label erstellen issues.create_label=Label erstellen
issues.label_templates.title=Lade vordefinierte Label 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.helper=Wähle ein Label
issues.label_templates.use=Label-Set verwenden issues.label_templates.use=Label-Set verwenden
issues.label_templates.fail_to_load_file=Fehler beim Laden der Label Template Datei '%s': %v 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 <b>%s</b> %s`
issues.open_tab=%d offen issues.open_tab=%d offen
issues.close_tab=%d geschlossen issues.close_tab=%d geschlossen
issues.filter_label=Label 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=Meilenstein
issues.filter_milestone_no_select=Alle Meilensteine issues.filter_milestone_no_select=Alle Meilensteine
issues.filter_assignee=Zuständig 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_total=Zeitaufwand insgesamt
issues.time_spent_from_all_authors=`Aufgewendete Zeit: %s` issues.time_spent_from_all_authors=`Aufgewendete Zeit: %s`
issues.due_date=Fällig am 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_modifying_due_date=Fehler beim Ändern des Fälligkeitsdatums.
issues.error_removing_due_date=Fehler beim Entfernen 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_add=Fälligkeitsdatum hinzufügen
issues.due_date_form_update=Fälligkeitsdatum ändern issues.due_date_form_update=Fälligkeitsdatum ändern
issues.due_date_form_remove=Fälligkeitsdatum löschen 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=Neuer Pull-Request
pulls.compare_changes_desc=Wähle die Ziel- und Quellbranch aus. pulls.compare_changes_desc=Wähle die Ziel- und Quellbranch aus.
pulls.compare_base=Ziel pulls.compare_base=Ziel
pulls.compare_compare=pull von pulls.compare_compare=pullen von
pulls.filter_branch=Branch filtern pulls.filter_branch=Branch filtern
pulls.no_results=Keine Ergebnisse verfügbar. pulls.no_results=Keine Ergebnisse verfügbar.
pulls.nothing_to_compare=Diese Branches sind identisch. Es muss kein Pull-Request erstellt werden. 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.desc=Beschreibung
milestones.due_date=Fälligkeitsdatum (optional) milestones.due_date=Fälligkeitsdatum (optional)
milestones.clear=Feld leeren milestones.clear=Feld leeren
milestones.invalid_due_date_format=Das Fälligkeitsdatum muss das Format 'JJJJ-MM-TT' haben. milestones.invalid_due_date_format=Das Fälligkeitsdatum muss das Format „JJJJ-MM-TT“ haben.
milestones.create_success=Der Meilenstein '%s' wurde erstellt. milestones.create_success=Der Meilenstein „%s“ wurde erstellt.
milestones.edit=Meilenstein bearbeiten milestones.edit=Meilenstein bearbeiten
milestones.edit_subheader=Benutze Meilensteine, um Issues zu organisieren und den Fortschritt darzustellen. milestones.edit_subheader=Benutze Meilensteine, um Issues zu organisieren und den Fortschritt darzustellen.
milestones.cancel=Abbrechen milestones.cancel=Abbrechen
milestones.modify=Meilenstein bearbeiten 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=Meilenstein löschen
milestones.deletion_desc=Das Löschen des Meilensteins entfernt ihn von allen Issues. Fortfahren? milestones.deletion_desc=Das Löschen des Meilensteins entfernt ihn von allen Issues. Fortfahren?
milestones.deletion_success=Der Meilenstein wurde gelöscht. milestones.deletion_success=Der Meilenstein wurde gelöscht.
@ -849,7 +849,7 @@ ext_wiki.desc=Verweis auf externes Wiki.
wiki=Wiki wiki=Wiki
wiki.welcome=Willkommen im 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.desc=Schreibe und teile Dokumentation mit Mitarbeitern.
wiki.create_first_page=Erstelle die erste Seite wiki.create_first_page=Erstelle die erste Seite
wiki.page=Seite wiki.page=Seite
@ -861,9 +861,9 @@ wiki.last_commit_info=%s hat diese Seite bearbeitet %s
wiki.edit_page_button=Bearbeiten wiki.edit_page_button=Bearbeiten
wiki.new_page_button=Neue Seite wiki.new_page_button=Neue Seite
wiki.delete_page_button=Seite löschen 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.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.pages=Seiten
wiki.last_updated=Zuletzt aktualisiert %s wiki.last_updated=Zuletzt aktualisiert %s
@ -911,7 +911,7 @@ activity.published_release_label=Veröffentlicht
search=Suchen search=Suchen
search.search_repo=Repository durchsuchen search.search_repo=Repository durchsuchen
search.results=Suchergebnisse für "%s" in <a href="%s"> %s</a> search.results=Suchergebnisse für „%s“ in <a href="%s"> %s</a>
settings=Einstellungen settings=Einstellungen
settings.desc=In den Einstellungen kannst du die Einstellungen des Repository anpassen settings.desc=In den Einstellungen kannst du die Einstellungen des Repository anpassen
@ -925,28 +925,28 @@ settings.hooks=Webhooks
settings.githooks=Git-Hooks settings.githooks=Git-Hooks
settings.basic_settings=Grundeinstellungen settings.basic_settings=Grundeinstellungen
settings.mirror_settings=Mirror Einstellungen 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.mirror_sync_in_progress=Mirror-Synchronisierung wird zurzeit ausgeführt. Komm in ein paar Minuten zurück.
settings.site=Webseite settings.site=Webseite
settings.update_settings=Einstellungen speichern settings.update_settings=Einstellungen speichern
settings.advanced_settings=Erweiterte Einstellungen 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_internal_wiki=Eingebautes Wiki verwenden
settings.use_external_wiki=Externes Wiki verwenden settings.use_external_wiki=Externes Wiki verwenden
settings.external_wiki_url=Externe Wiki URL settings.external_wiki_url=Externe Wiki URL
settings.external_wiki_url_error=Die externe Wiki-URL ist ungültig. 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.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.issues_desc=Repository-Issue-Tracker aktivieren
settings.use_internal_issue_tracker=Integrierten Issue-Tracker verwenden settings.use_internal_issue_tracker=Integrierten Issue-Tracker verwenden
settings.use_external_issue_tracker=Externen 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=URL eines externen Issue Trackers
settings.external_tracker_url_error=Die URL des externen Issue-Trackers ist ungültig. 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_url_format=URL-Format des externen Issue-Systems
settings.tracker_issue_style=Namenskonvention des externen Issue-Trackers settings.tracker_issue_style=Namenskonvention des externen Issue-Trackers
settings.tracker_issue_style.numeric=Numerisch settings.tracker_issue_style.numeric=Numerisch
settings.tracker_issue_style.alphanumeric=Alphanumerisch settings.tracker_issue_style.alphanumeric=Alphanumerisch
settings.tracker_url_format_desc=Du kannst die Platzhalter <code>{user}</code>, <code>{repo}</code>, <code>{index}</code> für den Benutzernamen, den Namen des Repositories und die Issue-Nummer verwenden. settings.tracker_url_format_desc=Du kannst die Platzhalter <code>{user}</code>, <code>{repo}</code>, <code>{index}</code> für den Benutzernamen, den Namen des Repositorys und die Issue-Nummer verwenden.
settings.enable_timetracker=Zeiterfassung aktivieren settings.enable_timetracker=Zeiterfassung aktivieren
settings.allow_only_contributors_to_track_time=Nur Mitarbeitern erlauben, die Zeiterfassung zu nutzen settings.allow_only_contributors_to_track_time=Nur Mitarbeitern erlauben, die Zeiterfassung zu nutzen
settings.pulls_desc=Repository-Pull-Requests aktivieren 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_confirm=Repository umwandeln
settings.convert_succeed=Das Mirror-Repository wurde erfolgreich in ein normales Repository umgewandelt. settings.convert_succeed=Das Mirror-Repository wurde erfolgreich in ein normales Repository umgewandelt.
settings.transfer=Besitz übertragen 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_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_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_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.transfer_form_title=Gib den Repository-Namen zur Bestätigung ein:
settings.wiki_delete=Wiki-Daten löschen 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_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.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=Dieses Repository löschen
settings.delete_desc=Wenn dieses Repository gelöscht wurde, gibt es keinen Weg zurück. Bitte sei vorsichtig. 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 <strong>NICHT</strong> rückgängig gemacht werden. settings.delete_notices_1=- Diese Operation kann <strong>NICHT</strong> rückgängig gemacht werden.
settings.delete_notices_2=- Die Operation wird das <strong>%s</strong>-Repository dauerhaft löschen, inklusive der Dateien, Issues, Kommentare und Zugriffseinstellungen. settings.delete_notices_2= Die Operation wird das <strong>%s</strong>-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_fork_1= Forks dieses Repositorys werden nach dem Löschen unabhängig.
settings.deletion_success=Das Repository wurde gelöscht. 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.transfer_owner=Neuer Besitzer
settings.make_transfer=Transfer durchführen settings.make_transfer=Transfer durchführen
settings.transfer_succeed=Das Repository wurde transferiert. 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.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.user_is_org_member=Der Benutzer ist ein Organisationsmitglied und kann nicht als Mitarbeiter hinzugefügt werden.
settings.add_webhook=Webhook hinzufügen 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 <a target="_blank" rel="noopener" href="%s">Anleitung zu Webhooks (Englisch)</a>. settings.hooks_desc=Webhooks senden bei bestimmten Gitea-Events automatisch „HTTP POST“-Anfragen an einen Server. Lies mehr in unserer <a target="_blank" rel="noopener" href="%s">Anleitung zu Webhooks (auf Englisch)</a>.
settings.webhook_deletion=Webhook löschen settings.webhook_deletion=Webhook löschen
settings.webhook_deletion_desc=Das Entfernen eines Webhooks löscht seine Einstellungen und Zustellungsverlauf. Fortfahren? settings.webhook_deletion_desc=Das Entfernen eines Webhooks löscht seine Einstellungen und Zustellungsverlauf. Fortfahren?
settings.webhook_deletion_success=Webhook wurde entfernt. settings.webhook_deletion_success=Webhook wurde entfernt.
@ -1066,18 +1066,18 @@ settings.title=Titel
settings.deploy_key_content=Inhalt settings.deploy_key_content=Inhalt
settings.key_been_used=Ein Deploy-Key mit identischem Inhalt wird bereits verwendet. 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.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=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_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.deploy_key_deletion_success=Der Deploy-Key wurde entfernt.
settings.branches=Branches 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=Push erlauben?
settings.protected_branch_can_push_yes=Du kannst pushen settings.protected_branch_can_push_yes=Du kannst pushen
settings.protected_branch_can_push_no=Du kannst nicht pushen settings.protected_branch_can_push_no=Du kannst nicht pushen
settings.branch_protection=Branch-Schutz" für Branch '<b>%s</b>' settings.branch_protection=Branch-Schutz für Branch „<b>%s</b>“
settings.protect_this_branch=Brach-Schutz aktivieren 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=Push-Whitelist aktivieren
settings.protect_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist Push-Beschränkungen zu umgehen. 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: 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_teams=Teams, die pushen dürfen:
settings.protect_whitelist_search_teams=Suche nach Teams… settings.protect_whitelist_search_teams=Suche nach Teams…
settings.protect_merge_whitelist_committers=Merge-Whitelist aktivieren 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_users=Nutzer, die mergen dürfen:
settings.protect_merge_whitelist_teams=Teams, die mergen dürfen: settings.protect_merge_whitelist_teams=Teams, die mergen dürfen:
settings.add_protected_branch=Schutz aktivieren settings.add_protected_branch=Schutz aktivieren
settings.delete_protected_branch=Schutz deaktivieren settings.delete_protected_branch=Schutz deaktivieren
settings.update_protect_branch_success=Branch-protection für die Branch '%s' wurde geändert. settings.update_protect_branch_success=Branch-Schutz für den Branch „%s“ wurde geändert.
settings.remove_protected_branch_success=Branch-protection für die Branch '%s' wurde deaktiviert. settings.remove_protected_branch_success=Branch-Schutz für den Branch „%s“ wurde deaktiviert.
settings.protected_branch_deletion=Brach-Schutz deaktivieren settings.protected_branch_deletion=Branch-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.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 eine Standardbranch für Pull-Requests und Code-Commits: settings.default_branch_desc=Wähle einen Standardbranch für Pull-Requests und Code-Commits:
settings.choose_branch=Wähle eine Branch… settings.choose_branch=Wähle einen Branch
settings.no_protected_branch=Es gibt keine geschützten Branches. settings.no_protected_branch=Es gibt keine geschützten Branches.
diff.browse_source=Quellcode durchsuchen diff.browse_source=Quellcode durchsuchen
@ -1131,7 +1131,7 @@ release.write=Schreiben
release.preview=Vorschau release.preview=Vorschau
release.loading=Laden… release.loading=Laden…
release.prerelease_desc=Als Pre-Release kennzeichnen 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.cancel=Abbrechen
release.publish=Release veröffentlichen release.publish=Release veröffentlichen
release.save_draft=Entwurf speichern release.save_draft=Entwurf speichern
@ -1146,27 +1146,29 @@ release.downloads=Downloads
branch.name=Branchname branch.name=Branchname
branch.search=Branches durchsuchen 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_head=Löschen
branch.delete=Branch '%s' löschen branch.delete=Branch „%s“ löschen
branch.delete_html=Branch löschen branch.delete_html=Branch löschen
branch.delete_desc=Das Löschen einer Branch ist permanent. Es <strong>KANN NICHT</strong> Rückgängig gemacht werden. Fortfahren? branch.delete_desc=Das Löschen eines Branches ist permanent. Es <strong>KANN NICHT</strong> rückgängig gemacht werden. Fortfahren?
branch.deletion_success=Branch '%s' wurde gelöscht. branch.deletion_success=Branch „%s“ wurde gelöscht.
branch.deletion_failed=Branch '%s' konnte nicht gelöscht werden. 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_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 <strong>%s</strong> branch.create_branch=Erstelle Branch <strong>%s</strong>
branch.create_from=von '%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_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.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.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.deleted_by=Von %s gelöscht
branch.restore_success=Branch '%s' wurde wiederhergestellt. branch.restore_success=Branch „%s“ wurde wiederhergestellt.
branch.restore_failed=Wiederherstellung der Branch '%s' fehlgeschlagen. branch.restore_failed=Wiederherstellung des Branches „%s“ fehlgeschlagen.
branch.protected_deletion_failed=Branch '%s' ist geschützt und kann nicht gelöscht werden. branch.protected_deletion_failed=Branch „%s“ ist geschützt und kann nicht gelöscht werden.
topic.manage_topics=Themen verwalten topic.manage_topics=Themen verwalten
topic.done=Fertig 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]
org_name_holder=Name der Organisation 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_permission_desc=Berechtigungen
team_unit_desc=Zugriff auf Repositorybereiche erlauben team_unit_desc=Zugriff auf Repositorybereiche erlauben
form.name_reserved=Der Organisationsname '%s' ist reserviert. form.name_reserved=Der Organisationsname „%s“ ist reserviert.
form.name_pattern_not_allowed=Das Muster '%s' ist in Organisationsnamen nicht erlaubt. 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.create_org_not_allowed=Du bist nicht berechtigt, eine Organisation zu erstellen.
settings=Einstellungen settings=Einstellungen
settings.options=Organisation 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=Schreibzugriff
teams.write_access_helper=Mitglieder können Teamrepositories ansehen und auf sie pushen. teams.write_access_helper=Mitglieder können Teamrepositories ansehen und auf sie pushen.
teams.admin_access=Administratorzugang 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.no_desc=Dieses Team hat keine Beschreibung
teams.settings=Einstellungen teams.settings=Einstellungen
teams.owners_permission_desc=Besitzer haben vollen Zugriff auf <strong>alle Repositories</strong> und <strong>Admin-Rechte</strong> für diese Organisation. teams.owners_permission_desc=Besitzer haben vollen Zugriff auf <strong>alle Repositories</strong> und <strong>Admin-Rechte</strong> für diese Organisation.
@ -1238,7 +1240,7 @@ teams.update_settings=Einstellungen aktualisieren
teams.delete_team=Team löschen teams.delete_team=Team löschen
teams.add_team_member=Teammitglied hinzufügen teams.add_team_member=Teammitglied hinzufügen
teams.delete_team_title=Team löschen 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.delete_team_success=Das Team wurde gelöscht.
teams.read_permission_desc=Dieses Team hat <strong>Lesezugriff</strong>: Mitglieder können Team-Repositories einsehen und klonen. teams.read_permission_desc=Dieses Team hat <strong>Lesezugriff</strong>: Mitglieder können Team-Repositories einsehen und klonen.
teams.write_permission_desc=Dieses Team hat <strong>Schreibzugriff</strong>: Mitglieder können Team-Repositories einsehen und darauf pushen. teams.write_permission_desc=Dieses Team hat <strong>Schreibzugriff</strong>: 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_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=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.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=Garbage-Collection auf Repositories ausführen
dashboard.git_gc_repos_success=Alle Repositories haben Garbage Collection beendet. 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=„.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_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=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 synchronisiert. 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=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.reinit_missing_repos_success=Alle verlorenen Git-Repositories mit existierenden Einträgen wurden erfolgreich aktualisiert.
dashboard.sync_external_users=Externe Benutzerdaten synchronisieren dashboard.sync_external_users=Externe Benutzerdaten synchronisieren
@ -1305,11 +1307,11 @@ dashboard.heap_memory_released=Freigegebener Heap-Memory
dashboard.heap_objects=Heap-Objekte dashboard.heap_objects=Heap-Objekte
dashboard.bootstrap_stack_usage=Bootstrap-Stack-Auslastung dashboard.bootstrap_stack_usage=Bootstrap-Stack-Auslastung
dashboard.stack_memory_obtained=Erhaltener Stack-Memory dashboard.stack_memory_obtained=Erhaltener Stack-Memory
dashboard.mspan_structures_usage=MSpan-Structures Auslastung dashboard.mspan_structures_usage=MSpan-Structures-Auslastung
dashboard.mspan_structures_obtained=MSpan-Structures erhalten dashboard.mspan_structures_obtained=Erhaltene MSpan-Structures
dashboard.mcache_structures_usage=MCache-Structures Auslastung dashboard.mcache_structures_usage=MCache-Structures-Auslastung
dashboard.mcache_structures_obtained=Erhaltene MCache-Structures 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.gc_metadata_obtained=Erhaltene GC-Metadata
dashboard.other_system_allocation_obtained=Andere erhaltene System-Allokationen dashboard.other_system_allocation_obtained=Andere erhaltene System-Allokationen
dashboard.next_gc_recycle=Nächster GC-Zyklus 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.is_activated=Account ist aktiviert
users.prohibit_login=Anmelden deaktivieren users.prohibit_login=Anmelden deaktivieren
users.is_admin=Ist Administrator 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_import_local=Darf lokale Repositories importieren
users.allow_create_organization=Darf Organisationen erstellen users.allow_create_organization=Darf Organisationen erstellen
users.update_profile=Benutzerkonto aktualisieren users.update_profile=Benutzerkonto aktualisieren
@ -1384,31 +1386,31 @@ auths.bind_dn=DN binden
auths.bind_password=Passwort 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.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_base=Basis für Benutzersuche
auths.user_dn=Benutzer DN auths.user_dn=Benutzer-DN
auths.attribute_username=Benutzername Attribut auths.attribute_username=Benutzernamens-Attribut
auths.attribute_username_placeholder=Leerlassen, um den in Gitea eingegebenen Benutzernamen zu verwenden. auths.attribute_username_placeholder=Leerlassen, um den in Gitea eingegebenen Benutzernamen zu verwenden.
auths.attribute_name=Vornamensattribut auths.attribute_name=Vornamensattribut
auths.attribute_surname=Nachnamensattribut auths.attribute_surname=Nachnamensattribut
auths.attribute_mail=E-Mail Attribut auths.attribute_mail=E-Mail-Attribut
auths.attribute_ssh_public_key=Öffentliches SSH-Schlüssel Attribut auths.attribute_ssh_public_key=Öffentlicher-SSH-Schlüssel-Attribut
auths.attributes_in_bind=Hole Attribute im Bind-Kontext auths.attributes_in_bind=Hole Attribute im Bind-Kontext
auths.use_paged_search=Seitensuche verwenden auths.use_paged_search=Seitensuche verwenden
auths.search_page_size=Seitengröße auths.search_page_size=Seitengröße
auths.filter=Benutzerfilter auths.filter=Benutzerfilter
auths.admin_filter=Admin Filter auths.admin_filter=Admin-Filter
auths.ms_ad_sa=MS AD Suchattribute auths.ms_ad_sa=MS-AD-Suchattribute
auths.smtp_auth=SMTP-Authentifizierungstyp auths.smtp_auth=SMTP-Authentifizierungstyp
auths.smtphost=SMTP-Host auths.smtphost=SMTP-Host
auths.smtpport=SMTP-Port auths.smtpport=SMTP-Port
auths.allowed_domains=Erlaubte Domains 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.enable_tls=TLS-Verschlüsselung aktivieren
auths.skip_tls_verify=TLS Verifikation überspringen auths.skip_tls_verify=TLS-Verifikation überspringen
auths.pam_service_name=PAM Dienstname auths.pam_service_name=PAM-Dienstname
auths.oauth2_provider=OAuth2 Anbieter auths.oauth2_provider=OAuth2-Anbieter
auths.oauth2_clientID=Client-ID (Schlüssel) auths.oauth2_clientID=Client-ID (Schlüssel)
auths.oauth2_clientSecret=Client-Secret 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_use_custom_url=Benutzerdefinierte URLs anstelle von Standard-URLs verwenden
auths.oauth2_tokenURL=Token-URL auths.oauth2_tokenURL=Token-URL
auths.oauth2_authURL=Authorisierungs-URL auths.oauth2_authURL=Authorisierungs-URL
@ -1416,48 +1418,48 @@ auths.oauth2_profileURL=Profil-URL
auths.oauth2_emailURL=E-Mail-URL auths.oauth2_emailURL=E-Mail-URL
auths.enable_auto_register=Automatische Registrierung aktivieren auths.enable_auto_register=Automatische Registrierung aktivieren
auths.tips=Tipps auths.tips=Tipps
auths.tips.oauth2.general=OAuth2 Authentifizierung auths.tips.oauth2.general=OAuth2-Authentifizierung
auths.tips.oauth2.general.tip=Beim Registrieren einer neuen OAuth2 Authentifizierung sollte die Callback/Weiterleitungs-URL <host>/user/oauth2/<Authentication Name>/callback sein. auths.tips.oauth2.general.tip=Beim Registrieren einer neuen OAuth2-Authentifizierung sollte die Callback-/Weiterleitungs-URL „<host>/user/oauth2/<Authentication Name>/callback sein.
auths.tip.oauth2_provider=OAuth2 Anbieter auths.tip.oauth2_provider=OAuth2-Anbieter
auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter https://bitbucket.org/account/user/<dein-benutzername>/oauth-consumers/new und füge die Berechtigung "Account"-"Read" hinzu. auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter https://bitbucket.org/account/user/<dein-benutzername>/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.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.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.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.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 (<server>/.well-known/openid-configuration) als Endpunkt. auths.tip.openid_connect=Benutze die OpenID Connect Discovery URL (<server>/.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.edit=Authentifikationsquelle bearbeiten
auths.activated=Diese Authentifikationsquelle ist aktiviert 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_success=Diese Authentifizierungsquelle wurde aktualisiert.
auths.update=Authentifizierungsquelle aktualisieren auths.update=Authentifizierungsquelle aktualisieren
auths.delete=Authentifikationsquelle löschen auths.delete=Authentifikationsquelle löschen
auths.delete_auth_title=Authentifizierungsquelle 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.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.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.deletion_success=Die Authentifizierungsquelle „%s“ wurde gelöscht.
auths.login_source_exist=Die Authentifizierungsquelle '%s' existiert bereits. auths.login_source_exist=Die Authentifizierungsquelle „%s“ existiert bereits.
config.server_config=Serverkonfiguration config.server_config=Serverkonfiguration
config.app_name=Seitentitel config.app_name=Seitentitel
config.app_ver=Gitea Version config.app_ver=Gitea-Version
config.app_url=Gitea Basis-URL config.app_url=Gitea-Basis-URL
config.custom_conf=Konfigurations-Datei-Pfad config.custom_conf=Konfigurations-Datei-Pfad
config.domain=SSH Server-Domain config.domain=SSH-Server-Domain
config.offline_mode=Lokaler Modus config.offline_mode=Lokaler Modus
config.disable_router_log=Router-Log deaktivieren config.disable_router_log=Router-Log deaktivieren
config.run_user=Ausführen als config.run_user=Ausführen als
config.run_mode=Laufzeit-Modus config.run_mode=Laufzeit-Modus
config.git_version=Git Version config.git_version=Git-Version
config.repo_root_path=Repository-Verzeichnis config.repo_root_path=Repository-Wurzelpfad
config.lfs_root_path=LFS-Wurzelpfad config.lfs_root_path=LFS-Wurzelpfad
config.static_file_root_path=Verzeichnis für statische Dateien config.static_file_root_path=Verzeichnis für statische Dateien
config.log_file_root_path=Logdateipfad config.log_file_root_path=Logdateipfad
config.script_type=Skript-Typ config.script_type=Skript-Typ
config.reverse_auth_user=Nutzer bei Reverse-Authentifizierung config.reverse_auth_user=Nutzer bei Reverse-Authentifizierung
config.ssh_config=SSH Konfiguration config.ssh_config=SSH-Konfiguration
config.ssh_enabled=Aktiviert config.ssh_enabled=Aktiviert
config.ssh_start_builtin_server=Eingebauten Server verwenden config.ssh_start_builtin_server=Eingebauten Server verwenden
config.ssh_domain=Server-Domain config.ssh_domain=Server-Domain
@ -1465,9 +1467,9 @@ config.ssh_port=Port
config.ssh_listen_port=Listen-Port config.ssh_listen_port=Listen-Port
config.ssh_root_path=Wurzelverzeichnis config.ssh_root_path=Wurzelverzeichnis
config.ssh_key_test_path=Schlüssel-Test-Pfad 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_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_config=Datenbankkonfiguration
config.db_type=Typ 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.register_email_confirm=E-Mail-Bestätigung benötigt zum Registrieren
config.disable_register=Selbstegistrierung deaktivieren config.disable_register=Selbstegistrierung deaktivieren
config.allow_only_external_registration=Registrierung nur über externe Services aktiveren config.allow_only_external_registration=Registrierung nur über externe Services aktiveren
config.enable_openid_signup=OpenID Selbstregistrierung aktivieren config.enable_openid_signup=OpenID-Selbstregistrierung aktivieren
config.enable_openid_signin=OpenID Anmeldung aktivieren config.enable_openid_signin=OpenID-Anmeldung aktivieren
config.show_registration_button=Schaltfläche zum Registrieren anzeigen config.show_registration_button=Schaltfläche zum Registrieren anzeigen
config.require_sign_in_view=Seiten nur für angemeldete Benutzer zugänglich config.require_sign_in_view=Seiten nur für angemeldete Benutzer zugänglich
config.mail_notify=E-Mail-Benachrichtigungen aktivieren config.mail_notify=E-Mail-Benachrichtigungen aktivieren
config.disable_key_size_check=Prüfung der Mindestschlüssellänge deaktiveren config.disable_key_size_check=Prüfung der Mindestschlüssellänge deaktiveren
config.enable_captcha=CAPTCHA aktivieren 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.reset_password_code_lives=Ablaufdatum des Passworts zurücksetzen
config.default_keep_email_private=E-Mail-Adressen standardmäßig verbergen 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.enable_timetracking=Zeiterfassung aktivieren
config.default_enable_timetracking=Zeiterfassung standardmäßig aktivieren config.default_enable_timetracking=Zeiterfassung standardmäßig aktivieren
config.default_allow_only_contributors_to_track_time=Nur Mitarbeitern erlauben, die Zeiterfassung zu nutzen 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.webhook_config=Webhook-Konfiguration
config.queue_length=Warteschlangenlänge config.queue_length=Warteschlangenlänge
config.deliver_timeout=Zeitlimit für Zustellung 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_enabled=Aktiviert
config.mailer_disable_helo=HELO Deaktivieren config.mailer_disable_helo=HELO deaktivieren
config.mailer_name=Name config.mailer_name=Name
config.mailer_host=Host config.mailer_host=Host
config.mailer_user=Benutzer config.mailer_user=Benutzer
@ -1512,8 +1514,8 @@ config.mailer_use_sendmail=Sendmail benutzen
config.mailer_sendmail_path=Sendmail-Pfad config.mailer_sendmail_path=Sendmail-Pfad
config.mailer_sendmail_args=Zusätzliche Argumente für Sendmail config.mailer_sendmail_args=Zusätzliche Argumente für Sendmail
config.send_test_mail=Test-E-Mail senden 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_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_sent=Eine Test-E-Mail wurde an „%s“ gesendet.
config.oauth_config=OAuth-Konfiguration config.oauth_config=OAuth-Konfiguration
config.oauth_enabled=Aktiviert config.oauth_enabled=Aktiviert
@ -1533,16 +1535,16 @@ config.session_life_time=Session-Lebensdauer
config.https_only=Nur HTTPS config.https_only=Nur HTTPS
config.cookie_life_time=Cookie-Lebensdauer config.cookie_life_time=Cookie-Lebensdauer
config.picture_config=Avatar-Konfiguration config.picture_config=Bild-und-Profilbild-Konfiguration
config.picture_service=Bilderservice config.picture_service=Bilderservice
config.disable_gravatar=Gravatar deaktivieren config.disable_gravatar=Gravatar deaktivieren
config.enable_federated_avatar=Föderierte Profilbilder einschalten config.enable_federated_avatar=Föderierte Profilbilder einschalten
config.git_config=Git Konfiguration config.git_config=Git-Konfiguration
config.git_disable_diff_highlight=Diff Syntaxhervorhebung ausschalten config.git_disable_diff_highlight=Diff-Syntaxhervorhebung ausschalten
config.git_max_diff_lines=Max Diff Zeilen (in einer Datei) 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_line_characters=Max. Diff-Zeichen (in einer Zeile)
config.git_max_diff_files=Max Diff Dateien (Anzeige) config.git_max_diff_files=Max. Diff-Dateien (Angezeigte)
config.git_gc_args=GC-Argumente config.git_gc_args=GC-Argumente
config.git_migrate_timeout=Zeitlimit für Migration config.git_migrate_timeout=Zeitlimit für Migration
config.git_mirror_timeout=Zeitlimit für Mirror-Aktualisierung config.git_mirror_timeout=Zeitlimit für Mirror-Aktualisierung
@ -1638,12 +1640,12 @@ mark_all_as_read=Alle als gelesen markieren
[gpg] [gpg]
error.extract_sign=Die Signatur konnte nicht extrahiert werden error.extract_sign=Die Signatur konnte nicht extrahiert werden
error.generate_hash=Es konnte kein Hash vom Commit generiert 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.no_gpg_keys_found=Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
error.not_signed_commit=Kein signierter Commit error.not_signed_commit=Kein signierter Commit
error.failed_retrieval_gpg_keys=Fehler beim Abrufen eines Keys des Commiter-Kontos error.failed_retrieval_gpg_keys=Fehler beim Abrufen eines Keys des Commiter-Kontos
[units] [units]
error.no_unit_allowed_repo=Du hast keine Berechtigung auf einen Bereich dieses Repositories 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 auf diesen Repository-Bereich zuzugreifen. error.unit_not_allowed=Du hast keine Berechtigung, um auf diesen Repository-Bereich zuzugreifen.

View File

@ -1167,6 +1167,8 @@ branch.protected_deletion_failed = Branch '%s' is protected. It cannot be delete
topic.manage_topics = Manage Topics topic.manage_topics = Manage Topics
topic.done = Done 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]
org_name_holder = Organization Name org_name_holder = Organization Name

View File

@ -40,6 +40,7 @@ u2f_unsupported_browser=Ваш браузер не підтримує U2F клю
u2f_error_1=Сталася невідома помилка. Спробуйте ще раз. u2f_error_1=Сталася невідома помилка. Спробуйте ще раз.
u2f_error_2=Переконайтеся, що ви використовуєте зашифроване з'єднання (https://) та відвідуєте правильну URL-адресу. u2f_error_2=Переконайтеся, що ви використовуєте зашифроване з'єднання (https://) та відвідуєте правильну URL-адресу.
u2f_error_3=Сервер не може обробити, ваш запит. u2f_error_3=Сервер не може обробити, ваш запит.
u2f_error_5=Таймаут досягнуто до того, як ваш ключ можна буде прочитати. Перезавантажте, щоб повторити спробу.
u2f_reload=Оновити u2f_reload=Оновити
repository=Репозиторій repository=Репозиторій
@ -105,6 +106,7 @@ ssh_port_helper=Номер порту, який використовує SSH с
http_port=Gitea HTTP порт http_port=Gitea HTTP порт
http_port_helper=Номер порту, який буде прослуховуватися Giteas веб-сервером. http_port_helper=Номер порту, який буде прослуховуватися Giteas веб-сервером.
app_url=Базова URL-адреса Gitea app_url=Базова URL-адреса Gitea
app_url_helper=Базова адреса для HTTP(S) клонування через URL та повідомлень електронної пошти.
log_root_path=Шлях до лог файлу log_root_path=Шлях до лог файлу
log_root_path_helper=Файли журналу будуть записані в цей каталог. log_root_path_helper=Файли журналу будуть записані в цей каталог.
@ -112,6 +114,7 @@ optional_title=Додаткові налаштування
email_title=Налаштування Email email_title=Налаштування Email
smtp_host=SMTP хост smtp_host=SMTP хост
smtp_from=Відправляти Email від імені smtp_from=Відправляти Email від імені
smtp_from_helper=Електронна пошта для використання в Gіtea. Введіть звичайну електронну адресу або використовуйте формат: "Ім'я" <email@example.com>.
mailer_user=SMTP Ім'я кристувача mailer_user=SMTP Ім'я кристувача
mailer_password=SMTP Пароль mailer_password=SMTP Пароль
register_confirm=Потрібно підтвердити електронну пошту для реєстрації register_confirm=Потрібно підтвердити електронну пошту для реєстрації
@ -125,9 +128,11 @@ federated_avatar_lookup=Увімкнути федеративні аватари
federated_avatar_lookup_popup=Увімкнути зовнішний Аватар за допомогою Libravatar. federated_avatar_lookup_popup=Увімкнути зовнішний Аватар за допомогою Libravatar.
disable_registration=Вимкнути самостійну реєстрацію disable_registration=Вимкнути самостійну реєстрацію
disable_registration_popup=Вимкнути самостійну реєстрацію користувачів, тільки адміністратор може створювати нові облікові записи. disable_registration_popup=Вимкнути самостійну реєстрацію користувачів, тільки адміністратор може створювати нові облікові записи.
allow_only_external_registration_popup=Включити реєстрацію тільки через зовнішні сервіси.
openid_signin=Увімкнути реєстрацію за допомогою OpenID openid_signin=Увімкнути реєстрацію за допомогою OpenID
openid_signin_popup=Увімкнути вхід за допомогою OpenID. openid_signin_popup=Увімкнути вхід за допомогою OpenID.
openid_signup=Увімкнути самостійну реєстрацію за допомогою OpenID openid_signup=Увімкнути самостійну реєстрацію за допомогою OpenID
openid_signup_popup=Увімкнути самореєстрацію користувачів на основі OpenID.
enable_captcha=Увімкнути CAPTCHA enable_captcha=Увімкнути CAPTCHA
enable_captcha_popup=Вимагати перевірку CAPTCHA при самостійній реєстрації користувача. enable_captcha_popup=Вимагати перевірку CAPTCHA при самостійній реєстрації користувача.
require_sign_in_view=Вимагати авторизації для перегляду сторінок require_sign_in_view=Вимагати авторизації для перегляду сторінок
@ -141,6 +146,7 @@ admin_email=Адреса електронної пошти
install_btn_confirm=Встановлення Gitea install_btn_confirm=Встановлення Gitea
test_git_failed=Не в змозі перевірити 'git' команду: %v test_git_failed=Не в змозі перевірити 'git' команду: %v
invalid_db_setting=Налаштування бази даних є некоректними: %v invalid_db_setting=Налаштування бази даних є некоректними: %v
invalid_repo_path=Помилковий шлях до кореня репозиторію: %v
save_config_failed=Не в змозі зберегти конфігурацію: %v save_config_failed=Не в змозі зберегти конфігурацію: %v
invalid_admin_setting=Неприпустимі налаштування облікового запису адміністратора: %v invalid_admin_setting=Неприпустимі налаштування облікового запису адміністратора: %v
install_success=Ласкаво просимо! Дякуємо вам за вибір Gitea. Розважайтеся, і будьте обережні! install_success=Ласкаво просимо! Дякуємо вам за вибір Gitea. Розважайтеся, і будьте обережні!
@ -152,6 +158,7 @@ default_allow_create_organization_popup=Дозволити новим облік
default_enable_timetracking=Увімкнути відстеження часу за замовчуванням default_enable_timetracking=Увімкнути відстеження часу за замовчуванням
default_enable_timetracking_popup=Включити відстеження часу для нових репозиторіїв за замовчуванням. default_enable_timetracking_popup=Включити відстеження часу для нових репозиторіїв за замовчуванням.
no_reply_address=Прихований поштовий домен no_reply_address=Прихований поштовий домен
no_reply_address_helper=Доменне ім'я для користувачів із прихованою електронною адресою. Наприклад, ім'я користувача 'joe' буде входити в Git як 'joe@noreply.example.org', якщо для прихованого домену електронної пошти встановлено 'noreply.example.org'.
[home] [home]
uname_holder=Ім'я користувача або Ел. пошта uname_holder=Ім'я користувача або Ел. пошта
@ -176,11 +183,13 @@ code=Код
repo_no_results=Відповідних репозиторіїв не знайдено. repo_no_results=Відповідних репозиторіїв не знайдено.
user_no_results=Відповідних користувачів не знайдено. user_no_results=Відповідних користувачів не знайдено.
org_no_results=Відповідних організацій не знайдено. org_no_results=Відповідних організацій не знайдено.
code_no_results=Відповідний пошуковому запитанню код не знайдено.
code_search_results=Результати пошуку '%s' code_search_results=Результати пошуку '%s'
[auth] [auth]
create_new_account=Реєстрація облікового запису create_new_account=Реєстрація облікового запису
register_helper_msg=Вже зареєстровані? Увійдіть зараз! register_helper_msg=Вже зареєстровані? Увійдіть зараз!
social_register_helper_msg=Вже є аккаунт? Зв'яжіть його зараз!
disable_register_prompt=Вибачте, можливість реєстрації відключена. Будь ласка, зв'яжіться з адміністратором сайту. disable_register_prompt=Вибачте, можливість реєстрації відключена. Будь ласка, зв'яжіться з адміністратором сайту.
disable_register_mail=Підтвердження реєстрації електронною поштою вимкнено. disable_register_mail=Підтвердження реєстрації електронною поштою вимкнено.
remember_me=Запам'ятати мене remember_me=Запам'ятати мене
@ -200,18 +209,22 @@ send_reset_mail=Натисніть сюди, щоб відправити лис
reset_password=Скинути пароль reset_password=Скинути пароль
invalid_code=Цей код підтвердження недійсний або закінчився. invalid_code=Цей код підтвердження недійсний або закінчився.
reset_password_helper=Натисніть тут для скидання пароля reset_password_helper=Натисніть тут для скидання пароля
password_too_short=Довжина пароля не може бути меншою за %d символів.
non_local_account=Нелокальні акаунти не можуть змінити пароль через Gitea. non_local_account=Нелокальні акаунти не можуть змінити пароль через Gitea.
verify=Підтвердити verify=Підтвердити
scratch_code=Одноразовий пароль scratch_code=Одноразовий пароль
use_scratch_code=Використовувати одноразовий пароль use_scratch_code=Використовувати одноразовий пароль
twofa_scratch_used=Ви використовували одноразовий пароль. Ви були перенаправлені на сторінку налаштувань для генерації нового коду або відключення двуфакторной аутентифікації. twofa_scratch_used=Ви використовували одноразовий пароль. Ви були перенаправлені на сторінку налаштувань для генерації нового коду або відключення двуфакторної автентифікації.
twofa_passcode_incorrect=Ваш пароль є невірним. Якщо ви втратили пристрій, використовуйте ваш одноразовий пароль. twofa_passcode_incorrect=Ваш пароль є невірним. Якщо ви втратили пристрій, використовуйте ваш одноразовий пароль.
twofa_scratch_token_incorrect=Невірний одноразовий пароль. twofa_scratch_token_incorrect=Невірний одноразовий пароль.
login_userpass=Увійти login_userpass=Увійти
login_openid=OpenID login_openid=OpenID
openid_connect_submit=Під’єднатися openid_connect_submit=Під’єднатися
openid_connect_title=Підключитися до існуючого облікового запису openid_connect_title=Підключитися до існуючого облікового запису
openid_connect_desc=Вибраний OpenID URI невідомий. Пов'яжіть його з новим обліковим записом тут.
openid_register_title=Створити новий обліковий запис 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=На жаль скидання пароля відключене. Будь ласка, зв'яжіться з адміністратором сайту. disable_forgot_password_mail=На жаль скидання пароля відключене. Будь ласка, зв'яжіться з адміністратором сайту.
[mail] [mail]
@ -247,6 +260,8 @@ TreeName=Шлях до файлу
Content=Зміст Content=Зміст
require_error=` не може бути пустим.` require_error=` не може бути пустим.`
alpha_dash_error=` повинен містити тільки літерно-цифрові символи, дефіс ('-') та підкреслення ('_'). `
alpha_dash_dot_error=` повинен містити тільки літерно-цифрові символи, дефіс ('-') , підкреслення ('_') та точки ('.'). `
git_ref_name_error=` повинен бути правильним посилальним ім'ям Git.` git_ref_name_error=` повинен бути правильним посилальним ім'ям Git.`
size_error=` повинен бути розмір %s.` size_error=` повинен бути розмір %s.`
min_size_error=` повинен бути принаймні %s символів.` min_size_error=` повинен бути принаймні %s символів.`
@ -262,6 +277,7 @@ username_been_taken=Ім'я користувача вже зайнято.
repo_name_been_taken=Ім'я репозіторію вже використовується. repo_name_been_taken=Ім'я репозіторію вже використовується.
org_name_been_taken=Назва організації вже зайнято. org_name_been_taken=Назва організації вже зайнято.
team_name_been_taken=Назва команди вже зайнято. team_name_been_taken=Назва команди вже зайнято.
team_no_units_error=Дозволити доступ до принаймні одного розділу репозитарію.
email_been_used=Ця електронна адреса вже використовується. email_been_used=Ця електронна адреса вже використовується.
openid_been_used=OpenID адреса '%s' вже використовується. openid_been_used=OpenID адреса '%s' вже використовується.
username_password_incorrect=Неправильне ім'я користувача або пароль. username_password_incorrect=Неправильне ім'я користувача або пароль.
@ -269,27 +285,31 @@ enterred_invalid_repo_name=Невірно введено ім'я репозит
enterred_invalid_owner_name=Ім'я нового власника не є дійсним. enterred_invalid_owner_name=Ім'я нового власника не є дійсним.
enterred_invalid_password=Введений вами пароль некоректний. enterred_invalid_password=Введений вами пароль некоректний.
user_not_exist=Даний користувач не існує. user_not_exist=Даний користувач не існує.
last_org_owner=Ви не можете вилучити останнього користувача з команди 'власники'. У кожній команді має бути хоча б один власник.
cannot_add_org_to_team=Організацію неможливо додати як учасника команди. cannot_add_org_to_team=Організацію неможливо додати як учасника команди.
invalid_ssh_key=Неможливо перевірити ваш SSH ключ: %s invalid_ssh_key=Неможливо перевірити ваш SSH ключ: %s
invalid_gpg_key=Неможливо перевірити ваш GPG ключ: %s invalid_gpg_key=Неможливо перевірити ваш GPG ключ: %s
unable_verify_ssh_key=Не вдається підтвердити ключ SSH; подвійно перевірте його на наявність похибки.
auth_failed=Помилка автентифікації: %v auth_failed=Помилка автентифікації: %v
still_own_repo=Ваш обліковий запис володіє одним або декількома репозиторіями; видаліть або перенесіть їх в першу чергу.
target_branch_not_exist=Цільової гілки не існує. target_branch_not_exist=Цільової гілки не існує.
[user] [user]
change_avatar=Змінити свій аватар… change_avatar=Змінити свій аватар…
join_on=Приєднався join_on=Приєднався(-лась)
repositories=Репозиторії repositories=Репозиторії
activity=Публічна активність activity=Публічна активність
followers=Підписники followers=Читачі
starred=Обрані Репозиторії starred=Обрані Репозиторії
following=Слідкувати following=Читає
follow=Підписатися follow=Підписатися
unfollow=Відписатися unfollow=Відписатися
form.name_reserved=Ім'я користувача "%s" зарезервовано. form.name_reserved=Ім'я користувача "%s" зарезервовано.
form.name_pattern_not_allowed=Шаблон '%s' не дозволено в імені користувача.
[settings] [settings]
profile=Профіль profile=Профіль
@ -311,6 +331,7 @@ u2f=Ключі безпеки
public_profile=Загальнодоступний профіль public_profile=Загальнодоступний профіль
profile_desc=Ваша адреса електронної пошти використовуватиметься для сповіщення та інших операцій. profile_desc=Ваша адреса електронної пошти використовуватиметься для сповіщення та інших операцій.
password_username_disabled=Нелокальним користувачам заборонено змінювати ім'я користувача. Щоб отримати докладнішу інформацію, зв'яжіться з адміністратором сайту.
full_name=Повне ім'я full_name=Повне ім'я
website=Веб-сайт website=Веб-сайт
location=Місцезнаходження location=Місцезнаходження
@ -340,28 +361,36 @@ password_change_disabled=Нелокальні акаунти не можуть
emails=Адреса електронної пошти emails=Адреса електронної пошти
manage_emails=Керування адресами ел. пошти manage_emails=Керування адресами ел. пошти
manage_openid=Керування OpenID
email_desc=Ваша основна адреса електронної пошти використовуватиметься для сповіщення та інших операцій. email_desc=Ваша основна адреса електронної пошти використовуватиметься для сповіщення та інших операцій.
primary=Основний primary=Основний
primary_email=Зробити основним primary_email=Зробити основним
delete_email=Видалити delete_email=Видалити
email_deletion=Видалити адресу електронної пошти email_deletion=Видалити адресу електронної пошти
openid_deletion=Видалити адресу OpenID openid_deletion=Видалити адресу OpenID
openid_deletion_success=Адреса OpenID була видалена.
add_new_email=Додати нову адресу електронної пошти add_new_email=Додати нову адресу електронної пошти
add_new_openid=Додати новий OpenID URI add_new_openid=Додати новий OpenID URI
add_email=Додати адресу електронної пошти add_email=Додати адресу електронної пошти
add_openid=Додати OpenID URI add_openid=Додати OpenID URI
add_email_confirmation_sent=Електронний лист із підтвердженням було відправлено на '%s', будь ласка, перевірте вашу поштову скриньку протягом наступних %s, щоб підтвердити адресу. add_email_confirmation_sent=Електронний лист із підтвердженням було відправлено на '%s', будь ласка, перевірте вашу поштову скриньку протягом наступних %s, щоб підтвердити адресу.
add_email_success=Додано нову адресу електронної пошти. add_email_success=Додано нову адресу електронної пошти.
add_openid_success=Нова адреса OpenID була додана.
keep_email_private=Приховати адресу електронної пошти keep_email_private=Приховати адресу електронної пошти
keep_email_private_popup=Вашу адресу електронної пошти буде приховано від інших користувачів. keep_email_private_popup=Вашу адресу електронної пошти буде приховано від інших користувачів.
manage_ssh_keys=Керувати SSH ключами manage_ssh_keys=Керувати SSH ключами
manage_gpg_keys=Керувати GPG ключами manage_gpg_keys=Керувати GPG ключами
add_key=Додати ключ add_key=Додати ключ
ssh_desc=Ці відкриті SSH-ключі пов'язані з вашим обліковим записом. Відповідні приватні ключі дозволяють отримати повний доступ до ваших репозиторіїв.
gpg_desc=Ці публічні ключі GPG пов'язані з вашим обліковим записом. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють здійснювати перевірку комітів.
ssh_helper=<strong>Потрібна допомога?</strong> Дивіться гід на GitHub з <a href="%s"> генерації ключів SSH</a> або виправлення <a href="%s">типових неполадок SSH</a>. ssh_helper=<strong>Потрібна допомога?</strong> Дивіться гід на GitHub з <a href="%s"> генерації ключів SSH</a> або виправлення <a href="%s">типових неполадок SSH</a>.
gpg_helper=<strong> Потрібна допомога? </strong> Перегляньте посібник GitHub <a href="%s"> про GPG </a>. gpg_helper=<strong> Потрібна допомога? </strong> Перегляньте посібник GitHub <a href="%s"> про GPG </a>.
add_new_key=Додати SSH ключ add_new_key=Додати SSH ключ
add_new_gpg_key=Додати GPG ключ add_new_gpg_key=Додати GPG ключ
ssh_key_been_used=Цей ключ SSH вже додано до вашого облікового запису.
ssh_key_name_used=Ключ SSH з таким самим ім'ям вже додано до вашого облікового запису.
gpg_key_id_used=Публічний ключ GPG з таким самим ідентифікатором вже існує.
subkeys=Підключі subkeys=Підключі
key_id=ID ключа key_id=ID ключа
key_name=Ім'я ключа key_name=Ім'я ключа
@ -399,20 +428,25 @@ generate_token=Згенерувати токен
delete_token=Видалити delete_token=Видалити
access_token_deletion=Видалити токен доступу access_token_deletion=Видалити токен доступу
twofa_desc=Двофакторна аутентифікація підвищує безпеку вашого облікового запису. twofa_desc=Двофакторна автентифікація підвищує безпеку вашого облікового запису.
twofa_is_enrolled=Ваш обліковий запис на даний час <strong>використовує</strong> двофакторну автентифікацію. twofa_is_enrolled=Ваш обліковий запис на даний час <strong>використовує</strong> двофакторну автентифікацію.
twofa_disable=Вимкнути двофакторну автентифікацію twofa_disable=Вимкнути двофакторну автентифікацію
twofa_scratch_token_regenerate=Перестворити токен одноразового пароля
twofa_enroll=Увімкнути двофакторну автентифікацію twofa_enroll=Увімкнути двофакторну автентифікацію
twofa_disable_note=При необхідності можна відключити двофакторну автентифікацію.
regenerate_scratch_token_desc=Якщо ви втратили свій токен одноразового пароля або вже використовували його для входу, ви можете скинути його тут.
twofa_disabled=Двофакторна автентифікація вимкнена. twofa_disabled=Двофакторна автентифікація вимкнена.
scan_this_image=Проскануйте це зображення вашим додатком для двуфакторної аутентифікації: scan_this_image=Проскануйте це зображення вашим додатком для двуфакторної автентифікації:
or_enter_secret=Або введіть секрет: %s or_enter_secret=Або введіть секрет: %s
passcode_invalid=Некоректний пароль. Спробуй ще раз. passcode_invalid=Некоректний пароль. Спробуй ще раз.
u2f_desc=Ключами безпеки є апаратні пристрої, що містять криптографічні ключі. Вони можуть використовуватися для двофакторної автентифікації. Ключ безпеки повинен підтримувати стандарт <a href="https://fidoalliance.org/">FIDO U2F</a>.
u2f_register_key=Додати ключ безпеки u2f_register_key=Додати ключ безпеки
u2f_nickname=Псевдонім u2f_nickname=Псевдонім
u2f_delete_key=Видалити ключ безпеки u2f_delete_key=Видалити ключ безпеки
manage_account_links=Керування обліковими записами manage_account_links=Керування обліковими записами
manage_account_links_desc=Ці зовнішні акаунти прив'язані до вашого аккаунту Gitea.
remove_account_link=Видалити облікові записи remove_account_link=Видалити облікові записи
orgs_none=Ви не є учасником будь-якої організації. orgs_none=Ви не є учасником будь-якої організації.
@ -428,6 +462,7 @@ owner=Власник
repo_name=Назва репозиторію repo_name=Назва репозиторію
visibility=Видимість visibility=Видимість
visiblity_helper=Зробити репозиторій приватним visiblity_helper=Зробити репозиторій приватним
visiblity_fork_helper=(Зміна цього вплине на всі форки.)
clone_helper=Потрібна допомога у клонуванні? Відвідайте <a target="_blank" rel="noopener" href="%s">Допомогу</a>. clone_helper=Потрібна допомога у клонуванні? Відвідайте <a target="_blank" rel="noopener" href="%s">Допомогу</a>.
fork_repo=Форкнути репозиторій fork_repo=Форкнути репозиторій
fork_from=Форк з fork_from=Форк з
@ -439,6 +474,7 @@ license=Ліцензія
license_helper=Виберіть ліцензійний файл. license_helper=Виберіть ліцензійний файл.
readme=README readme=README
readme_helper=Виберіть шаблон README. readme_helper=Виберіть шаблон README.
auto_init=Ініціалізувати репозиторій (Додає .gitignore, LICENSE та README)
create_repo=Створити репозиторій create_repo=Створити репозиторій
default_branch=Головна гілка default_branch=Головна гілка
mirror_prune=Очистити mirror_prune=Очистити
@ -553,6 +589,7 @@ commits.date=Дата
commits.older=Давніше commits.older=Давніше
commits.newer=Новіше commits.newer=Новіше
commits.signed_by=Підписано commits.signed_by=Підписано
commits.gpg_key_id=Ідентифікатор GPG ключа
ext_issues=Зов. Проблеми ext_issues=Зов. Проблеми
@ -603,7 +640,10 @@ issues.filter_sort.recentupdate=Нещодавно оновлено
issues.filter_sort.leastupdate=Найдавніше оновлені issues.filter_sort.leastupdate=Найдавніше оновлені
issues.filter_sort.mostcomment=Найбільш коментовані issues.filter_sort.mostcomment=Найбільш коментовані
issues.filter_sort.leastcomment=Найменш коментовані issues.filter_sort.leastcomment=Найменш коментовані
issues.filter_sort.moststars=Найбільш обраних
issues.filter_sort.feweststars=Найменш обраних
issues.filter_sort.mostforks=Найбільше форків issues.filter_sort.mostforks=Найбільше форків
issues.filter_sort.fewestforks=Найменше форків
issues.action_open=Відкрити issues.action_open=Відкрити
issues.action_close=Закрити issues.action_close=Закрити
issues.action_label=Мітка issues.action_label=Мітка
@ -721,6 +761,8 @@ milestones.edit=Редагувати етап
milestones.cancel=Відмінити milestones.cancel=Відмінити
milestones.modify=Оновити етап milestones.modify=Оновити етап
milestones.deletion=Видалити етап milestones.deletion=Видалити етап
milestones.filter_sort.closest_due_date=Найближче за датою
milestones.filter_sort.furthest_due_date=Далі за датою
milestones.filter_sort.most_issues=Найбільш проблем milestones.filter_sort.most_issues=Найбільш проблем
milestones.filter_sort.least_issues=Найменш проблем milestones.filter_sort.least_issues=Найменш проблем
@ -968,6 +1010,7 @@ topic.done=Готово
[org] [org]
org_name_holder=Назва організації org_name_holder=Назва організації
org_full_name_holder=Повна назва організації org_full_name_holder=Повна назва організації
org_name_helper=Назва організації має бути простою та зрозумілою.
create_org=Створити організацію create_org=Створити організацію
repo_updated=Оновлено repo_updated=Оновлено
people=Учасники people=Учасники
@ -1096,7 +1139,9 @@ users.admin=Адміністратор
users.repos=Репозиторії users.repos=Репозиторії
users.created=Створено users.created=Створено
users.last_login=Останній вхід users.last_login=Останній вхід
users.never_login=Ніколи не входив
users.send_register_notify=Надіслати повідомлення про реєстрацію користувача users.send_register_notify=Надіслати повідомлення про реєстрацію користувача
users.new_success=Обліковий запис '%s' створений.
users.edit=Редагувати users.edit=Редагувати
users.auth_source=Джерело автентифікації users.auth_source=Джерело автентифікації
users.local=Локальні users.local=Локальні
@ -1130,7 +1175,7 @@ repos.forks=Форки
repos.issues=Проблеми repos.issues=Проблеми
repos.size=Розмір repos.size=Розмір
auths.auth_manage_panel=Керування джерелом аутентифікації auths.auth_manage_panel=Керування джерелом автентифікації
auths.new=Додати джерело автентифікації auths.new=Додати джерело автентифікації
auths.name=Ім'я auths.name=Ім'я
auths.type=Тип auths.type=Тип
@ -1167,8 +1212,8 @@ auths.oauth2_profileURL=URL профілю
auths.oauth2_emailURL=URL електронної пошти auths.oauth2_emailURL=URL електронної пошти
auths.enable_auto_register=Увімкнути автоматичну реєстрацію auths.enable_auto_register=Увімкнути автоматичну реєстрацію
auths.tips=Поради auths.tips=Поради
auths.tips.oauth2.general=OAuth2 аутентифікація auths.tips.oauth2.general=OAuth2 автентифікація
auths.tips.oauth2.general.tip=При додаванні нового OAuth2 провайдера, URL адреса переадресації по завершенні аутентифікації повинена виглядати так:<host>/user/oauth2/<Authentication Name>/callback auths.tips.oauth2.general.tip=При додаванні нового OAuth2 провайдера, URL адреса переадресації по завершенні автентифікації повинена виглядати так:<host>/user/oauth2/<Authentication Name>/callback
auths.tip.oauth2_provider=Постачальник OAuth2 auths.tip.oauth2_provider=Постачальник OAuth2
auths.tip.dropbox=Додайте новий додаток на https://www.dropbox.com/developers/apps auths.tip.dropbox=Додайте новий додаток на https://www.dropbox.com/developers/apps
auths.tip.facebook=Створіть новий додаток на https://developers.facebook.com/apps і додайте модуль "Facebook Login auths.tip.facebook=Створіть новий додаток на https://developers.facebook.com/apps і додайте модуль "Facebook Login
@ -1232,6 +1277,7 @@ config.default_keep_email_private=Приховати адресу електро
config.default_allow_create_organization=Дозволити створення організацій за замовчуванням config.default_allow_create_organization=Дозволити створення організацій за замовчуванням
config.enable_timetracking=Увімкнути відстеження часу config.enable_timetracking=Увімкнути відстеження часу
config.default_enable_timetracking=Увімкнути відстеження часу за замовчуванням config.default_enable_timetracking=Увімкнути відстеження часу за замовчуванням
config.no_reply_address=Прихований домен електронної пошти
config.webhook_config=Конфігурація web-хуків config.webhook_config=Конфігурація web-хуків
config.queue_length=Довжина черги config.queue_length=Довжина черги

View File

@ -213,6 +213,7 @@ send_reset_mail=单击此处(重新)发送您的密码重置邮件
reset_password=重置密码 reset_password=重置密码
invalid_code=此确认密钥无效或已过期。 invalid_code=此确认密钥无效或已过期。
reset_password_helper=单击此处重置密码 reset_password_helper=单击此处重置密码
password_too_short=密码长度不能少于 %d 位。
non_local_account=非本地帐户不能通过 Gitea 的 web 界面更改密码。 non_local_account=非本地帐户不能通过 Gitea 的 web 界面更改密码。
verify=验证 verify=验证
scratch_code=验证口令 scratch_code=验证口令

View File

@ -2336,8 +2336,10 @@ function initTopicbar() {
}).done(function() { }).done(function() {
editDiv.hide(); editDiv.hide();
viewDiv.show(); viewDiv.show();
}).fail(function(xhr) {
alert(xhr.responseJSON.message)
}) })
}) });
$('#topic_edit .dropdown').dropdown({ $('#topic_edit .dropdown').dropdown({
allowAdditions: true, allowAdditions: true,

View File

@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved. // 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -165,7 +166,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
// "$ref": "#/responses/Issue" // "$ref": "#/responses/Issue"
var deadlineUnix util.TimeStamp var deadlineUnix util.TimeStamp
if form.Deadline != nil { if form.Deadline != nil && ctx.Repo.IsWriter() {
deadlineUnix = util.TimeStamp(form.Deadline.Unix()) deadlineUnix = util.TimeStamp(form.Deadline.Unix())
} }
@ -178,15 +179,22 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
DeadlineUnix: deadlineUnix, DeadlineUnix: deadlineUnix,
} }
// Get all assignee IDs var assigneeIDs = make([]int64, 0)
assigneeIDs, err := models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees) var err error
if err != nil { if ctx.Repo.IsWriter() {
if models.IsErrUserNotExist(err) { issue.MilestoneID = form.Milestone
ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err)) assigneeIDs, err = models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
} else { if err != nil {
ctx.Error(500, "AddAssigneeByName", err) 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 { if err := models.NewIssue(ctx.Repo.Repository, issue, form.Labels, assigneeIDs, nil); err != nil {

View File

@ -182,7 +182,14 @@ func NewTeamPost(ctx *context.Context, form auth.CreateTeamForm) {
Authorize: models.ParseAccessMode(form.Permission), Authorize: models.ParseAccessMode(form.Permission),
} }
if t.Authorize < models.AccessModeAdmin { 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 ctx.Data["Team"] = t
@ -264,9 +271,17 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) {
} }
t.Description = form.Description t.Description = form.Description
if t.Authorize < models.AccessModeAdmin { 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 { } else {
t.UnitTypes = nil models.UpdateTeamUnits(t, nil)
} }
if ctx.HasError() { if ctx.HasError() {

View File

@ -12,8 +12,8 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
) )
// TopicPost response for creating repository // TopicsPost response for creating repository
func TopicPost(ctx *context.Context) { func TopicsPost(ctx *context.Context) {
if ctx.User == nil { if ctx.User == nil {
ctx.JSON(403, map[string]interface{}{ ctx.JSON(403, map[string]interface{}{
"message": "Only owners could change the topics.", "message": "Only owners could change the topics.",
@ -27,6 +27,37 @@ func TopicPost(ctx *context.Context) {
topics = strings.Split(topicsStr, ",") 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...) err := models.SaveTopics(ctx.Repo.Repository.ID, topics...)
if err != nil { if err != nil {
log.Error(2, "SaveTopics failed: %v", err) log.Error(2, "SaveTopics failed: %v", err)

View File

@ -210,7 +210,7 @@ func GogsHooksNewPost(ctx *context.Context, form auth.NewGogshookForm) {
Secret: form.Secret, Secret: form.Secret,
HookEvent: ParseHookEvent(form.WebhookForm), HookEvent: ParseHookEvent(form.WebhookForm),
IsActive: form.Active, IsActive: form.Active,
HookTaskType: models.GITEA, HookTaskType: models.GOGS,
OrgID: orCtx.OrgID, OrgID: orCtx.OrgID,
} }
if err := w.UpdateEvent(); err != nil { if err := w.UpdateEvent(); err != nil {

View File

@ -486,7 +486,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/:id", repo.WebHooksEdit) m.Get("/:id", repo.WebHooksEdit)
m.Post("/:id/test", repo.TestWebhook) m.Post("/:id/test", repo.TestWebhook)
m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) 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("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) 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)) }, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeReleases))
m.Group("/:username/:reponame", func() { m.Group("/:username/:reponame", func() {
m.Post("/topics", repo.TopicPost) m.Post("/topics", repo.TopicsPost)
}, context.RepoAssignment(), reqRepoAdmin) }, context.RepoAssignment(), reqRepoAdmin)
m.Group("/:username/:reponame", func() { m.Group("/:username/:reponame", func() {

View File

@ -203,7 +203,11 @@ func Issues(ctx *context.Context) {
return return
} }
} else { } else {
userRepoIDs, err = ctxUser.GetAccessRepoIDs() unitType := models.UnitTypeIssues
if isPullList {
unitType = models.UnitTypePullRequests
}
userRepoIDs, err = ctxUser.GetAccessRepoIDs(unitType)
if err != nil { if err != nil {
ctx.ServerError("ctxUser.GetAccessRepoIDs", err) ctx.ServerError("ctxUser.GetAccessRepoIDs", err)
return return

View File

@ -26,8 +26,8 @@ func TestIssues(t *testing.T) {
Issues(ctx) Issues(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) 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.EqualValues(t, true, ctx.Data["IsShowClosed"])
assert.Len(t, ctx.Data["Issues"], 1) assert.Len(t, ctx.Data["Issues"], 1)
assert.Len(t, ctx.Data["Repos"], 2) assert.Len(t, ctx.Data["Repos"], 1)
} }

View File

@ -24,12 +24,7 @@ func Account(ctx *context.Context) {
ctx.Data["PageIsSettingsAccount"] = true ctx.Data["PageIsSettingsAccount"] = true
ctx.Data["Email"] = ctx.User.Email ctx.Data["Email"] = ctx.User.Email
emails, err := models.GetEmailAddresses(ctx.User.ID) loadAccountData(ctx)
if err != nil {
ctx.ServerError("GetEmailAddresses", err)
return
}
ctx.Data["Emails"] = emails
ctx.HTML(200, tplSettingsAccount) ctx.HTML(200, tplSettingsAccount)
} }
@ -40,6 +35,8 @@ func AccountPost(ctx *context.Context, form auth.ChangePasswordForm) {
ctx.Data["PageIsSettingsAccount"] = true ctx.Data["PageIsSettingsAccount"] = true
if ctx.HasError() { if ctx.HasError() {
loadAccountData(ctx)
ctx.HTML(200, tplSettingsAccount) ctx.HTML(200, tplSettingsAccount)
return return
} }
@ -85,15 +82,9 @@ func EmailPost(ctx *context.Context, form auth.AddEmailForm) {
return 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() { if ctx.HasError() {
loadAccountData(ctx)
ctx.HTML(200, tplSettingsAccount) ctx.HTML(200, tplSettingsAccount)
return return
} }
@ -105,6 +96,8 @@ func EmailPost(ctx *context.Context, form auth.AddEmailForm) {
} }
if err := models.AddEmailAddress(email); err != nil { if err := models.AddEmailAddress(email); err != nil {
if models.IsErrEmailAlreadyUsed(err) { if models.IsErrEmailAlreadyUsed(err) {
loadAccountData(ctx)
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsAccount, &form) ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsAccount, &form)
return return
} }
@ -149,6 +142,8 @@ func DeleteAccount(ctx *context.Context) {
if _, err := models.UserSignIn(ctx.User.Name, ctx.Query("password")); err != nil { if _, err := models.UserSignIn(ctx.User.Name, ctx.Query("password")); err != nil {
if models.IsErrUserNotExist(err) { if models.IsErrUserNotExist(err) {
loadAccountData(ctx)
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_password"), tplSettingsAccount, nil) ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_password"), tplSettingsAccount, nil)
} else { } else {
ctx.ServerError("UserSignIn", err) ctx.ServerError("UserSignIn", err)
@ -172,3 +167,12 @@ func DeleteAccount(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/") 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
}

View File

@ -22,12 +22,7 @@ func Applications(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsApplications"] = true ctx.Data["PageIsSettingsApplications"] = true
tokens, err := models.ListAccessTokens(ctx.User.ID) loadApplicationsData(ctx)
if err != nil {
ctx.ServerError("ListAccessTokens", err)
return
}
ctx.Data["Tokens"] = tokens
ctx.HTML(200, tplSettingsApplications) ctx.HTML(200, tplSettingsApplications)
} }
@ -38,12 +33,8 @@ func ApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) {
ctx.Data["PageIsSettingsApplications"] = true ctx.Data["PageIsSettingsApplications"] = true
if ctx.HasError() { if ctx.HasError() {
tokens, err := models.ListAccessTokens(ctx.User.ID) loadApplicationsData(ctx)
if err != nil {
ctx.ServerError("ListAccessTokens", err)
return
}
ctx.Data["Tokens"] = tokens
ctx.HTML(200, tplSettingsApplications) ctx.HTML(200, tplSettingsApplications)
return return
} }
@ -75,3 +66,12 @@ func DeleteApplication(ctx *context.Context) {
"redirect": setting.AppSubURL + "/user/settings/applications", "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
}

View File

@ -23,19 +23,7 @@ func Keys(ctx *context.Context) {
ctx.Data["PageIsSettingsKeys"] = true ctx.Data["PageIsSettingsKeys"] = true
ctx.Data["DisableSSH"] = setting.SSH.Disabled ctx.Data["DisableSSH"] = setting.SSH.Disabled
keys, err := models.ListPublicKeys(ctx.User.ID) loadKeysData(ctx)
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
ctx.HTML(200, tplSettingsKeys) ctx.HTML(200, tplSettingsKeys)
} }
@ -45,21 +33,9 @@ func KeysPost(ctx *context.Context, form auth.AddKeyForm) {
ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsKeys"] = true 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() { if ctx.HasError() {
loadKeysData(ctx)
ctx.HTML(200, tplSettingsKeys) ctx.HTML(200, tplSettingsKeys)
return 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.Flash.Error(ctx.Tr("form.invalid_gpg_key", err.Error()))
ctx.Redirect(setting.AppSubURL + "/user/settings/keys") ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
case models.IsErrGPGKeyIDAlreadyUsed(err): case models.IsErrGPGKeyIDAlreadyUsed(err):
loadKeysData(ctx)
ctx.Data["Err_Content"] = true ctx.Data["Err_Content"] = true
ctx.RenderWithErr(ctx.Tr("settings.gpg_key_id_used"), tplSettingsKeys, &form) ctx.RenderWithErr(ctx.Tr("settings.gpg_key_id_used"), tplSettingsKeys, &form)
case models.IsErrGPGNoEmailFound(err): case models.IsErrGPGNoEmailFound(err):
loadKeysData(ctx)
ctx.Data["Err_Content"] = true ctx.Data["Err_Content"] = true
ctx.RenderWithErr(ctx.Tr("settings.gpg_no_key_email_found"), tplSettingsKeys, &form) ctx.RenderWithErr(ctx.Tr("settings.gpg_no_key_email_found"), tplSettingsKeys, &form)
default: default:
@ -103,9 +83,13 @@ func KeysPost(ctx *context.Context, form auth.AddKeyForm) {
ctx.Data["HasSSHError"] = true ctx.Data["HasSSHError"] = true
switch { switch {
case models.IsErrKeyAlreadyExist(err): case models.IsErrKeyAlreadyExist(err):
loadKeysData(ctx)
ctx.Data["Err_Content"] = true ctx.Data["Err_Content"] = true
ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplSettingsKeys, &form) ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplSettingsKeys, &form)
case models.IsErrKeyNameAlreadyUsed(err): case models.IsErrKeyNameAlreadyUsed(err):
loadKeysData(ctx)
ctx.Data["Err_Title"] = true ctx.Data["Err_Title"] = true
ctx.RenderWithErr(ctx.Tr("settings.ssh_key_name_used"), tplSettingsKeys, &form) ctx.RenderWithErr(ctx.Tr("settings.ssh_key_name_used"), tplSettingsKeys, &form)
default: default:
@ -147,3 +131,19 @@ func DeleteKey(ctx *context.Context) {
"redirect": setting.AppSubURL + "/user/settings/keys", "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
}

View File

@ -32,6 +32,7 @@ const (
func Profile(ctx *context.Context) { func Profile(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsProfile"] = true ctx.Data["PageIsSettingsProfile"] = true
ctx.HTML(200, tplSettingsProfile) ctx.HTML(200, tplSettingsProfile)
} }

View File

@ -22,6 +22,30 @@ func Security(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsSecurity"] = true 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 enrolled := true
_, err := models.GetTwoFactorByUID(ctx.User.ID) _, err := models.GetTwoFactorByUID(ctx.User.ID)
if err != nil { if err != nil {
@ -71,30 +95,10 @@ func Security(ctx *context.Context) {
} }
ctx.Data["AccountLinks"] = sources ctx.Data["AccountLinks"] = sources
if ctx.Query("openid.return_to") != "" {
settingsOpenIDVerify(ctx)
return
}
openid, err := models.GetUserOpenIDs(ctx.User.ID) openid, err := models.GetUserOpenIDs(ctx.User.ID)
if err != nil { if err != nil {
ctx.ServerError("GetUserOpenIDs", err) ctx.ServerError("GetUserOpenIDs", err)
return return
} }
ctx.Data["OpenIDs"] = openid 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",
})
} }

View File

@ -19,12 +19,8 @@ func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) {
ctx.Data["PageIsSettingsSecurity"] = true ctx.Data["PageIsSettingsSecurity"] = true
if ctx.HasError() { if ctx.HasError() {
openid, err := models.GetUserOpenIDs(ctx.User.ID) loadSecurityData(ctx)
if err != nil {
ctx.ServerError("GetUserOpenIDs", err)
return
}
ctx.Data["OpenIDs"] = openid
ctx.HTML(200, tplSettingsSecurity) ctx.HTML(200, tplSettingsSecurity)
return return
} }
@ -37,6 +33,8 @@ func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) {
id, err := openid.Normalize(form.Openid) id, err := openid.Normalize(form.Openid)
if err != nil { if err != nil {
loadSecurityData(ctx)
ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &form) ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &form)
return return
} }
@ -53,6 +51,8 @@ func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) {
// Check that the OpenID is not already used // Check that the OpenID is not already used
for _, obj := range oids { for _, obj := range oids {
if obj.URI == id { if obj.URI == id {
loadSecurityData(ctx)
ctx.RenderWithErr(ctx.Tr("form.openid_been_used", id), tplSettingsSecurity, &form) ctx.RenderWithErr(ctx.Tr("form.openid_been_used", id), tplSettingsSecurity, &form)
return return
} }
@ -61,6 +61,8 @@ func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) {
redirectTo := setting.AppURL + "user/settings/security" redirectTo := setting.AppURL + "user/settings/security"
url, err := openid.RedirectURL(id, redirectTo, setting.AppURL) url, err := openid.RedirectURL(id, redirectTo, setting.AppURL)
if err != nil { if err != nil {
loadSecurityData(ctx)
ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &form) ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &form)
return return
} }
@ -73,13 +75,6 @@ func settingsOpenIDVerify(ctx *context.Context) {
fullURL := setting.AppURL + ctx.Req.Request.URL.String()[1:] fullURL := setting.AppURL + ctx.Req.Request.URL.String()[1:]
log.Trace("Full URL: " + fullURL) 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) id, err := openid.Verify(fullURL)
if err != nil { if err != nil {
ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &auth.AddOpenIDForm{ ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &auth.AddOpenIDForm{

View File

@ -57,7 +57,7 @@
{{range $t, $unit := $.Units}} {{range $t, $unit := $.Units}}
<div class="field"> <div class="field">
<div class="ui toggle checkbox"> <div class="ui toggle checkbox">
<input type="checkbox" class="hidden" name="units" value="{{$unit.Type}}"{{if $.Team.UnitEnabled $unit.Type}} checked{{end}}> <input type="checkbox" class="hidden" name="units" value="{{$unit.Type}}"{{if or (eq $.Team.ID 0) ($.Team.UnitEnabled $unit.Type)}} checked{{end}}>
<label>{{$.i18n.Tr $unit.NameKey}}</label> <label>{{$.i18n.Tr $unit.NameKey}}</label>
<span class="help">{{$.i18n.Tr $unit.DescKey}}</span> <span class="help">{{$.i18n.Tr $unit.DescKey}}</span>
</div> </div>