Merge branch 'master' into feat/approval-new

# Conflicts:
#	models/issue_comment.go
#	models/migrations/migrations.go
#	models/migrations/v64.go
#	models/models.go
#	public/css/index.css
pull/3748/head
Jonas Franz 7 years ago
commit 6d00c1a7ce
No known key found for this signature in database
GPG Key ID: 506AEEBE80BEDECD
  1. 48
      .drone.yml
  2. 10
      .gitignore
  3. 2
      Makefile
  4. 25
      README.md
  5. 2
      README_ZH.md
  6. 5
      contrib/systemd/gitea.service
  7. 134
      custom/conf/app.ini.sample
  8. 2
      docs/content/doc/advanced/config-cheat-sheet.en-us.md
  9. 611
      docs/content/doc/features/comparison.en-us.md
  10. 41
      docs/content/doc/usage/issue-pull-request-templates.en-us.md
  11. 194
      integrations/auth_ldap_test.go
  12. 16
      models/error.go
  13. 2
      models/fixtures/issue.yml
  14. 8
      models/fixtures/issue_assignees.yml
  15. 3
      models/fixtures/issue_user.yml
  16. 1
      models/fixtures/login_source.yml
  17. 159
      models/issue.go
  18. 263
      models/issue_assignees.go
  19. 71
      models/issue_assignees_test.go
  20. 112
      models/issue_comment.go
  21. 46
      models/issue_list.go
  22. 13
      models/issue_mail.go
  23. 4
      models/issue_milestone.go
  24. 52
      models/issue_user.go
  25. 17
      models/issue_user_test.go
  26. 13
      models/migrations/migrations.go
  27. 12
      models/migrations/v56.go
  28. 126
      models/migrations/v64.go
  29. 1
      models/models.go
  30. 7
      models/pull.go
  31. 6
      models/repo.go
  32. 6
      models/user.go
  33. 6
      models/webhook_dingtalk.go
  34. 6
      models/webhook_discord.go
  35. 6
      models/webhook_slack.go
  36. 1
      modules/auth/repo_form.go
  37. 1
      modules/auth/user_form.go
  38. 3
      modules/context/repo.go
  39. 4
      modules/setting/setting.go
  40. 3
      options/locale/locale_bg-BG.ini
  41. 3
      options/locale/locale_cs-CZ.ini
  42. 152
      options/locale/locale_de-DE.ini
  43. 26
      options/locale/locale_en-US.ini
  44. 3
      options/locale/locale_es-ES.ini
  45. 3
      options/locale/locale_fi-FI.ini
  46. 3
      options/locale/locale_fr-FR.ini
  47. 3
      options/locale/locale_hu-HU.ini
  48. 3
      options/locale/locale_id-ID.ini
  49. 3
      options/locale/locale_it-IT.ini
  50. 3
      options/locale/locale_ja-JP.ini
  51. 3
      options/locale/locale_ko-KR.ini
  52. 3
      options/locale/locale_lv-LV.ini
  53. 3
      options/locale/locale_nl-NL.ini
  54. 3
      options/locale/locale_pl-PL.ini
  55. 23
      options/locale/locale_pt-BR.ini
  56. 3
      options/locale/locale_ru-RU.ini
  57. 3
      options/locale/locale_sr-SP.ini
  58. 3
      options/locale/locale_sv-SE.ini
  59. 3
      options/locale/locale_tr-TR.ini
  60. 243
      options/locale/locale_uk-UA.ini
  61. 639
      options/locale/locale_zh-CN.ini
  62. 3
      options/locale/locale_zh-HK.ini
  63. 3
      options/locale/locale_zh-TW.ini
  64. 665
      package-lock.json
  65. 2
      package.json
  66. 2
      public/css/index.css
  67. 158
      public/js/index.js
  68. 12
      public/less/_base.less
  69. 7
      public/less/_repository.less
  70. 388
      public/swagger.v1.json
  71. 65
      routers/api/v1/repo/issue.go
  72. 2
      routers/api/v1/repo/issue_comment.go
  73. 8
      routers/api/v1/repo/issue_label.go
  74. 73
      routers/api/v1/repo/pull.go
  75. 2
      routers/install.go
  76. 70
      routers/repo/issue.go
  77. 10
      routers/repo/pull.go
  78. 6
      routers/repo/topic.go
  79. 3
      routers/user/auth.go
  80. 66
      snap/helpers/app.ini
  81. 126
      snap/helpers/configuration.sh
  82. 23
      snap/helpers/simple_launcher.sh
  83. 3
      snap/hooks/configure
  84. 45
      snap/hooks/install
  85. 121
      snap/snapcraft.yaml
  86. 2
      templates/admin/config.tmpl
  87. 10
      templates/explore/repo_list.tmpl
  88. 6
      templates/install.tmpl
  89. 2
      templates/repo/header.tmpl
  90. 8
      templates/repo/issue/list.tmpl
  91. 41
      templates/repo/issue/new_form.tmpl
  92. 32
      templates/repo/issue/view_content/comments.tmpl
  93. 43
      templates/repo/issue/view_content/sidebar.tmpl
  94. 2
      vendor/github.com/lafriks/xormstore/xormstore.go
  95. 6
      vendor/vendor.json

@ -1,5 +1,5 @@
workspace:
base: /srv/app
base: /go
path: src/code.gitea.io/gitea
clone:
@ -56,21 +56,18 @@ pipeline:
event: [ push, tag, pull_request ]
build-without-gcc:
image: webhippie/golang:1.8
image: golang:1.8
pull: true
environment:
GOPATH: /srv/app
commands:
- go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
when:
event: [ push, tag, pull_request ]
build:
image: webhippie/golang:edge
image: golang:1.10
pull: true
environment:
TAGS: bindata sqlite
GOPATH: /srv/app
commands:
- make clean
- make generate
@ -85,12 +82,11 @@ pipeline:
event: [ push, tag, pull_request ]
test:
image: webhippie/golang:edge
image: golang:1.10
pull: true
group: test
environment:
TAGS: bindata sqlite
GOPATH: /srv/app
commands:
- make unit-test-coverage
when:
@ -98,12 +94,11 @@ pipeline:
branch: [ master ]
test:
image: webhippie/golang:edge
image: golang:1.10
pull: true
group: test
environment:
TAGS: bindata sqlite
GOPATH: /srv/app
commands:
- make test
when:
@ -111,12 +106,11 @@ pipeline:
branch: [ release/* ]
test:
image: webhippie/golang:edge
image: golang:1.10
pull: true
group: test
environment:
TAGS: bindata
GOPATH: /srv/app
commands:
- make test
when:
@ -124,60 +118,64 @@ pipeline:
# Commented until db locking have been resolved!
# test-sqlite:
# image: webhippie/golang:edge
# image: golang:1.10
# pull: true
# group: test
# environment:
# TAGS: bindata
# GOPATH: /srv/app
# commands:
# - make test-sqlite
# when:
# event: [ push, tag, pull_request ]
test-mysql:
image: webhippie/golang:edge
image: golang:1.10
pull: true
group: test
environment:
TAGS: bindata
GOPATH: /srv/app
TEST_LDAP: "1"
commands:
- curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash
- apt-get install -y git-lfs
- make integration-test-coverage
when:
event: [ push, pull_request ]
branch: [ master ]
test-mysql:
image: webhippie/golang:edge
image: golang:1.10
pull: true
group: test
environment:
TAGS: bindata
GOPATH: /srv/app
TEST_LDAP: "1"
commands:
- curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash
- apt-get install -y git-lfs
- make test-mysql
when:
event: [ tag ]
test-pgsql:
image: webhippie/golang:edge
image: golang:1.10
pull: true
group: test
environment:
TAGS: bindata
GOPATH: /srv/app
TEST_LDAP: "1"
commands:
- curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash
- apt-get install -y git-lfs
- make test-pgsql
when:
event: [ push, tag, pull_request ]
generate-coverage:
image: webhippie/golang:edge
image: golang:1.10
pull: true
environment:
TAGS: bindata
GOPATH: /srv/app
commands:
- make coverage
when:
@ -198,7 +196,6 @@ pipeline:
pull: true
environment:
TAGS: bindata sqlite
GOPATH: /srv/app
commands:
- make release
when:
@ -342,3 +339,8 @@ services:
- POSTGRES_DB=test
when:
event: [ push, tag, pull_request ]
ldap:
image: gitea/test-openldap:latest
when:
event: [ push, tag, pull_request ]

10
.gitignore vendored

@ -59,3 +59,13 @@ coverage.all
/integrations/mysql.ini
/integrations/pgsql.ini
/node_modules
# Snapcraft
snap/.snapcraft/
parts/
stage/
prime/
*.snap
*.snap-build
*_source.tar.bz2

@ -292,7 +292,7 @@ stylesheets-check: generate-stylesheets
.PHONY: generate-stylesheets
generate-stylesheets:
node_modules/.bin/lessc --no-ie-compat --clean-css public/less/index.less public/css/index.css
node_modules/.bin/lessc --clean-css public/less/index.less public/css/index.css
.PHONY: swagger-ui
swagger-ui:

@ -4,12 +4,11 @@
[![Build Status](https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg)](https://drone.gitea.io/go-gitea/gitea)
[![Join the Discord chat at https://discord.gg/NsatcWJ](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/NsatcWJ)
[![Join the Matrix chat at https://matrix.to/#/#gitea:matrix.org](https://img.shields.io/badge/matrix-%23gitea%3Amatrix.org-7bc9a4.svg)](https://matrix.to/#/#gitea:matrix.org)
[![](https://images.microbadger.com/badges/image/gitea/gitea.svg)](https://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
[![codecov](https://codecov.io/gh/go-gitea/gitea/branch/master/graph/badge.svg)](https://codecov.io/gh/go-gitea/gitea)
[![Go Report Card](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea)
[![GoDoc](https://godoc.org/code.gitea.io/gitea?status.svg)](https://godoc.org/code.gitea.io/gitea)
[![Release](https://github-release-version.herokuapp.com/github/go-gitea/gitea/release.svg?style=flat)](https://github.com/go-gitea/gitea/releases/latest)
[![GitHub release](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest)
[![Help Contribute to Open Source](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea)
[![Become a backer/sponsor of gitea](https://opencollective.com/gitea/tiers/backer/badge.svg?label=backer&color=brightgreen)](https://opencollective.com/gitea)
@ -63,7 +62,6 @@ For more information and instructions about how to install Gitea, please look
at our [documentation](https://docs.gitea.io/en-us/). If you have questions
that are not covered by the documentation, you can get in contact with us on
our [Discord server](https://discord.gg/NsatcWJ),
[Matrix room](https://matrix.to/#/#gitea:matrix.org),
or [forum](https://discourse.gitea.io/)!
## Authors
@ -72,6 +70,27 @@ or [forum](https://discourse.gitea.io/)!
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
* [Translators](options/locale/TRANSLATORS)
## Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/gitea#backer)]
<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
## Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/gitea#sponsor)]
<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
## License
This project is licensed under the MIT License.

@ -8,7 +8,7 @@
[![Coverage Status](https://coverage.gitea.io/badges/go-gitea/gitea/coverage.svg)](https://coverage.gitea.io/go-gitea/gitea)
[![Go Report Card](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea)
[![GoDoc](https://godoc.org/code.gitea.io/gitea?status.svg)](https://godoc.org/code.gitea.io/gitea)
[![Release](https://github-release-version.herokuapp.com/github/go-gitea/gitea/release.svg?style=flat)](https://github.com/go-gitea/gitea/releases/latest)
[![GitHub release](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest)
[![Become a backer/sponsor of gitea](https://opencollective.com/gitea/tiers/backer/badge.svg?label=backer&color=brightgreen)](https://opencollective.com/gitea)
| | | |

@ -22,6 +22,11 @@ WorkingDirectory=/home/git/gitea
ExecStart=/home/git/gitea/gitea web
Restart=always
Environment=USER=git HOME=/home/git
# If you want to bind Gitea to a port below 1024 uncomment
# the two values below
###
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
#AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target

@ -2,7 +2,9 @@
; Copy required sections to your own app.ini (default is custom/conf/app.ini)
; and modify as needed.
; App name that shows on every page title
; see https://docs.gitea.io/en-us/config-cheat-sheet/ for additional documentation.
; App name that shows in every page title
APP_NAME = Gitea: Git with a cup of tea
; Change it if you run locally
RUN_USER = git
@ -16,28 +18,28 @@ SCRIPT_TYPE = bash
ANSI_CHARSET =
; Force every new repository to be private
FORCE_PRIVATE = false
; Default private when create a new repository, could be: last, private, public. Default is last which means last user repo visiblity.
; Default privacy setting when creating a new repository, allowed values: last, private, public. Default is last which means the last setting used.
DEFAULT_PRIVATE = last
; Global maximum creation limit of repository per user, -1 means no limit
; Global limit of repositories per user, applied at creation time. -1 means no limit
MAX_CREATION_LIMIT = -1
; Mirror sync queue length, increase if mirror syncing starts hanging
MIRROR_QUEUE_LENGTH = 1000
; Patch test queue length, increase if pull request patch testing starts hanging
PULL_REQUEST_QUEUE_LENGTH = 1000
; Preferred Licenses to place at the top of the List
; Name must match file name in conf/license or custom/conf/license
; The name here must match the filename in conf/license or custom/conf/license
PREFERRED_LICENSES = Apache License 2.0,MIT License
; Disable ability to interact with repositories by HTTP protocol
; Disable the ability to interact with repositories using the HTTP protocol
DISABLE_HTTP_GIT = false
; Force ssh:// clone url instead of scp-style uri when default SSH port is used
USE_COMPAT_SSH_URI = false
[repository.editor]
; List of file extensions that should have line wraps in the CodeMirror editor
; Separate extensions with a comma. To line wrap files w/o extension, just put a comma
; List of file extensions for which lines should be wrapped in the CodeMirror editor
; Separate extensions with a comma. To line wrap files without an extension, just put a comma
LINE_WRAP_EXTENSIONS = .txt,.md,.markdown,.mdown,.mkd,
; Valid file modes that have a preview API associated with them, such as api/v1/markdown
; Separate values by commas. Preview tab in edit mode won't show if the file extension doesn't match
; Separate the values by commas. The preview tab in edit mode won't be displayed if the file extension doesn't match
PREVIEWABLE_FILE_MODES = markdown
[repository.local]
@ -53,39 +55,39 @@ ENABLED = true
TEMP_PATH = data/tmp/uploads
; One or more allowed types, e.g. image/jpeg|image/png. Nothing means any file type
ALLOWED_TYPES =
; Max size of each file in MB. Defaults to 3MB
; Max size of each file in megabytes. Defaults to 3MB
FILE_MAX_SIZE = 3
; Max number of files per upload. Defaults to 5
MAX_FILES = 5
[ui]
; Number of repositories that are showed in one explore page
; Number of repositories that are displayed on one explore page
EXPLORE_PAGING_NUM = 20
; Number of issues that are showed in one page
; Number of issues that are displayed on one page
ISSUE_PAGING_NUM = 10
; Number of maximum commits showed in one activity feed
; Number of maximum commits displayed in one activity feed
FEED_MAX_COMMIT_NUM = 5
; Value of `theme-color` meta tag, used by Android >= 5.0
; An invalid color like "none" or "disable" will have the default style
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
THEME_COLOR_META_TAG = `#6cc644`
; Max size of files to be displayed (defaults is 8MiB)
; Max size of files to be displayed (default is 8MiB)
MAX_DISPLAY_FILE_SIZE = 8388608
; Whether show the user email in the Explore Users page
; Whether the email of the user should be shown in the Explore Users page
SHOW_USER_EMAIL = true
[ui.admin]
; Number of users that are showed in one page
; Number of users that are displayed on one page
USER_PAGING_NUM = 50
; Number of repos that are showed in one page
; Number of repos that are displayed on one page
REPO_PAGING_NUM = 50
; Number of notices that are showed in one page
; Number of notices that are displayed on in one page
NOTICE_PAGING_NUM = 25
; Number of organization that are showed in one page
; Number of organizations that are displayed on one page
ORG_PAGING_NUM = 50
[ui.user]
; Number of repos that are showed in one page
; Number of repos that are displayed on one page
REPO_PAGING_NUM = 15
[ui.meta]
@ -100,19 +102,19 @@ ENABLE_HARD_LINE_BREAK = false
; for example git,magnet
CUSTOM_URL_SCHEMES =
; List of file extensions that should be rendered/edited as Markdown
; Separate extensions with a comma. To render files w/o extension as markdown, just put a comma
; Separate the extensions with a comma. To render files without any extension as markdown, just put a comma
FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
[server]
; Listen protocol. One of 'http', 'https', 'unix' or 'fcgi'.
; The protocol the server listens on. One of 'http', 'https', 'unix' or 'fcgi'.
PROTOCOL = http
DOMAIN = localhost
ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
; Listen address. Either a IPv4/IPv6 address or the path to a unix socket.
; The address to listen on. Either a IPv4/IPv6 address or the path to a unix socket.
HTTP_ADDR = 0.0.0.0
HTTP_PORT = 3000
; If REDIRECT_OTHER_PORT is true, and PROTOCOL is set to https an http server
; will be started on PORT_TO_REDIRECT and redirect request to the main
; will be started on PORT_TO_REDIRECT and it will redirect plain, non-secure http requests to the main
; ROOT_URL. Defaults are false for REDIRECT_OTHER_PORT and 80 for
; PORT_TO_REDIRECT.
REDIRECT_OTHER_PORT = false
@ -125,33 +127,33 @@ UNIX_SOCKET_PERMISSION = 666
LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
; Disable SSH feature when not available
DISABLE_SSH = false
; Whether use builtin SSH server or not.
; Whether to use the builtin SSH server or not.
START_SSH_SERVER = false
; Username to use for builtin SSH server. If blank, then it is the value of RUN_USER.
; Username to use for the builtin SSH server. If blank, then it is the value of RUN_USER.
BUILTIN_SSH_SERVER_USER =
; Domain name to be exposed in clone URL
SSH_DOMAIN = %(DOMAIN)s
; Network interface builtin SSH server listens on
; THe network interface the builtin SSH server should listen on
SSH_LISTEN_HOST =
; Port number to be exposed in clone URL
SSH_PORT = 22
; Port number builtin SSH server listens on
; The port number the builtin SSH server should listen on
SSH_LISTEN_PORT = %(SSH_PORT)s
; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
SSH_ROOT_PATH =
; For built-in SSH server only, choose the ciphers to support for SSH connections,
; For the built-in SSH server, choose the ciphers to support for SSH connections,
; for system SSH this setting has no effect
SSH_SERVER_CIPHERS = aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, arcfour256, arcfour128
; For built-in SSH server only, choose the key exchange algorithms to support for SSH connections,
; For the built-in SSH server, choose the key exchange algorithms to support for SSH connections,
; for system SSH this setting has no effect
SSH_SERVER_KEY_EXCHANGES = diffie-hellman-group1-sha1, diffie-hellman-group14-sha1, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, curve25519-sha256@libssh.org
; For built-in SSH server only, choose the MACs to support for SSH connections,
; For the built-in SSH server, choose the MACs to support for SSH connections,
; for system SSH this setting has no effect
SSH_SERVER_MACS = hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1, hmac-sha1-96
; Directory to create temporary files when test public key using ssh-keygen,
; default is system temporary directory.
; Directory to create temporary files in when testing public keys using ssh-keygen,
; default is the system temporary directory.
SSH_KEY_TEST_PATH =
; Path to ssh-keygen, default is 'ssh-keygen' and let shell find out which one to call.
; Path to ssh-keygen, default is 'ssh-keygen' which means the shell is responsible for finding out which one to call.
SSH_KEYGEN_PATH = ssh-keygen
; Enable SSH Authorized Key Backup when rewriting all keys, default is true
SSH_BACKUP_AUTHORIZED_KEYS = true
@ -171,7 +173,7 @@ DISABLE_ROUTER_LOG = false
; $ openssl pkcs12 -in cert.pfx -out key.pem -nocerts -nodes
CERT_FILE = custom/https/cert.pem
KEY_FILE = custom/https/key.pem
; Upper level of template and static file path
; Root directory containing templates and static files.
; default is the path where Gitea is executed
STATIC_ROOT_PATH =
; Default path for App data
@ -182,9 +184,9 @@ ENABLE_GZIP = false
LANDING_PAGE = home
; Enables git-lfs support. true or false, default is false.
LFS_START_SERVER = false
; Where your lfs files put on, default is data/lfs.
; Where your lfs files reside, default is data/lfs.
LFS_CONTENT_PATH = data/lfs
; LFS authentication secret, changed this to yourself.
; LFS authentication secret, change this yourself
LFS_JWT_SECRET =
; Define allowed algorithms and their minimum key length (use -1 to disable a type)
@ -204,7 +206,7 @@ USER = root
PASSWD =
; For "postgres" only, either "disable", "require" or "verify-full"
SSL_MODE = disable
; For "sqlite3" and "tidb", use absolute path when you start as service
; For "sqlite3" and "tidb", use absolute path when you start gitea as service
PATH = data/gitea.db
; For "sqlite3" only. Query timeout
SQLITE_TIMEOUT = 500
@ -222,7 +224,7 @@ UPDATE_BUFFER_LEN = 20
MAX_FILE_SIZE = 1048576
[admin]
; Disable regular (non-admin) users to create organizations
; Disallow regular (non-admin) users from creating organizations.
DISABLE_REGULAR_ORG_CREATION = false
[security]
@ -230,13 +232,13 @@ DISABLE_REGULAR_ORG_CREATION = false
INSTALL_LOCK = false
; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!!
SECRET_KEY = !#@FDEWREWR&*(
; Auto-login remember days
; How long to remember that an user is logged in before requiring relogin (in days)
LOGIN_REMEMBER_DAYS = 7
COOKIE_USERNAME = gitea_awesome
COOKIE_REMEMBER_NAME = gitea_incredible
; Reverse proxy authentication header name of user name
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
; Sets the minimum password length for new Users
; The minimum password length for new Users
MIN_PASSWORD_LENGTH = 6
; True when users are allowed to import local server paths
IMPORT_LOCAL_PATHS = false
@ -245,7 +247,7 @@ DISABLE_GIT_HOOKS = false
[openid]
;
; OpenID is an open standard and decentralized authentication protocol.
; OpenID is an open, standard and decentralized authentication protocol.
; Your identity is the address of a webpage you provide, which describes
; how to prove you are in control of that page.
;
@ -264,7 +266,7 @@ DISABLE_GIT_HOOKS = false
; Whether to allow signin in via OpenID
ENABLE_OPENID_SIGNIN = true
; Whether to allow registering via OpenID
; Do not include to rely on DISABLE_REGISTRATION setting
; Do not include to rely on rhw DISABLE_REGISTRATION setting
;ENABLE_OPENID_SIGNUP = true
; Allowed URI patterns (POSIX regexp).
; Space separated.
@ -280,12 +282,14 @@ BLACKLISTED_URIS =
[service]
; Time limit to confirm account/email registration
ACTIVE_CODE_LIVE_MINUTES = 180
; Time limit to confirm forgot password reset process
; Time limit to perform the reset of a forgotten password
RESET_PASSWD_CODE_LIVE_MINUTES = 180
; User need to confirm e-mail for registration
; Whether a new user needs to confirm their email when registering.
REGISTER_EMAIL_CONFIRM = false
; Does not allow register and admin create account only
; Disallow registration, only allow admins to create accounts.
DISABLE_REGISTRATION = false
; Allow registration only using third part services, it works only when DISABLE_REGISTRATION is false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
; User must sign in to view anything.
REQUIRE_SIGNIN_VIEW = false
; Mail notification
@ -296,10 +300,10 @@ ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
; Enable captcha validation for registration
ENABLE_CAPTCHA = true
; Default value for KeepEmailPrivate
; New user will get the value of this setting copied into their profile
; Each new user will get the value of this setting copied into their profile
DEFAULT_KEEP_EMAIL_PRIVATE = false
; Default value for AllowCreateOrganization
; New user will have rights set to create organizations depending on this setting
; Every new user will have rights set to create organizations depending on this setting
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
; Enable Timetracking
ENABLE_TIMETRACKING = true
@ -307,10 +311,10 @@ ENABLE_TIMETRACKING = true
; Repositories will use timetracking by default depending on this setting
DEFAULT_ENABLE_TIMETRACKING = true
; Default value for AllowOnlyContributorsToTrackTime
; Only users with write permissions could track time if this is true
; Only users with write permissions can track time if this is true
DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME = true
; Default value for the domain part of the user's email address in the git log
; if he has set KeepEmailPrivate true. The user's email replaced with a
; if he has set KeepEmailPrivate to true. The user's email will be replaced with a
; concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
NO_REPLY_ADDRESS = noreply.example.org
@ -335,9 +339,9 @@ SUBJECT = %(APP_NAME)s
; QQ: smtp.qq.com:465
; Note, if the port ends with "465", SMTPS will be used. Using STARTTLS on port 587 is recommended per RFC 6409. If the server supports STARTTLS it will always be used.
HOST =
; Disable HELO operation when hostname are different.
; Disable HELO operation when hostnames are different.
DISABLE_HELO =
; Custom hostname for HELO operation, default is from system.
; Custom hostname for HELO operation, if no value is provided, one is retrieved from system.
HELO_HOSTNAME =
; Do not verify the certificate of the server. Only use this for self-signed certificates
SKIP_VERIFY =
@ -377,7 +381,7 @@ ITEM_TTL = 16h
; Either "memory", "file", or "redis", default is "memory"
PROVIDER = memory
; Provider config options
; memory: not have any config yet
; memory: doesn't have any config yet
; file: session file path, e.g. `data/sessions`
; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table`
@ -398,11 +402,11 @@ AVATAR_UPLOAD_PATH = data/avatars
; Chinese users can choose "duoshuo"
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
GRAVATAR_SOURCE = gravatar
; This value will be forced to be true in offline mode.
; This value will always be true in offline mode.
DISABLE_GRAVATAR = false
; Federated avatar lookup uses DNS to discover avatar associated
; with emails, see https://www.libravatar.org
; This value will be forced to be false in offline mode or Gravatar is disabled.
; This value will always be false in offline mode or when Gravatar is disabled.
ENABLE_FEDERATED_AVATAR = false
[attachment]
@ -412,9 +416,9 @@ ENABLE = true
PATH = data/attachments
; One or more allowed types, e.g. image/jpeg|image/png
ALLOWED_TYPES = image/jpeg|image/png|application/zip|application/gzip
; Max size of each file. Defaults to 32MB
; Max size of each file. Defaults to 4MB
MAX_SIZE = 4
; Max number of files per upload. Defaults to 10
; Max number of files per upload. Defaults to 5
MAX_FILES = 5
[time]
@ -428,7 +432,7 @@ ROOT_PATH =
; Either "console", "file", "conn", "smtp" or "database", default is "console"
; Use comma to separate multiple modes, e.g. "console, file"
MODE = console
; Buffer length of channel, keep it as it is if you don't know what it is.
; Buffer length of the channel, keep it as it is if you don't know what it is.
BUFFER_LEN = 10000
; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
LEVEL = Trace
@ -442,13 +446,13 @@ LEVEL =
LEVEL =
; This enables automated log rotate(switch of following options), default is true
LOG_ROTATE = true
; Max line number of single file, default is 1000000
; Max number of lines in a single file, default is 1000000
MAX_LINES = 1000000
; Max size shift of single file, default is 28 means 1 << 28, 256MB
; Max size shift of a single file, default is 28 means 1 << 28, 256MB
MAX_SIZE_SHIFT = 28
; Segment log daily, default is true
DAILY_ROTATE = true
; Expired days of log file(delete after max days), default is 7
; delete the log file after n days, default is 7
MAX_DAYS = 7
; For "conn" mode only
@ -532,9 +536,9 @@ UPDATE_EXISTING = true
[git]
; Disables highlight of added and removed changes
DISABLE_DIFF_HIGHLIGHT = false
; Max number of lines allowed of a single file in diff view
; Max number of lines allowed in a single file in diff view
MAX_GIT_DIFF_LINES = 1000
; Max number of characters of a line allowed in diff view
; Max number of allowed characters in a line in diff view
MAX_GIT_DIFF_LINE_CHARACTERS = 5000
; Max number of files shown in diff view
MAX_GIT_DIFF_FILES = 100
@ -559,7 +563,7 @@ MIN_INTERVAL = 10m
[api]
; Enables /api/swagger, /api/v1/swagger etc. endpoints. True or false; default is true.
ENABLE_SWAGGER_ENDPOINT = true
; Max number of items will response in a page
; Max number of items in a page
MAX_RESPONSE_ITEMS = 50
[i18n]
@ -598,7 +602,7 @@ ko-KR = ko
SHOW_FOOTER_BRANDING = false
; Show version information about Gitea and Go in the footer
SHOW_FOOTER_VERSION = true
; Show time of template execution in the footer
; Show template execution time in the footer
SHOW_FOOTER_TEMPLATE_LOAD_TIME = true
[markup.asciidoc]
@ -607,5 +611,5 @@ ENABLED = false
FILE_EXTENSIONS = .adoc,.asciidoc
; External command to render all matching extensions
RENDER_COMMAND = "asciidoc --out-file=- -"
; Input is not a standard input but a file
; Don't pass the file on STDIN, pass the filename as argument instead.
IS_INPUT_FILE = false

@ -201,8 +201,6 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
## Cache (`cache`)
- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, or `memcache`.
- To use `redis` or `memcache`, be sure to rebuild everything with build tags `redis` or
`memcache`: `go build -tags='redis'`.
- `INTERVAL`: **60**: Garbage Collection interval (sec), for memory cache only.
- `HOST`: **\<empty\>**: Connection string for `redis` and `memcache`.
- Redis: `network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180`

@ -0,0 +1,611 @@
---
date: "2018-05-07T13:00:00+02:00"
title: "Gitea compared to other Git hosting options"
slug: "comparison"
weight: 5
toc: true
draft: false
menu:
sidebar:
parent: "features"
name: "Comparison"
weight: 5
identifier: "comparison"
---
# Gitea compared to other Git hosting options
To help decide if Gitea is suited for your needs here is how it compares to other Git self hosted options.
Be warned that we don't regularly check for feature changes in other products so this list can be outdated. If you find anything that needs to be updated in table below please report [issue on Github](https://github.com/go-gitea/gitea/issues).
_Symbols used in table:_
* _✓ - supported_
* _⁄ - supported with limited functionality_
* _✘ - unsupported_
<table border="1" cellpadding="4">
<thead>
<tr>
<td>Feature</td>
<td>Gitea</td>
<td>Gogs</td>
<td>GitHub EE</td>
<td>GitLab CE</td>
<td>GitLab EE</td>
<td>BitBucket</td>
</tr>
</thead>
<tbody>
<tr>
<td>Open source and free</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Issue tracker</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Pull/Merge requests</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Squash merging</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Rebase merging</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Pull/Merge request inline comments</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Pull/Merge request approval</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Merge conflict resolution</td>
<td></td>
<td></td>
<td></td>
<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>
</tr>
<tr>
<td>Markdown support</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>
</tr>
<tr>
<td>Revert specific commits or a merge request</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>
</tr>
<tr>
<td>Time tracking</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>
</tr>
<tr>
<td>Related issues</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>
</tr>
<tr>
<td>Comment reactions</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>
</tr>
<tr>
<td>Batch issue handling</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>
</tr>
<tr>
<td>Create new branches from issues</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>
</tr>
<tr>
<td>Web code editor</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>
</tr>
<tr>
<td>Create new branches</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>
</tr>
<tr>
<td>Repository code search</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>
</tr>
<tr>
<td>Issue search</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>
</tr>
<tr>
<td>Git LFS 2.0</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>
</tr>
<tr>
<td>Static Git-powered pages</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>
</tr>
<tr>
<td>Granular user roles (Code, Issues, Wiki etc)</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>
</tr>
<tr>
<td>GPG Signed Commits</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>
</tr>
<tr>
<td>Verified Committer</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>
</tr>
<tr>
<td>Custom Git Hooks</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>
</tr>
<tr>
<td>Deploy Tokens</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>
</tr>
<tr>
<td>Easy upgrade process</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>
</tr>
<tr>
<td>External git mirroring</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>
</tr>
<tr>
<td>Multiple LDAP / AD server support</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>
</tr>
<tr>
<td>OpenId Connect support</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>
</tr>
<tr>
<td>Act as OAuth 2.0 provider</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>
</tr>
<tr>
<td>Webhook support</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>
</tr>
<tr>
<td>Discord integration</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>
</tr>
<tr>
<td>External CI/CD status display</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>
</tr>
<tr>
<td>Multiple OS support</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>
</tr>
</tbody>
</table>

@ -0,0 +1,41 @@
---
date: "2018-05-10T16:00:00+02:00"
title: "Usage: Issue and Pull Request templates"
slug: "issue-pull-request-templates"
weight: 15
toc: true
draft: false
menu:
sidebar:
parent: "usage"
name: "Issue and Pull Request templates"
weight: 15
identifier: "issue-pull-request-templates"
---
# Issue and Pull Request Templates
For some projects there are a standard list of questions that users need to be asked
for creating an issue, or adding a pull request. Gitea supports adding templates to the
main branch of the repository so that they can autopopulate the form when users are
creating issues, and pull requests. This will cut down on the initial back and forth
of getting some clarifiying details.
Possible file names for issue templates:
* ISSUE_TEMPLATE.md
* issue_template.md
* .gitea/ISSUE_TEMPLATE.md
* .gitea/issue_template.md
* .github/ISSUE_TEMPLATE.md
* .github/issue_template.md
Possible file names for PR templates:
* PULL_REQUEST_TEMPLATE.md
* pull_request_template.md
* .gitea/PULL_REQUEST_TEMPLATE.md
* .gitea/pull_request_template.md
* .github/PULL_REQUEST_TEMPLATE.md
* .github/pull_request_template.md

@ -0,0 +1,194 @@
// 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 integrations
import (
"net/http"
"os"
"strings"
"testing"
"code.gitea.io/gitea/models"
"github.com/Unknwon/i18n"
"github.com/stretchr/testify/assert"
)
type ldapUser struct {
UserName string
Password string
FullName string
Email string
OtherEmails []string
IsAdmin bool
SSHKeys []string
}
var gitLDAPUsers = []ldapUser{
{
UserName: "professor",
Password: "professor",
FullName: "Hubert Farnsworth",
Email: "professor@planetexpress.com",
OtherEmails: []string{"hubert@planetexpress.com"},
IsAdmin: true,
},
{
UserName: "hermes",
Password: "hermes",
FullName: "Conrad Hermes",
Email: "hermes@planetexpress.com",
IsAdmin: true,
},
{
UserName: "fry",
Password: "fry",
FullName: "Philip Fry",
Email: "fry@planetexpress.com",
},
{
UserName: "leela",
Password: "leela",
FullName: "Leela Turanga",
Email: "leela@planetexpress.com",
},
{
UserName: "bender",
Password: "bender",
FullName: "Bender Rodríguez",
Email: "bender@planetexpress.com",
},
}
var otherLDAPUsers = []ldapUser{
{
UserName: "zoidberg",
Password: "zoidberg",
FullName: "John Zoidberg",
Email: "zoidberg@planetexpress.com",
},
{
UserName: "amy",
Password: "amy",
FullName: "Amy Kroker",
Email: "amy@planetexpress.com",
},
}
func skipLDAPTests() bool {
return os.Getenv("TEST_LDAP") != "1"
}
func getLDAPServerHost() string {
host := os.Getenv("TEST_LDAP_HOST")
if len(host) == 0 {
host = "ldap"
}
return host
}
func addAuthSourceLDAP(t *testing.T) {
session := loginUser(t, "user1")
csrf := GetCSRF(t, session, "/admin/auths/new")
req := NewRequestWithValues(t, "POST", "/admin/auths/new", map[string]string{
"_csrf": csrf,
"type": "2",
"name": "ldap",
"host": getLDAPServerHost(),
"port": "389",
"bind_dn": "uid=gitea,ou=service,dc=planetexpress,dc=com",
"bind_password": "password",
"user_base": "ou=people,dc=planetexpress,dc=com",
"filter": "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))",
"admin_filter": "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)",
"attribute_username": "uid",
"attribute_name": "givenName",
"attribute_surname": "sn",
"attribute_mail": "mail",
"is_sync_enabled": "on",
"is_active": "on",
})
session.MakeRequest(t, req, http.StatusFound)
}
func TestLDAPUserSignin(t *testing.T) {
if skipLDAPTests() {
t.Skip()
return
}
prepareTestEnv(t)
addAuthSourceLDAP(t)
u := gitLDAPUsers[0]
session := loginUserWithPassword(t, u.UserName, u.Password)
req := NewRequest(t, "GET", "/user/settings")
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Equal(t, u.UserName, htmlDoc.GetInputValueByName("name"))
assert.Equal(t, u.FullName, htmlDoc.GetInputValueByName("full_name"))
assert.Equal(t, u.Email, htmlDoc.GetInputValueByName("email"))
}
func TestLDAPUserSync(t *testing.T) {
if skipLDAPTests() {
t.Skip()
return
}
prepareTestEnv(t)
addAuthSourceLDAP(t)
models.SyncExternalUsers()
session := loginUser(t, "user1")
// Check if users exists
for _, u := range gitLDAPUsers {
req := NewRequest(t, "GET", "/admin/users?q="+u.UserName)
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
tr := htmlDoc.doc.Find("table.table tbody tr")
if !assert.True(t, tr.Length() == 1) {
continue
}
tds := tr.Find("td")
if !assert.True(t, tds.Length() > 0) {
continue
}
assert.Equal(t, u.UserName, strings.TrimSpace(tds.Find("td:nth-child(2) a").Text()))
assert.Equal(t, u.Email, strings.TrimSpace(tds.Find("td:nth-child(3) span").Text()))
if u.IsAdmin {
assert.True(t, tds.Find("td:nth-child(5) i").HasClass("fa-check-square-o"))
} else {
assert.True(t, tds.Find("td:nth-child(5) i").HasClass("fa-square-o"))
}
}
// Check if no users exist
for _, u := range otherLDAPUsers {
req := NewRequest(t, "GET", "/admin/users?q="+u.UserName)
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
tr := htmlDoc.doc.Find("table.table tbody tr")
assert.True(t, tr.Length() == 0)
}
}
func TestLDAPUserSigninFailed(t *testing.T) {
if skipLDAPTests() {
t.Skip()
return
}
prepareTestEnv(t)
addAuthSourceLDAP(t)
u := otherLDAPUsers[0]
testLoginFailed(t, u.UserName, u.Password, i18n.Tr("en", "form.username_password_incorrect"))
}

@ -733,6 +733,22 @@ func (err ErrRepoFileAlreadyExist) Error() string {
return fmt.Sprintf("repository file already exists [file_name: %s]", err.FileName)
}
// ErrUserDoesNotHaveAccessToRepo represets an error where the user doesn't has access to a given repo
type ErrUserDoesNotHaveAccessToRepo struct {
UserID int64
RepoName string
}
// IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrRepoFileAlreadyExist.
func IsErrUserDoesNotHaveAccessToRepo(err error) bool {
_, ok := err.(ErrUserDoesNotHaveAccessToRepo)
return ok
}
func (err ErrUserDoesNotHaveAccessToRepo) Error() string {
return fmt.Sprintf("user doesn't have acces to repo [user_id: %d, repo_name: %s]", err.UserID, err.RepoName)
}
// __________ .__
// \______ \____________ ____ ____ | |__
// | | _/\_ __ \__ \ / \_/ ___\| | \

@ -3,7 +3,6 @@
repo_id: 1
index: 1
poster_id: 1
assignee_id: 1
name: issue1
content: content for the first issue
is_closed: false
@ -67,7 +66,6 @@
repo_id: 3
index: 1
poster_id: 1
assignee_id: 1
name: issue6
content: content6
is_closed: false

@ -0,0 +1,8 @@
-
id: 1
assignee_id: 1
issue_id: 1
-
id: 2
assignee_id: 1
issue_id: 6

@ -3,7 +3,6 @@
uid: 1
issue_id: 1
is_read: true
is_assigned: true
is_mentioned: false
-
@ -11,7 +10,6 @@
uid: 2
issue_id: 1
is_read: true
is_assigned: false
is_mentioned: false
-
@ -19,5 +17,4 @@
uid: 4
issue_id: 1
is_read: false
is_assigned: false
is_mentioned: false

@ -37,7 +37,7 @@ type Issue struct {
MilestoneID int64 `xorm:"INDEX"`
Milestone *Milestone `xorm:"-"`
Priority int
AssigneeID int64 `xorm:"INDEX"`
AssigneeID int64 `xorm:"-"`
Assignee *User `xorm:"-"`
IsClosed bool `xorm:"INDEX"`
IsRead bool `xorm:"-"`
@ -56,6 +56,7 @@ type Issue struct {
Comments []*Comment `xorm:"-"`
Reactions ReactionList `xorm:"-"`
TotalTrackedTime int64 `xorm:"-"`
Assignees []*User `xorm:"-"`
}
var (
@ -140,22 +141,6 @@ func (issue *Issue) loadPoster(e Engine) (err error) {
return
}
func (issue *Issue) loadAssignee(e Engine) (err error) {
if issue.Assignee == nil && issue.AssigneeID > 0 {
issue.Assignee, err = getUserByID(e, issue.AssigneeID)
if err != nil {
issue.AssigneeID = -1
issue.Assignee = NewGhostUser()
if !IsErrUserNotExist(err) {
return fmt.Errorf("getUserByID.(assignee) [%d]: %v", issue.AssigneeID, err)
}
err = nil
return
}
}
return
}
func (issue *Issue) loadPullRequest(e Engine) (err error) {
if issue.IsPull && issue.PullRequest == nil {
issue.PullRequest, err = getPullRequestByIssueID(e, issue.ID)
@ -231,7 +216,7 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
}
}
if err = issue.loadAssignee(e); err != nil {
if err = issue.loadAssignees(e); err != nil {
return
}
@ -343,8 +328,11 @@ func (issue *Issue) APIFormat() *api.Issue {
if issue.Milestone != nil {
apiIssue.Milestone = issue.Milestone.APIFormat()
}
if issue.Assignee != nil {
apiIssue.Assignee = issue.Assignee.APIFormat()
if len(issue.Assignees) > 0 {
for _, assignee := range issue.Assignees {
apiIssue.Assignees = append(apiIssue.Assignees, assignee.APIFormat())
}
apiIssue.Assignee = issue.Assignees[0].APIFormat() // For compatibility, we're keeping the first assignee as `apiIssue.Assignee`
}
if issue.IsPull {
apiIssue.PullRequest = &api.PullRequestMeta{
@ -605,19 +593,6 @@ func (issue *Issue) ReplaceLabels(labels []*Label, doer *User) (err error) {
return sess.Commit()
}
// GetAssignee sets the Assignee attribute of this issue.
func (issue *Issue) GetAssignee() (err error) {
if issue.AssigneeID == 0 || issue.Assignee != nil {
return nil
}
issue.Assignee, err = GetUserByID(issue.AssigneeID)
if IsErrUserNotExist(err) {
return nil
}
return err
}
// ReadBy sets issue to be read by given user.
func (issue *Issue) ReadBy(userID int64) error {
if err := UpdateIssueUserByRead(userID, issue.ID); err != nil {
@ -823,55 +798,6 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
return nil
}
// ChangeAssignee changes the Assignee field of this issue.
func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
var oldAssigneeID = issue.AssigneeID
issue.AssigneeID = assigneeID
if err = UpdateIssueUserByAssignee(issue); err != nil {
return fmt.Errorf("UpdateIssueUserByAssignee: %v", err)
}
sess := x.NewSession()
defer sess.Close()
if err = issue.loadRepo(sess); err != nil {
return fmt.Errorf("loadRepo: %v", err)
}
if _, err = createAssigneeComment(sess, doer, issue.Repo, issue, oldAssigneeID, assigneeID); err != nil {
return fmt.Errorf("createAssigneeComment: %v", err)
}
issue.Assignee, err = GetUserByID(issue.AssigneeID)
if err != nil && !IsErrUserNotExist(err) {
log.Error(4, "GetUserByID [assignee_id: %v]: %v", issue.AssigneeID, err)
return nil
}
// Error not nil here means user does not exist, which is remove assignee.
isRemoveAssignee := err != nil
if issue.IsPull {
issue.PullRequest.Issue = issue
apiPullRequest := &api.PullRequestPayload{
Index: issue.Index,
PullRequest: issue.PullRequest.APIFormat(),
Repository: issue.Repo.APIFormat(AccessModeNone),
Sender: doer.APIFormat(),
}
if isRemoveAssignee {
apiPullRequest.Action = api.HookIssueUnassigned
} else {
apiPullRequest.Action = api.HookIssueAssigned
}
if err := PrepareWebhooks(issue.Repo, HookEventPullRequest, apiPullRequest); err != nil {
log.Error(4, "PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, isRemoveAssignee, err)
return nil
}
}
go HookQueue.Add(issue.RepoID)
return nil
}
// GetTasks returns the amount of tasks in the issues content
func (issue *Issue) GetTasks() int {
return len(issueTasksPat.FindAllStringIndex(issue.Content, -1))
@ -887,6 +813,7 @@ type NewIssueOptions struct {
Repo *Repository
Issue *Issue
LabelIDs []int64
AssigneeIDs []int64
Attachments []string // In UUID format.
IsPull bool
}
@ -909,14 +836,32 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
}
}
if assigneeID := opts.Issue.AssigneeID; assigneeID > 0 {
valid, err := hasAccess(e, assigneeID, opts.Repo, AccessModeWrite)
if err != nil {
return fmt.Errorf("hasAccess [user_id: %d, repo_id: %d]: %v", assigneeID, opts.Repo.ID, err)
// Keep the old assignee id thingy for compatibility reasons
if opts.Issue.AssigneeID > 0 {
isAdded := false
// Check if the user has already been passed to issue.AssigneeIDs, if not, add it
for _, aID := range opts.AssigneeIDs {
if aID == opts.Issue.AssigneeID {
isAdded = true
break
}
}
if !valid {
opts.Issue.AssigneeID = 0
opts.Issue.Assignee = nil
if !isAdded {
opts.AssigneeIDs = append(opts.AssigneeIDs, opts.Issue.AssigneeID)
}
}
// Check for and validate assignees
if len(opts.AssigneeIDs) > 0 {
for _, assigneeID := range opts.AssigneeIDs {
valid, err := hasAccess(e, assigneeID, opts.Repo, AccessModeWrite)
if err != nil {
return fmt.Errorf("hasAccess [user_id: %d, repo_id: %d]: %v", assigneeID, opts.Repo.ID, err)
}
if !valid {
return ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: opts.Repo.Name}
}
}
}
@ -931,11 +876,10 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
}
}
if opts.Issue.AssigneeID > 0 {
if err = opts.Issue.loadRepo(e); err != nil {
return err
}
if _, err = createAssigneeComment(e, doer, opts.Issue.Repo, opts.Issue, -1, opts.Issue.AssigneeID); err != nil {
// Insert the assignees
for _, assigneeID := range opts.AssigneeIDs {
err = opts.Issue.changeAssignee(e, doer, assigneeID)
if err != nil {
return err
}
}
@ -995,7 +939,7 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
}
// NewIssue creates new issue with labels for repository.
func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []int64, uuids []string) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
@ -1007,7 +951,11 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string)
Issue: issue,
LabelIDs: labelIDs,
Attachments: uuids,
AssigneeIDs: assigneeIDs,
}); err != nil {
if IsErrUserDoesNotHaveAccessToRepo(err) {
return err
}
return fmt.Errorf("newIssue: %v", err)
}
@ -1150,7 +1098,8 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) error {
}
if opts.AssigneeID > 0 {
sess.And("issue.assignee_id=?", opts.AssigneeID)
sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
And("issue_assignees.assignee_id = ?", opts.AssigneeID)
}
if opts.PosterID > 0 {
@ -1372,7 +1321,8 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
}
if opts.AssigneeID > 0 {
sess.And("issue.assignee_id = ?", opts.AssigneeID)
sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
And("issue_assignees.assignee_id = ?", opts.AssigneeID)
}
if opts.PosterID > 0 {
@ -1438,13 +1388,15 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
}
case FilterModeAssign:
stats.OpenCount, err = x.Where(cond).And("is_closed = ?", false).
And("assignee_id = ?", opts.UserID).
Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
And("issue_assignees.assignee_id = ?", opts.UserID).
Count(new(Issue))
if err != nil {
return nil, err
}
stats.ClosedCount, err = x.Where(cond).And("is_closed = ?", true).
And("assignee_id = ?", opts.UserID).
Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
And("issue_assignees.assignee_id = ?", opts.UserID).
Count(new(Issue))
if err != nil {
return nil, err
@ -1466,7 +1418,8 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
cond = cond.And(builder.Eq{"issue.is_closed": opts.IsClosed})
stats.AssignCount, err = x.Where(cond).
And("assignee_id = ?", opts.UserID).
Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
And("issue_assignees.assignee_id = ?", opts.UserID).
Count(new(Issue))
if err != nil {
return nil, err
@ -1505,8 +1458,10 @@ func GetRepoIssueStats(repoID, uid int64, filterMode int, isPull bool) (numOpen
switch filterMode {
case FilterModeAssign:
openCountSession.And("assignee_id = ?", uid)
closedCountSession.And("assignee_id = ?", uid)
openCountSession.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
And("issue_assignees.assignee_id = ?", uid)
closedCountSession.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
And("issue_assignees.assignee_id = ?", uid)
case FilterModeCreate:
openCountSession.And("poster_id = ?", uid)
closedCountSession.And("poster_id = ?", uid)

@ -0,0 +1,263 @@
// 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 models
import (
"fmt"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/sdk/gitea"
"github.com/go-xorm/xorm"
)
// IssueAssignees saves all issue assignees
type IssueAssignees struct {
ID int64 `xorm:"pk autoincr"`
AssigneeID int64 `xorm:"INDEX"`
IssueID int64 `xorm:"INDEX"`
}
// This loads all assignees of an issue
func (issue *Issue) loadAssignees(e Engine) (err error) {
// Reset maybe preexisting assignees
issue.Assignees = []*User{}
err = e.Table("`user`").
Join("INNER", "issue_assignees", "assignee_id = `user`.id").
Where("issue_assignees.issue_id = ?", issue.ID).
Find(&issue.Assignees)
if err != nil {
return err
}
// Check if we have at least one assignee and if yes put it in as `Assignee`
if len(issue.Assignees) > 0 {
issue.Assignee = issue.Assignees[0]
}
return
}
// GetAssigneesByIssue returns everyone assigned to that issue
func GetAssigneesByIssue(issue *Issue) (assignees []*User, err error) {
err = issue.loadAssignees(x)
if err != nil {
return assignees, err
}
return issue.Assignees, nil
}
// IsUserAssignedToIssue returns true when the user is assigned to the issue
func IsUserAssignedToIssue(issue *Issue, user *User) (isAssigned bool, err error) {
isAssigned, err = x.Exist(&IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID})
return
}
// DeleteNotPassedAssignee deletes all assignees who aren't passed via the "assignees" array
func DeleteNotPassedAssignee(issue *Issue, doer *User, assignees []*User) (err error) {
var found bool
for _, assignee := range issue.Assignees {
found = false
for _, alreadyAssignee := range assignees {
if assignee.ID == alreadyAssignee.ID {
found = true
break
}
}
if !found {
// This function also does comments and hooks, which is why we call it seperatly instead of directly removing the assignees here
if err := UpdateAssignee(issue, doer, assignee.ID); err != nil {
return err
}
}
}
return nil
}
// MakeAssigneeList concats a string with all names of the assignees. Useful for logs.
func MakeAssigneeList(issue *Issue) (assigneeList string, err error) {
err = issue.loadAssignees(x)
if err != nil {
return "", err
}
for in, assignee := range issue.Assignees {
assigneeList += assignee.Name
if len(issue.Assignees) > (in + 1) {
assigneeList += ", "
}
}
return
}
// ClearAssigneeByUserID deletes all assignments of an user
func clearAssigneeByUserID(sess *xorm.Session, userID int64) (err error) {
_, err = sess.Delete(&IssueAssignees{AssigneeID: userID})
return
}
// AddAssigneeIfNotAssigned adds an assignee only if he isn't aleady assigned to the issue
func AddAssigneeIfNotAssigned(issue *Issue, doer *User, assigneeID int64) (err error) {
// Check if the user is already assigned
isAssigned, err := IsUserAssignedToIssue(issue, &User{ID: assigneeID})
if err != nil {
return err
}
if !isAssigned {
return issue.ChangeAssignee(doer, assigneeID)
}
return nil
}
// UpdateAssignee deletes or adds an assignee to an issue
func UpdateAssignee(issue *Issue, doer *User, assigneeID int64) (err error) {
return issue.ChangeAssignee(doer, assigneeID)
}
// ChangeAssignee changes the Assignee of this issue.
func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if err := issue.changeAssignee(sess, doer, assigneeID); err != nil {
return err
}
return sess.Commit()
}
func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID int64) (err error) {
// Update the assignee
removed, err := updateIssueAssignee(sess, issue, assigneeID)
if err != nil {
return fmt.Errorf("UpdateIssueUserByAssignee: %v", err)
}
// Repo infos
if err = issue.loadRepo(sess); err != nil {
return fmt.Errorf("loadRepo: %v", err)
}
// Comment
if _, err = createAssigneeComment(sess, doer, issue.Repo, issue, assigneeID, removed); err != nil {
return fmt.Errorf("createAssigneeComment: %v", err)
}
if issue.IsPull {
issue.PullRequest = &PullRequest{Issue: issue}
apiPullRequest := &api.PullRequestPayload{
Index: issue.Index,
PullRequest: issue.PullRequest.APIFormat(),
Repository: issue.Repo.APIFormat(AccessModeNone),
Sender: doer.APIFormat(),
}
if removed {
apiPullRequest.Action = api.HookIssueUnassigned
} else {
apiPullRequest.Action = api.HookIssueAssigned
}
if err := PrepareWebhooks(issue.Repo, HookEventPullRequest, apiPullRequest); err != nil {
log.Error(4, "PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
return nil
}
}
go HookQueue.Add(issue.RepoID)
return nil
}
// UpdateAPIAssignee is a helper function to add or delete one or multiple issue assignee(s)
// Deleting is done the Github way (quote from their api documentation):
// https://developer.github.com/v3/issues/#edit-an-issue
// "assignees" (array): Logins for Users to assign to this issue.
// Pass one or more user logins to replace the set of assignees on this Issue.
// Send an empty array ([]) to clear all assignees from the Issue.
func UpdateAPIAssignee(issue *Issue, oneAssignee string, multipleAssignees []string, doer *User) (err error) {
var allNewAssignees []*User
// Keep the old assignee thingy for compatibility reasons
if oneAssignee != "" {
// Prevent double adding assignees
var isDouble bool
for _, assignee := range multipleAssignees {
if assignee == oneAssignee {
isDouble = true
break
}
}
if !isDouble {
multipleAssignees = append(multipleAssignees, oneAssignee)
}
}
// Loop through all assignees to add them
for _, assigneeName := range multipleAssignees {
assignee, err := GetUserByName(assigneeName)
if err != nil {
return err
}
allNewAssignees = append(allNewAssignees, assignee)
}
// Delete all old assignees not passed
if err = DeleteNotPassedAssignee(issue, doer, allNewAssignees); err != nil {
return err
}
// Add all new assignees
// Update the assignee. The function will check if the user exists, is already
// assigned (which he shouldn't as we deleted all assignees before) and
// has access to the repo.
for _, assignee := range allNewAssignees {
// Extra method to prevent double adding (which would result in removing)
err = AddAssigneeIfNotAssigned(issue, doer, assignee.ID)
if err != nil {
return err
}
}
return
}
// MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs
func MakeIDsFromAPIAssigneesToAdd(oneAssignee string, multipleAssignees []string) (assigneeIDs []int64, err error) {
// Keeping the old assigning method for compatibility reasons
if oneAssignee != "" {
// Prevent double adding assignees
var isDouble bool
for _, assignee := range multipleAssignees {
if assignee == oneAssignee {
isDouble = true
break
}
}
if !isDouble {
multipleAssignees = append(multipleAssignees, oneAssignee)
}
}
// Get the IDs of all assignees
assigneeIDs = GetUserIDsByNames(multipleAssignees)
return
}

@ -0,0 +1,71 @@
// 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 models
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestUpdateAssignee(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
// Fake issue with assignees
issue, err := GetIssueByID(1)
assert.NoError(t, err)
// Assign multiple users
user2, err := GetUserByID(2)
assert.NoError(t, err)
err = UpdateAssignee(issue, &User{ID: 1}, user2.ID)
assert.NoError(t, err)
user3, err := GetUserByID(3)
assert.NoError(t, err)
err = UpdateAssignee(issue, &User{ID: 1}, user3.ID)
assert.NoError(t, err)
user1, err := GetUserByID(1) // This user is already assigned (see the definition in fixtures), so running UpdateAssignee should unassign him
assert.NoError(t, err)
err = UpdateAssignee(issue, &User{ID: 1}, user1.ID)
assert.NoError(t, err)
// Check if he got removed
isAssigned, err := IsUserAssignedToIssue(issue, user1)
assert.NoError(t, err)
assert.False(t, isAssigned)
// Check if they're all there
assignees, err := GetAssigneesByIssue(issue)
assert.NoError(t, err)
var expectedAssignees []*User
expectedAssignees = append(expectedAssignees, user2)
expectedAssignees = append(expectedAssignees, user3)
for in, assignee := range assignees {
assert.Equal(t, assignee.ID, expectedAssignees[in].ID)
}
// Check if the user is assigned
isAssigned, err = IsUserAssignedToIssue(issue, user2)
assert.NoError(t, err)
assert.True(t, isAssigned)
// This user should not be assigned
isAssigned, err = IsUserAssignedToIssue(issue, &User{ID: 4})
assert.NoError(t, err)
assert.False(t, isAssigned)
// Clean everyone
err = DeleteNotPassedAssignee(issue, user1, []*User{})
assert.NoError(t, err)
// Check they're gone
assignees, err = GetAssigneesByIssue(issue)
assert.NoError(t, err)
assert.Equal(t, 0, len(assignees))
}

@ -88,23 +88,22 @@ const (
// Comment represents a comment in commit and issue page.
type Comment struct {
ID int64 `xorm:"pk autoincr"`
Type CommentType
PosterID int64 `xorm:"INDEX"`
Poster *User `xorm:"-"`
IssueID int64 `xorm:"INDEX"`
LabelID int64
Label *Label `xorm:"-"`
OldMilestoneID int64
MilestoneID int64
OldMilestone *Milestone `xorm:"-"`
Milestone *Milestone `xorm:"-"`
OldAssigneeID int64
AssigneeID int64
Assignee *User `xorm:"-"`
OldAssignee *User `xorm:"-"`
OldTitle string
NewTitle string
ID int64 `xorm:"pk autoincr"`
Type CommentType
PosterID int64 `xorm:"INDEX"`
Poster *User `xorm:"-"`
IssueID int64 `xorm:"INDEX"`
LabelID int64
Label *Label `xorm:"-"`
OldMilestoneID int64
MilestoneID int64
OldMilestone *Milestone `xorm:"-"`
Milestone *Milestone `xorm:"-"`
AssigneeID int64
RemovedAssignee bool
Assignee *User `xorm:"-"`
OldTitle string
NewTitle string
CommitID int64
Line int64 // - previous line / + proposed line
@ -259,18 +258,9 @@ func (c *Comment) LoadMilestone() error {
return nil
}
// LoadAssignees if comment.Type is CommentTypeAssignees, then load assignees
func (c *Comment) LoadAssignees() error {
// LoadAssigneeUser if comment.Type is CommentTypeAssignees, then load assignees
func (c *Comment) LoadAssigneeUser() error {
var err error
if c.OldAssigneeID > 0 {
c.OldAssignee, err = getUserByID(x, c.OldAssigneeID)
if err != nil {
if !IsErrUserNotExist(err) {
return err
}
c.OldAssignee = NewGhostUser()
}
}
if c.AssigneeID > 0 {
c.Assignee, err = getUserByID(x, c.AssigneeID)
@ -382,21 +372,21 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
LabelID = opts.Label.ID
}
comment := &Comment{
Type: opts.Type,
PosterID: opts.Doer.ID,
Poster: opts.Doer,
IssueID: opts.Issue.ID,
LabelID: LabelID,
OldMilestoneID: opts.OldMilestoneID,
MilestoneID: opts.MilestoneID,
OldAssigneeID: opts.OldAssigneeID,
AssigneeID: opts.AssigneeID,
CommitID: opts.CommitID,
CommitSHA: opts.CommitSHA,
Line: opts.LineNum,
Content: opts.Content,
OldTitle: opts.OldTitle,
NewTitle: opts.NewTitle,
Type: opts.Type,
PosterID: opts.Doer.ID,
Poster: opts.Doer,
IssueID: opts.Issue.ID,
LabelID: LabelID,
OldMilestoneID: opts.OldMilestoneID,
MilestoneID: opts.MilestoneID,
RemovedAssignee: opts.RemovedAssignee,
AssigneeID: opts.AssigneeID,
CommitID: opts.CommitID,
CommitSHA: opts.CommitSHA,
Line: opts.LineNum,
Content: opts.Content,
OldTitle: opts.OldTitle,
NewTitle: opts.NewTitle,
TreePath: opts.TreePath,
ReviewID: opts.ReviewID,
}
@ -540,14 +530,14 @@ func createMilestoneComment(e *xorm.Session, doer *User, repo *Repository, issue
})
}
func createAssigneeComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue, oldAssigneeID, assigneeID int64) (*Comment, error) {
func createAssigneeComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue, assigneeID int64, removedAssignee bool) (*Comment, error) {
return createComment(e, &CreateCommentOptions{
Type: CommentTypeAssignees,
Doer: doer,
Repo: repo,
Issue: issue,
OldAssigneeID: oldAssigneeID,
AssigneeID: assigneeID,
Type: CommentTypeAssignees,
Doer: doer,
Repo: repo,
Issue: issue,
RemovedAssignee: removedAssignee,
AssigneeID: assigneeID,
})
}
@ -608,19 +598,19 @@ type CreateCommentOptions struct {
Issue *Issue
Label *Label
OldMilestoneID int64
MilestoneID int64
OldAssigneeID int64
AssigneeID int64
OldTitle string
NewTitle string
CommitID int64
CommitSHA string
LineNum int64
OldMilestoneID int64
MilestoneID int64
AssigneeID int64
RemovedAssignee bool
OldTitle string
NewTitle string
CommitID int64
CommitSHA string
LineNum int64
TreePath string
ReviewID int64
Content string
Attachments []string // UUIDs of attachments
Content string
Attachments []string // UUIDs of attachments
}
// CreateComment creates comment of issue or commit.

@ -154,38 +154,38 @@ func (issues IssueList) loadMilestones(e Engine) error {
return nil
}
func (issues IssueList) getAssigneeIDs() []int64 {
var ids = make(map[int64]struct{}, len(issues))
for _, issue := range issues {
if _, ok := ids[issue.AssigneeID]; !ok {
ids[issue.AssigneeID] = struct{}{}
}
}
return keysInt64(ids)
}
func (issues IssueList) loadAssignees(e Engine) error {
assigneeIDs := issues.getAssigneeIDs()
if len(assigneeIDs) == 0 {
if len(issues) == 0 {
return nil
}
assigneeMaps := make(map[int64]*User, len(assigneeIDs))
err := e.
In("id", assigneeIDs).
Find(&assigneeMaps)
type AssigneeIssue struct {
IssueAssignee *IssueAssignees `xorm:"extends"`
Assignee *User `xorm:"extends"`
}
var assignees = make(map[int64][]*User, len(issues))
rows, err := e.Table("issue_assignees").
Join("INNER", "user", "`user`.id = `issue_assignees`.assignee_id").
In("`issue_assignees`.issue_id", issues.getIssueIDs()).
Rows(new(AssigneeIssue))
if err != nil {
return err
}
defer rows.Close()
for _, issue := range issues {
if issue.AssigneeID <= 0 {
continue
}
var ok bool
if issue.Assignee, ok = assigneeMaps[issue.AssigneeID]; !ok {
issue.Assignee = NewGhostUser()
for rows.Next() {
var assigneeIssue AssigneeIssue
err = rows.Scan(&assigneeIssue)
if err != nil {
return err
}
assignees[assigneeIssue.IssueAssignee.IssueID] = append(assignees[assigneeIssue.IssueAssignee.IssueID], assigneeIssue.Assignee)
}
for _, issue := range issues {
issue.Assignees = assignees[issue.ID]
}
return nil
}

@ -46,9 +46,16 @@ func mailIssueCommentToParticipants(e Engine, issue *Issue, doer *User, content
participants = append(participants, issue.Poster)
}
// Assignee must receive any communications
if issue.Assignee != nil && issue.AssigneeID > 0 && issue.AssigneeID != doer.ID {
participants = append(participants, issue.Assignee)
// Assignees must receive any communications
assignees, err := GetAssigneesByIssue(issue)
if err != nil {
return err
}
for _, assignee := range assignees {
if assignee.ID != doer.ID {
participants = append(participants, assignee)
}
}
tos := make([]string, 0, len(watchers)) // List of email addresses.

@ -24,7 +24,7 @@ type Milestone struct {
NumClosedIssues int
NumOpenIssues int `xorm:"-"`
Completeness int // Percentage(1-100).
IsOverDue bool `xorm:"-"`
IsOverdue bool `xorm:"-"`
DeadlineString string `xorm:"-"`
DeadlineUnix util.TimeStamp
@ -52,7 +52,7 @@ func (m *Milestone) AfterLoad() {
m.DeadlineString = m.DeadlineUnix.Format("2006-01-02")
if util.TimeStampNow() >= m.DeadlineUnix {
m.IsOverDue = true
m.IsOverdue = true
}
}

@ -6,6 +6,8 @@ package models
import (
"fmt"
"github.com/go-xorm/xorm"
)
// IssueUser represents an issue-user relation.
@ -14,7 +16,6 @@ type IssueUser struct {
UID int64 `xorm:"INDEX"` // User ID.
IssueID int64
IsRead bool
IsAssigned bool
IsMentioned bool
}
@ -32,9 +33,8 @@ func newIssueUsers(e Engine, repo *Repository, issue *Issue) error {
issueUsers := make([]*IssueUser, 0, len(assignees)+1)
for _, assignee := range assignees {
issueUsers = append(issueUsers, &IssueUser{
IssueID: issue.ID,
UID: assignee.ID,
IsAssigned: assignee.ID == issue.AssigneeID,
IssueID: issue.ID,
UID: assignee.ID,
})
isPosterAssignee = isPosterAssignee || assignee.ID == issue.PosterID
}
@ -51,34 +51,38 @@ func newIssueUsers(e Engine, repo *Repository, issue *Issue) error {
return nil
}
func updateIssueUserByAssignee(e Engine, issue *Issue) (err error) {
if _, err = e.Exec("UPDATE `issue_user` SET is_assigned = ? WHERE issue_id = ?", false, issue.ID); err != nil {
return err
func updateIssueAssignee(e *xorm.Session, issue *Issue, assigneeID int64) (removed bool, err error) {
// Check if the user exists
_, err = GetUserByID(assigneeID)
if err != nil {
return false, err
}
// Assignee ID equals to 0 means clear assignee.
if issue.AssigneeID > 0 {
if _, err = e.Exec("UPDATE `issue_user` SET is_assigned = ? WHERE uid = ? AND issue_id = ?", true, issue.AssigneeID, issue.ID); err != nil {
return err
// Check if the submitted user is already assigne, if yes delete him otherwise add him
var toBeDeleted bool
for _, assignee := range issue.Assignees {
if assignee.ID == assigneeID {
toBeDeleted = true
break
}
}
return updateIssue(e, issue)
}
assigneeIn := IssueAssignees{AssigneeID: assigneeID, IssueID: issue.ID}
// UpdateIssueUserByAssignee updates issue-user relation for assignee.
func UpdateIssueUserByAssignee(issue *Issue) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = updateIssueUserByAssignee(sess, issue); err != nil {
return err
if toBeDeleted {
_, err = e.Delete(assigneeIn)
if err != nil {
return toBeDeleted, err
}
} else {
_, err = e.Insert(assigneeIn)
if err != nil {
return toBeDeleted, err
}
}
return sess.Commit()
return toBeDeleted, nil
}
// UpdateIssueUserByRead updates issue-user relation for reading.

@ -32,23 +32,6 @@ func Test_newIssueUsers(t *testing.T) {
AssertExistsAndLoadBean(t, &IssueUser{IssueID: newIssue.ID, UID: repo.OwnerID})
}
func TestUpdateIssueUserByAssignee(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
issue := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
// artificially change assignee in issue_user table
AssertSuccessfulInsert(t, &IssueUser{IssueID: issue.ID, UID: 5, IsAssigned: true})
_, err := x.Cols("is_assigned").
Update(&IssueUser{IsAssigned: false}, &IssueUser{IssueID: issue.ID, UID: issue.AssigneeID})
assert.NoError(t, err)
assert.NoError(t, UpdateIssueUserByAssignee(issue))
// issue_user table should now be correct again
AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: issue.AssigneeID}, "is_assigned=1")
AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: 5}, "is_assigned=0")
}
func TestUpdateIssueUserByRead(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
issue := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)

@ -181,6 +181,8 @@ var migrations = []Migration{
// v63 -> v64
NewMigration("add language column for user setting", addLanguageSetting),
// v64 -> v65
NewMigration("add multiple assignees", addMultipleAssignees),
// v65 -> v66
NewMigration("add review", addReview),
}
@ -231,7 +233,7 @@ Please try to upgrade to a lower version (>= v0.6.0) first, then upgrade to curr
return nil
}
func dropTableColumns(x *xorm.Engine, tableName string, columnNames ...string) (err error) {
func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...string) (err error) {
if tableName == "" || len(columnNames) == 0 {
return nil
}
@ -247,17 +249,10 @@ func dropTableColumns(x *xorm.Engine, tableName string, columnNames ...string) (
}
cols += "DROP COLUMN `" + col + "`"
}
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err)
}
case setting.UseMSSQL:
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
cols := ""
for _, col := range columnNames {
if cols != "" {

@ -9,5 +9,15 @@ import (
)
func removeIsOwnerColumnFromOrgUser(x *xorm.Engine) (err error) {
return dropTableColumns(x, "org_user", "is_owner", "num_teams")
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err := dropTableColumns(sess, "org_user", "is_owner", "num_teams"); err != nil {
return err
}
return sess.Commit()
}

@ -5,27 +5,127 @@
package migrations
import (
"fmt"
"code.gitea.io/gitea/modules/util"
"github.com/go-xorm/xorm"
)
func addReview(x *xorm.Engine) error {
// Review see models/review.go
type Review struct {
ID int64 `xorm:"pk autoincr"`
Type string
ReviewerID int64 `xorm:"index"`
IssueID int64 `xorm:"index"`
Content string
func addMultipleAssignees(x *xorm.Engine) error {
// Redeclare issue struct
type Issue struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"`
Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository.
PosterID int64 `xorm:"INDEX"`
Title string `xorm:"name"`
Content string `xorm:"TEXT"`
MilestoneID int64 `xorm:"INDEX"`
Priority int
AssigneeID int64 `xorm:"INDEX"`
IsClosed bool `xorm:"INDEX"`
IsPull bool `xorm:"INDEX"` // Indicates whether is a pull request or not.
NumComments int
Ref string
DeadlineUnix util.TimeStamp `xorm:"INDEX"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
ClosedUnix util.TimeStamp `xorm:"INDEX"`
}
// Updated the comment table
type Comment struct {
ID int64 `xorm:"pk autoincr"`
Type int
PosterID int64 `xorm:"INDEX"`
IssueID int64 `xorm:"INDEX"`
LabelID int64
OldMilestoneID int64
MilestoneID int64
OldAssigneeID int64
AssigneeID int64
RemovedAssignee bool
OldTitle string
NewTitle string
CommitID int64
Line int64
Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
// Reference issue in commit message
CommitSHA string `xorm:"VARCHAR(40)"`
}
// Create the table
type IssueAssignees struct {
ID int64 `xorm:"pk autoincr"`
AssigneeID int64 `xorm:"INDEX"`
IssueID int64 `xorm:"INDEX"`
}
if err := x.Sync2(IssueAssignees{}); err != nil {
return err
}
if err := x.Sync2(Comment{}); err != nil {
return err
}
// Range over all issues and insert a new entry for each issue/assignee
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
allIssues := []Issue{}
if err := sess.Find(&allIssues); err != nil {
return err
}
for _, issue := range allIssues {
if issue.AssigneeID != 0 {
_, err := sess.Insert(IssueAssignees{IssueID: issue.ID, AssigneeID: issue.AssigneeID})
if err != nil {
sess.Rollback()
return err
}
}
}
// Migrate comments
// First update everything to not have nulls in db
if _, err := sess.Where("type = ?", 9).Cols("removed_assignee").Update(Comment{RemovedAssignee: false}); err != nil {
return err
}
allAssignementComments := []Comment{}
if err := sess.Where("type = ?", 9).Find(&allAssignementComments); err != nil {
return err
}
for _, comment := range allAssignementComments {
// Everytime where OldAssigneeID is > 0, the assignement was removed.
if comment.OldAssigneeID > 0 {
_, err := sess.ID(comment.ID).Update(Comment{RemovedAssignee: true})
if err != nil {
return err
}
}
}
if err := dropTableColumns(sess, "issue", "assignee_id"); err != nil {
return err
}
if err := x.Sync2(new(Review)); err != nil {
return fmt.Errorf("Sync2: %v", err)
if err := dropTableColumns(sess, "issue_user", "is_assigned"); err != nil {
return err
}
return nil
return sess.Commit()
}

@ -119,6 +119,7 @@ func init() {
new(RepoIndexerStatus),
new(LFSLock),
new(Reaction),
new(IssueAssignees),
new(Review),
)

@ -198,6 +198,7 @@ func (pr *PullRequest) APIFormat() *api.PullRequest {
Labels: apiIssue.Labels,
Milestone: apiIssue.Milestone,
Assignee: apiIssue.Assignee,
Assignees: apiIssue.Assignees,
State: apiIssue.State,
Comments: apiIssue.Comments,
HTMLURL: pr.Issue.HTMLURL(),
@ -719,7 +720,7 @@ func (pr *PullRequest) testPatch() (err error) {
}
// NewPullRequest creates new pull request with labels for repository.
func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) {
func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte, assigneeIDs []int64) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
@ -732,7 +733,11 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
LabelIDs: labelIDs,
Attachments: uuids,
IsPull: true,
AssigneeIDs: assigneeIDs,
}); err != nil {
if IsErrUserDoesNotHaveAccessToRepo(err) {
return err
}
return fmt.Errorf("newIssue: %v", err)
}

@ -600,9 +600,9 @@ func (repo *Repository) GetAssignees() (_ []*User, err error) {
return repo.getAssignees(x)
}
// GetAssigneeByID returns the user that has write access of repository by given ID.
func (repo *Repository) GetAssigneeByID(userID int64) (*User, error) {
return GetAssigneeByID(repo, userID)
// GetUserIfHasWriteAccess returns the user that has write access of repository by given ID.
func (repo *Repository) GetUserIfHasWriteAccess(userID int64) (*User, error) {
return GetUserIfHasWriteAccess(repo, userID)
}
// GetMilestoneByID returns the milestone belongs to repository by given ID.

@ -993,7 +993,7 @@ func deleteUser(e *xorm.Session, u *User) error {
// ***** END: PublicKey *****
// Clear assignee.
if _, err = e.Exec("UPDATE `issue` SET assignee_id=0 WHERE assignee_id=?", u.ID); err != nil {
if err = clearAssigneeByUserID(e, u.ID); err != nil {
return fmt.Errorf("clear assignee: %v", err)
}
@ -1110,8 +1110,8 @@ func GetUserByID(id int64) (*User, error) {
return getUserByID(x, id)
}
// GetAssigneeByID returns the user with write access of repository by given ID.
func GetAssigneeByID(repo *Repository, userID int64) (*User, error) {
// GetUserIfHasWriteAccess returns the user with write access of repository by given ID.
func GetUserIfHasWriteAccess(repo *Repository, userID int64) (*User, error) {
has, err := HasAccess(userID, repo, AccessModeWrite)
if err != nil {
return nil, err

@ -118,8 +118,12 @@ func getDingtalkPullRequestPayload(p *api.PullRequestPayload) (*DingtalkPayload,
title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueAssigned:
list, err := MakeAssigneeList(&Issue{ID: p.PullRequest.ID})
if err != nil {
return &DingtalkPayload{}, err
}
title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d %s", p.Repository.FullName,
p.PullRequest.Assignee.UserName, p.Index, p.PullRequest.Title)
list, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueUnassigned:
title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)

@ -191,8 +191,12 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta)
text = p.PullRequest.Body
color = warnColor
case api.HookIssueAssigned:
list, err := MakeAssigneeList(&Issue{ID: p.PullRequest.ID})
if err != nil {
return &DiscordPayload{}, err
}
title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d %s", p.Repository.FullName,
p.PullRequest.Assignee.UserName, p.Index, p.PullRequest.Title)
list, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
color = successColor
case api.HookIssueUnassigned:

@ -172,8 +172,12 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S
text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
attachmentText = SlackTextFormatter(p.PullRequest.Body)
case api.HookIssueAssigned:
list, err := MakeAssigneeList(&Issue{ID: p.PullRequest.ID})
if err != nil {
return &SlackPayload{}, err
}
text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName,
SlackLinkFormatter(setting.AppURL+p.PullRequest.Assignee.UserName, p.PullRequest.Assignee.UserName),
SlackLinkFormatter(setting.AppURL+list, list),
titleLink, senderLink)
case api.HookIssueUnassigned:
text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)

@ -254,6 +254,7 @@ func (f *NewDingtalkHookForm) Validate(ctx *macaron.Context, errs binding.Errors
type CreateIssueForm struct {
Title string `binding:"Required;MaxSize(255)"`
LabelIDs string `form:"label_ids"`
AssigneeIDs string `form:"assignee_ids"`
Ref string `form:"ref"`
MilestoneID int64
AssigneeID int64

@ -44,6 +44,7 @@ type InstallForm struct {
EnableOpenIDSignIn bool
EnableOpenIDSignUp bool
DisableRegistration bool
AllowOnlyExternalRegistration bool
EnableCaptcha bool
RequireSignInView bool
DefaultKeepEmailPrivate bool

@ -99,8 +99,9 @@ func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) b
// Checking for following:
// 1. Is timetracker enabled
// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
isAssigned, _ := models.IsUserAssignedToIssue(issue, user)
return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() ||
r.IsWriter() || issue.IsPoster(user.ID) || issue.AssigneeID == user.ID)
r.IsWriter() || issue.IsPoster(user.ID) || isAssigned)
}
// GetCommitsCount returns cached commit count for current view

@ -1143,6 +1143,7 @@ var Service struct {
ResetPwdCodeLives int
RegisterEmailConfirm bool
DisableRegistration bool
AllowOnlyExternalRegistration bool
ShowRegistrationButton bool
RequireSignInView bool
EnableNotifyMail bool
@ -1168,7 +1169,8 @@ func newService() {
Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!Service.DisableRegistration)
Service.AllowOnlyExternalRegistration = sec.Key("ALLOW_ONLY_EXTERNAL_REGISTRATION").MustBool()
Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!(Service.DisableRegistration || Service.AllowOnlyExternalRegistration))
Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()

@ -271,9 +271,6 @@ issues.new.no_milestone=Няма етап
issues.new.clear_milestone=Изчисти етап
issues.new.open_milestone=Отворени етапи
issues.new.closed_milestone=Затворени етапи
issues.new.assignee=Изпълнител
issues.new.clear_assignee=Изчисти изпълнител
issues.new.no_assignee=Няма изпълнител
issues.create=Създай задача
issues.new_label=Нов етикет
issues.create_label=Създай етикет

@ -270,9 +270,6 @@ issues.new.no_milestone=Bez milníku
issues.new.clear_milestone=Smazat milník
issues.new.open_milestone=Otevřít milník
issues.new.closed_milestone=Zavřené milníky
issues.new.assignee=Zpracovatel
issues.new.clear_assignee=Smazat zpracovatele
issues.new.no_assignee=Bez zpracovatele
issues.create=Vytvořit úkol
issues.new_label=Nový štítek
issues.create_label=Vytvořit štítek

@ -81,7 +81,7 @@ err_empty_admin_password=Das Administrator-Passwort darf nicht leer sein.
general_title=Allgemeine Einstellungen
app_name=Seitentitel
app_name_helper=Gebe hier den Namen deines Unternehmens ein.
app_name_helper=Du kannst hier den Namen deines Unternehmens eingeben.
repo_path=Repository-Verzeichnis
repo_path_helper=Remote-Git-Repositories werden in diesem Verzeichnis gespeichert.
lfs_path=Git LFS-Wurzelpfad
@ -136,6 +136,7 @@ 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).
invalid_db_setting=Datenbankeinstellungen sind ungültig: %v
invalid_repo_path=Repository-Verzeichnis ist ungültig: %v
run_user_not_match=Der "Ausführen als" Benutzer ist nicht der aktuelle Benutzer: %s -> %s
save_config_failed=Fehler beim Speichern der Konfiguration: %v
invalid_admin_setting=Administrator-Konto Einstellungen sind ungültig: %v
install_success=Willkommen! Danke, dass du Gitea gewählt hast. Viel Spaß!
@ -157,7 +158,7 @@ show_more_repos=Zeige mehr Repositories…
collaborative_repos=Gemeinschaftliche Repositories
my_orgs=Meine Organisationen
my_mirrors=Meine Mirrors
view_home=%s betrachten
view_home=%s ansehen
search_repos=Finde ein Repository…
issues.in_your_repos=Eigene Repositories
@ -188,6 +189,7 @@ confirmation_mail_sent_prompt=Eine neue Bestätigungs-E-Mail wurde an <b>%s</b>
reset_password_mail_sent_prompt=Eine E-Mail wurde an <b>%s</b> gesendet. Bitte überprüfe dein Postfach innerhalb der nächsten %s, um das Passwort zurückzusetzen.
active_your_account=Aktiviere dein Konto
prohibit_login=Anmelden verboten
prohibit_login_desc=Dein Account wurde gesperrt, bitte wende dich an den Administrator.
resent_limit_prompt=Du hast bereits eine Aktivierungs-E-Mail angefordert. Bitte warte 3 Minuten und probiere es dann nochmal.
has_unconfirmed_mail=Hallo %s, du hast eine unbestätigte E-Mail-Adresse (<b>%s</b>). Wenn du keine Bestätigungs-E-Mail erhalten hast oder eine neue senden möchtest, klicke bitte auf den folgenden Button.
resend_mail=Aktivierungs-E-Mail erneut verschicken
@ -208,7 +210,9 @@ login_userpass=Anmelden
login_openid=OpenID
openid_connect_submit=Verbinden
openid_connect_title=Mit bestehendem Konto verbinden
openid_connect_desc=Die gewählte OpenID URI ist unbekannt. Ordne sie hier einem neuen Account zu.
openid_register_title=Neues Konto einrichten
openid_register_desc=Die gewählte OpenID URI ist unbekannt. Ordne sie hier einem neuen Account zu.
openid_signin_desc=Gib deine OpenID-URI ein. Zum Beispiel: https://anne.me, bob.openid.org.cn oder gnusocial.net/carry.
disable_forgot_password_mail=Das Zurücksetzen von Passwörtern wurde deaktiviert. Bitte wende dich an den Administrator.
@ -279,6 +283,7 @@ unable_verify_ssh_key=Dein SSH-Key kann nicht überprüft werden, probiere es er
auth_failed=Authentifizierung fehlgeschlagen: %v
still_own_repo=Dein Konto besitzt ein oder mehrere Repositories. Diese müssen zuerst gelöscht oder übertragen werden.
still_has_org=Dein Account ist Mitglied in mindestens einer Organisation. Bitte verlasse diese zuerst.
org_still_own_repo=Diese Organisation besitzt noch mindestens ein Repository. Bitte lösche oder übertrage diese zuerst.
target_branch_not_exist=Die Ziel-Branch existiert nicht.
@ -370,11 +375,16 @@ openid_desc=Mit OpenID kannst du dich über einen Drittanbieter authentifizieren
manage_ssh_keys=SSH-Schlüssel verwalten
manage_gpg_keys=GPG-Schlüssel verwalten
add_key=Schlüssel hinzufügen
ssh_desc=Diese öffentlichen SSH-Keys sind mit deinem Account verbunden. Der dazugehörigen privaten SSH-Keys geben dir vollen Zugriff auf deine Repositories.
gpg_desc=Diese öffentlichen GPG-Keys sind mit deinem Account verbunden. Halte die dazugehörigen privaten SSH-Keys geheim, da diese deine Commits signieren.
ssh_helper=<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>.
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_gpg_key=GPG-Schlüssel hinzufügen
ssh_key_been_used=Dieser SSH-Key wurde bereits zu deinem Account hinzugefügt.
ssh_key_name_used=Ein gleichnamiger SSH-Key existiert bereits in deinem Account.
gpg_key_id_used=Ein öffentlicher GPG-Schlüssel mit der gleichen ID existiert bereits.
gpg_no_key_email_found=Dieser GPG Schlüssel kann mit keiner E-Mail-Adresse deines Accounts verwendet werden.
subkeys=Unterschlüssel
key_id=Schlüssel-ID
key_name=Schlüsselname
@ -402,6 +412,7 @@ hide_openid=Nicht im Profil anzeigen
ssh_disabled=SSH ist deaktiviert
manage_social=Verknüpfte soziale Konten verwalten
social_desc=Diese Accounts sind mit deinem Gitea-Konto verbunden. Schau dir alle Accounts an, um sicherzustellen dass du alle legitimiert hast, da man sich darüber in deinem Gitea-Konto anmelden kann.
unbind=Trennen
unbind_success=Das Konto wurde von deinem Gitea-Konto getrennt.
@ -414,7 +425,10 @@ generate_token=Token generieren
generate_token_success=Ein neuer Token wurde generiert. Kopiere diesen, da er nicht erneut angezeigt wird.
delete_token=Löschen
access_token_deletion=Zugriffstoken löschen
access_token_deletion_desc=Wenn du ein Token löschst, haben die Anwendungen, die es nutzen keinen Zugriff mehr auf deinen Account. Fortfahren?
delete_token_success=Der Zugriffstoken wurde gelöscht. Anwendungen die diesen Token genutzt haben, haben nun keinen Zugriff mehr auf deinen Account.
twofa_desc=Zwei-Faktor-Authentifizierung trägt zu einer höheren Accountsicherheit bei.
twofa_is_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung <strong>eingeschaltet</strong>.
twofa_not_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung momentan nicht eingeschaltet.
twofa_disable=Zwei-Faktor-Authentifizierung deaktivieren
@ -423,6 +437,7 @@ twofa_scratch_token_regenerated=Dein Einmalpasswort ist %s. Bewahre es an einem
twofa_enroll=Zwei-Faktor-Authentifizierung aktivieren
twofa_disable_note=Du kannst die Zwei-Faktor-Authentifizierung auch wieder deaktivieren.
twofa_disable_desc=Wenn du die Zwei-Faktor-Authentifizierung deaktivierst, wird die Sicherheit deines Kontos verringert. Fortfahren?
regenerate_scratch_token_desc=Wenn du dein Einmalpasswort verlegt oder es bereits benutzt hast, kannst du es hier zurücksetzen.
twofa_disabled=Zwei-Faktor-Authentifizierung wurde deaktiviert.
scan_this_image=Scanne diese Grafik mit deiner Authentifizierungs-App:
or_enter_secret=Oder gib das Secret ein: %s
@ -430,21 +445,34 @@ then_enter_passcode=Und gebe dann die angezeigte PIN der Anwendung ein:
passcode_invalid=Die PIN ist falsch. Probiere es erneut.
twofa_enrolled=Die Zwei-Faktor-Authentifizierung wurde für dein Konto aktiviert. Bewahre dein Einmalpasswort (%s) an einem sicheren Ort auf, da es nicht wieder angezeigt werden wird.
manage_account_links=Verknüpfte Accounts verwalten
manage_account_links_desc=Diese externen Accounts sind mit deinem Gitea-Account verknüpft.
account_links_not_available=Es sind keine externen Accounts mit diesem Gitea-Account verknüpft.
remove_account_link=Verknüpften Account entfernen
remove_account_link_desc=Wenn du den verknüpften Account entfernst, wirst du darüber nicht mehr auf deinen Gitea-Account zugreifen können. Fortfahren?
remove_account_link_success=Der verknüpfte Account wurde entfernt.
orgs_none=Du bist kein Mitglied in einer Organisation.
repos_none=Du besitzt keine Repositories
delete_account=Konto löschen
delete_prompt=Wenn du fortfährst wird dein Account permanent gelöscht. Dies <strong>KANN NICHT</strong> rückgängig gemacht werden.
confirm_delete_account=Löschen bestätigen
delete_account_title=Benutzerkonto löschen
delete_account_desc=Bist du sicher, dass du diesen Account dauerhaft löschen möchtest?
[repo]
owner=Besitzer
repo_name=Repository-Name
repo_name_helper=Ein guter Repository-Name besteht normalerweise aus kurzen, unvergesslichen und einzigartigen Schlagwörtern.
visibility=Sichtbarkeit
visiblity_helper=privates Repository
visiblity_helper_forced=Auf dieser Gitea-Instanz können nur private Repositories angelegt werden.
visiblity_fork_helper=(Eine Änderung dieses Wertes wirkt sich auf alle Forks aus)
clone_helper=Brauchst du Hilfe beim Klonen? Öffne die <a target="_blank" rel="noopener" href="%s">Hilfe</a>.
fork_repo=Repository forken
fork_from=Fork von
fork_visiblity_helper=Die Sichtbarkeit einer geforkten Repository kann nicht geändert werden.
repo_desc=Beschreibung
repo_lang=Sprache
repo_gitignore_helper=Wähle eine .gitignore-Vorlage aus.
@ -452,11 +480,16 @@ license=Lizenz
license_helper=Wähle eine Lizenz aus.
readme=README
readme_helper=Wähle eine README-Vorlage aus.
auto_init=Repository initialisieren (Fügt .gitignore, License und README-Dateien hinzu)
create_repo=Repository erstellen
default_branch=Standardbranch
mirror_prune=Entfernen
mirror_prune_desc=Entferne veraltete remote-tracking Referenzen
mirror_interval=Spiegelintervall (gültige Zeiteinheiten sind 'h', 'm', 's')
mirror_interval_invalid=Das Spiegel-Intervall ist ungültig.
mirror_address=Klonen via URL
mirror_address_desc=Bitte gebe alle benötigten Zugangsdaten in der URL an.
mirror_last_synced=Zuletzt synchronisiert
watchers=Beobachter
stargazers=Favorisiert von
forks=Forks
@ -465,16 +498,22 @@ reactions_more=und %d weitere
form.reach_limit_of_creation=Du hast bereits dein Limit von %d Repositories erreicht.
form.name_reserved=Der Repository-Name '%s' ist reserviert.
form.name_pattern_not_allowed='%s' ist nicht erlaubt für Repository-Namen.
migrate_type=Migrationstyp
migrate_type_helper=Dieses Repository wird ein <span class="text blue">Mirror</span> sein
migrate_repo=Repository migrieren
migrate.clone_address=Migrations- / Klon-URL
migrate.clone_address_desc=Die HTTP(s) oder Klon-URL eines bereits existierenden Repositories
migrate.clone_local_path=oder ein lokaler Serverpfad
migrate.permission_denied=Du hast keine Berechtigung zum Importieren lokaler Repositories.
migrate.invalid_local_path=Der lokale Pfad ist ungültig, existiert nicht oder ist kein Ordner.
migrate.failed=Fehler bei der Migration: %v
migrate.lfs_mirror_unsupported=Spiegeln von LFS-Objekten wird nicht unterstützt - nutze stattdessen 'git lfs fetch --all' und 'git lfs push --all'.
mirror_from=Mirror von
forked_from=geforkt von
fork_from_self=Du kannst kein Repository forken, das dir bereits gehört.
copy_link=Kopieren
copy_link_success=Der Link wurde kopiert
copy_link_error=Verwende ⌘-C oder Strg-C zum Kopieren
@ -513,11 +552,13 @@ file_permalink=Permalink
file_too_large=Die Datei ist zu groß zum Anzeigen.
video_not_supported_in_browser=Dein Browser unterstützt das HTML5 'video'-Tag nicht.
stored_lfs=Gespeichert mit Git LFS
commit_graph=Commit graph
editor.new_file=Neue Datei
editor.upload_file=Datei hochladen
editor.edit_file=Datei bearbeiten
editor.preview_changes=Vorschau der Änderungen
editor.cannot_edit_non_text_files=Binärdateien können nicht im Webinterface bearbeitet werden.
editor.edit_this_file=Datei bearbeiten
editor.fork_before_edit=Du musst dieses Repository forken, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen.
editor.delete_this_file=Datei löschen
@ -542,6 +583,7 @@ editor.directory_is_a_file=Der Verzeichnisname '%s' wird bereits als Dateiname i
editor.file_is_a_symlink='%s' ist ein symolischer Link. Symbolische Links können mit dem Web Editor nicht bearbeitet werden.
editor.filename_is_a_directory=Der Dateiname '%s' wird bereits als Verzeichnisname in diesem Repository verwendet.
editor.file_editing_no_longer_exists=Die bearbeitete Datei '%s' existiert nicht mehr in diesem Repository.
editor.file_changed_while_editing=Der Inhalt der Datei hat sich seit dem Beginn der Bearbeitung geändert. <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.no_changes_to_show=Keine Änderungen vorhanden.
editor.fail_to_update_file=Fehler beim Ändern/Erstellen der Datei '%s'. Fehler: %v
@ -564,8 +606,9 @@ commits.signed_by=Signiert von
commits.gpg_key_id=GPG Schlüssel-ID
ext_issues=Externe Issues
ext_issues.desc=Link zu externem Issuetracker.
issues.desc=Bearbeite Bug Reports, Aufgaben und Meilensteine.
issues.desc=Verwalte Bug-Reports, Aufgaben und Meilensteine.
issues.new=Neuer Issue
issues.new.labels=Label
issues.new.no_label=Kein Label
@ -575,9 +618,9 @@ issues.new.no_milestone=Kein Meilenstein
issues.new.clear_milestone=Meilenstein entfernen
issues.new.open_milestone=Offene Meilensteine
issues.new.closed_milestone=Geschlossene Meilensteine
issues.new.assignee=Zuständig
issues.new.clear_assignee=Zuständigkeit entfernen
issues.new.no_assignee=Niemand zuständig
issues.new.assignees=Zuständig
issues.new.clear_assignees=Zuständige entfernen
issues.new.no_assignees=Niemand zugewiesen
issues.no_ref=Keine Branch/Tag angegeben
issues.create=Issue erstellen
issues.new_label=Neues Label
@ -686,12 +729,19 @@ issues.add_time_hours=Stunden
issues.add_time_minutes=Minuten
issues.cancel_tracking=Abbrechen
issues.cancel_tracking_history=hat die Zeiterfassung %s abgebrochen
issues.time_spent_from_all_authors=`Aufgewendete Zeit: %s`
issues.due_date_form_add=Fälligkeitsdatum hinzufügen
issues.due_date_form_update=Fälligkeitsdatum ändern
issues.due_date_form_remove=Fälligkeitsdatum löschen
issues.due_date_not_set=Kein Fälligkeitsdatum gesetzt.
issues.due_date_overdue=Überfällig
pulls.new=Neuer Pull-Request
pulls.compare_changes=Neuer Pull-Request
pulls.compare_compare=pull von
pulls.filter_branch=Branch filtern
pulls.no_results=Keine Ergebnisse verfügbar.
pulls.nothing_to_compare=Diese Branches sind identisch. Es muss kein Pull-Request erstellt werden.
pulls.create=Pull-Request erstellen
pulls.title_desc=möchte %[1]d Commits von <code>%[2]s</code> nach <code>%[3]s</code> zusammenführen
pulls.merged_title_desc=hat %[1]d Commits von <code>%[2]s</code> nach <code>%[3]s</code> %[4]s zusammengeführt
@ -838,10 +888,16 @@ settings.convert_confirm=Repository umwandeln
settings.convert_succeed=Das Mirror-Repository wurde erfolgreich in ein normales Repository umgewandelt.
settings.transfer=Besitz übertragen
settings.transfer_desc=Übertrage dieses Repository auf einen anderen Benutzer oder eine Organisation in der Du Admin-Rechte hast.
settings.transfer_notices_1=- Du wirst keinen Zugriff mehr haben, wenn der neue Besitzer ein individueller Benutzer ist.
settings.transfer_notices_2=- Du wirst weiterhin Zugriff haben, wenn der neue Besitzer eine Organisation ist und du einer der Besitzer bist.
settings.transfer_form_title=Gib den Repository-Namen zur Bestätigung ein:
settings.wiki_delete=Wiki-Daten löschen
settings.wiki_delete_desc=Das Löschen von Wiki-Daten kann nicht rückgängig gemacht werden. Bitte sei vorsichtig.
settings.wiki_delete_notices_1=- Dies löscht und deaktiviert das Wiki für %s.
settings.confirm_wiki_delete=Wiki-Daten löschen
settings.wiki_deletion_success=Repository Wiki-Daten wurden gelöscht.
settings.delete=Dieses Repository löschen
settings.delete_desc=Wenn dieses Repository gelöscht wurde, gibt es keinen Weg zurück. Bitte sei vorsichtig.
settings.delete_notices_1=- Diese Operation kann <strong>NICHT</strong> rückgängig gemacht werden.
settings.deletion_success=Das Repository wurde gelöscht.
settings.update_settings_success=Repository Einstellungen wurden aktualisiert.
@ -855,12 +911,16 @@ settings.delete_collaborator=Entfernen
settings.collaborator_deletion=Mitarbeiter entfernen
settings.remove_collaborator_success=Der Mitarbeiter wurde entfernt.
settings.search_user_placeholder=Benutzer suchen…
settings.org_not_allowed_to_be_collaborator=Organisationen können nicht als Mitarbeiter hinzugefügt werden.
settings.user_is_org_member=Der Benutzer ist ein Organisationsmitglied und kann nicht als Mitarbeiter hinzugefügt werden.
settings.add_webhook=Webhook hinzufügen
settings.webhook_deletion=Webhook löschen
settings.webhook_deletion_success=Webhook wurde entfernt.
settings.webhook.test_delivery=Senden testen
settings.webhook.request=Anfrage
settings.webhook.response=Antwort
settings.webhook.headers=Kopfzeilen
settings.webhook.payload=Inhalt
settings.webhook.body=Inhalt
settings.githook_edit_desc=Wenn ein Hook nicht aktiv ist, wird der Standardinhalt benutzt. Lasse den Inhalt leer, um den Hook zu deaktivieren.
settings.githook_name=Hook-Name
@ -873,6 +933,8 @@ settings.slack_icon_url=Icon-URL
settings.discord_username=Benutzername
settings.discord_icon_url=Icon-URL
settings.slack_color=Farbe
settings.event_send_everything=Alle Events
settings.event_choose=Benutzerdefinierte Events…
settings.event_create=Erstellen
settings.event_create_desc=Branch oder Tag erstellt.
settings.event_pull_request=Pull-Request
@ -881,7 +943,10 @@ settings.event_push=Push
settings.event_push_desc=Git push in ein Repository.
settings.event_repository=Repository
settings.event_repository_desc=Repository erstellt oder gelöscht.
settings.add_hook_success=Webhook wurde hinzugefügt.
settings.update_webhook=Webhook aktualisieren
settings.update_hook_success=Webhook wurde aktualisiert.
settings.delete_webhook=Webhook entfernen
settings.recent_deliveries=Letzte Zustellungen
settings.hook_type=Hook Typ
settings.slack_token=Token
@ -889,6 +954,7 @@ settings.slack_domain=Domain
settings.slack_channel=Kanal
settings.deploy_keys=Deploy-Schlüssel
settings.add_deploy_key=Deploy-Schlüssel hinzufügen
settings.is_writable=Erlaube Schreibzugriff
settings.title=Titel
settings.deploy_key_content=Inhalt
settings.branches=Branches
@ -896,10 +962,15 @@ settings.protected_branch=Branch-Protection
settings.protected_branch_can_push=Push erlauben?
settings.protected_branch_can_push_yes=Du kannst pushen
settings.protected_branch_can_push_no=Du kannst nicht pushen
settings.branch_protection=Branch-Schutz" für Branch '<b>%s</b>'
settings.protect_this_branch=Brach-Schutz aktivieren
settings.protect_whitelist_search_users=Benutzer suchen…
settings.protect_whitelist_search_teams=Suche nach Teams…
settings.add_protected_branch=Schutz aktivieren
settings.delete_protected_branch=Schutz deaktivieren
settings.protected_branch_deletion=Brach-Schutz deaktivieren
settings.choose_branch=Wähle eine Branch…
settings.no_protected_branch=Es gibt keine geschützten Branches.
diff.browse_source=Quellcode durchsuchen
diff.parent=Ursprung
@ -955,6 +1026,9 @@ branch.create_from=von '%s'
branch.create_success=Branch '%s' wurde erstellt.
branch.branch_already_exists=Branch '%s' existiert bereits in diesem Repository.
branch.deleted_by=Von %s gelöscht
branch.restore_success=Branch '%s' wurde wiederhergestellt.
branch.restore_failed=Wiederherstellung der Branch '%s' fehlgeschlagen.
branch.protected_deletion_failed=Branch '%s' ist geschützt und kann nicht gelöscht werden.
topic.manage_topics=Themen verwalten
topic.done=Fertig
@ -962,6 +1036,7 @@ topic.done=Fertig
[org]
org_name_holder=Name der Organisation
org_full_name_holder=Vollständiger Name der Organisation
org_name_helper=Organisationsnamen sollten kurz und einprägsam sein.
create_org=Organisation erstellen
repo_updated=Aktualisiert
people=Personen
@ -973,8 +1048,12 @@ create_team=Team erstellen
org_desc=Beschreibung
team_name=Teamname
team_desc=Beschreibung
team_name_helper=Teamnamen sollten kurz und einprägsam sein.
team_permission_desc=Berechtigungen
team_unit_desc=Zugriff auf Repositorybereiche erlauben
form.name_reserved=Der Organisationsname '%s' ist reserviert.
form.create_org_not_allowed=Du bist nicht berechtigt eine Organisation zu erstellen.
settings=Einstellungen
settings.options=Organisation
@ -983,12 +1062,17 @@ settings.website=Webseite
settings.location=Standort
settings.update_settings=Einstellungen speichern
settings.update_setting_success=Organisationseinstellungen wurden aktualisiert.
settings.update_avatar_success=Der Organisationsavatar wurde aktualisiert.
settings.delete=Organisation löschen
settings.delete_account=Diese Organisation löschen
settings.confirm_delete_account=Löschen
settings.delete_org_title=Organisation löschen
settings.delete_org_desc=Diese Organisation wird dauerhaft gelöscht. Fortfahren?
settings.hooks_desc=Webhooks hinzufügen, die für <strong>alle</strong> Repositories dieser Organisation ausgelöst werden.
members.membership_visibility=Sichtbarkeit der Mitgliedschaft:
members.public=Sichtbar
members.private_helper=sichtbar machen
members.member_role=Mitgliedsrolle:
members.owner=Besitzer
members.member=Mitglied
@ -1000,12 +1084,17 @@ members.invite_now=Jetzt einladen
teams.join=Beitreten
teams.leave=Verlassen
teams.read_access=Lesezugriff
teams.read_access_helper=Mitglieder können Teamrepositories ansehen und klonen.
teams.write_access=Schreibzugriff
teams.admin_access=Administratorzugang
teams.admin_access_helper=Mitglieder können auf Team Repositories "pushen", von ihnen "pullen" und Mitarbeiter hinzufügen.
teams.no_desc=Dieses Team hat keine Beschreibung
teams.settings=Einstellungen
teams.members=Teammitglieder
teams.update_settings=Einstellungen aktualisieren
teams.delete_team=Team löschen
teams.add_team_member=Teammitglied hinzufügen
teams.delete_team_title=Team löschen
teams.delete_team_success=Das Team wurde gelöscht.
teams.repositories=Team-Repositories
teams.search_repo_placeholder=Repository durchsuchen…
@ -1015,6 +1104,7 @@ teams.add_nonexistent_repo=Das Repository, das du hinzufügen möchten, existier
[admin]
dashboard=Dashboard
users=Benutzerkonten
organizations=Organisationen
repositories=Repositories
authentication=Authentifizierungsquellen
@ -1043,6 +1133,7 @@ dashboard.git_gc_repos_success=Alle Repositories haben Garbage Collection beende
dashboard.reinit_missing_repos=Alle Git-Repositories mit Einträgen neu einlesen
dashboard.reinit_missing_repos_success=Alle verlorenen Git-Repositories mit existierenden Einträgen wurden erfolgreich aktualisiert.
dashboard.sync_external_users=Externe Benutzerdaten synchronisieren
dashboard.sync_external_users_started=Externe Benutzersynchronisation gestartet.
dashboard.git_fsck=Healthchecks auf alle Repositories ausführen
dashboard.server_uptime=Server-Uptime
dashboard.current_goroutine=Aktuelle Goroutinen
@ -1081,13 +1172,23 @@ users.activated=Aktiviert
users.admin=Administrator
users.repos=Repositories
users.created=Registriert am
users.last_login=Letzte Anmeldung
users.new_success=Der Account '%s' wurde erstellt.
users.edit=Bearbeiten
users.auth_source=Authentifizierungsquelle
users.local=Lokal
users.update_profile_success=Der Account '%s' wurde aktualisiert.
users.edit_account=Benutzerkonto bearbeiten
users.max_repo_creation=Maximale Anzahl Repositories
users.max_repo_creation_desc=(Gib -1 ein, um das globale Standardlimit zu verwenden.)
users.is_activated=Account ist aktiviert
users.is_admin=Ist Administrator
users.allow_git_hook=Darf "Git Hooks" erstellen
users.allow_import_local=Darf lokale Repositories importieren
users.allow_create_organization=Darf Organisationen erstellen
users.update_profile=Benutzerkonto aktualisieren
users.delete_account=Benutzerkonto löschen
users.deletion_success=Der Account wurde gelöscht.
orgs.org_manage_panel=Organisationsverwaltung
orgs.name=Name
@ -1104,6 +1205,7 @@ repos.stars=Favoriten
repos.issues=Issues
repos.size=Größe
auths.auth_manage_panel=Authentifikationsquellen verwalten
auths.new=Authentifizierungsquelle hinzufügen
auths.name=Name
auths.type=Typ
@ -1121,6 +1223,9 @@ auths.bind_password=Passwort binden
auths.user_base=Basis für Benutzersuche
auths.user_dn=Benutzer DN
auths.attribute_username=Benutzername Attribut
auths.attribute_name=Vornamensattribut
auths.attribute_surname=Nachnamensattribut
auths.attribute_mail=E-Mail Attribut
auths.filter=Benutzerfilter
auths.admin_filter=Admin Filter
auths.ms_ad_sa=MS AD Suchattribute
@ -1149,12 +1254,27 @@ auths.tip.facebook=Erstelle eine neue Anwendung auf https://developers.facebook.
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.openid_connect=Benutze die OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) als Endpunkt.
auths.edit=Authentifikationsquelle bearbeiten
auths.activated=Diese Authentifikationsquelle ist aktiviert
auths.new_success=Die Authentifizierung "%s" wurde hinzugefügt.
auths.update_success=Diese Authentifizierungsquelle wurde aktualisiert.
auths.update=Authentifizierungsquelle aktualisieren
auths.delete=Authentifikationsquelle löschen
auths.delete_auth_title=Authentifizierungsquelle löschen
auths.delete_auth_desc=Das Löschen einer Authentifizierungsquelle verhindert, dass Benutzer sich darüber anmelden können. Fortfahren?
auths.still_in_used=Diese Authentifizierungsquelle wird noch verwendet. Bearbeite oder lösche zuerst alle Benutzer, die diese Authentifizierungsquelle benutzen.
auths.deletion_success=Die Authentifizierungsquelle '%s' wurde gelöscht.
auths.login_source_exist=Die Authentifizierungsquelle '%s' existiert bereits.
config.server_config=Serverkonfiguration
config.app_name=Seitentitel
config.app_ver=Gitea Version
config.app_url=Gitea Basis-URL
config.custom_conf=Konfigurations-Datei-Pfad
config.domain=SSH Server-Domain
config.offline_mode=Lokaler Modus
config.disable_router_log=Router-Log deaktivieren
config.run_user=Ausführen als
config.run_mode=Laufzeit-Modus
config.git_version=Git Version
config.repo_root_path=Repository-Verzeichnis
@ -1187,16 +1307,26 @@ config.db_path=Verzeichnis
config.db_path_helper=(für "sqlite3" und "tidb")
config.service_config=Service-Konfiguration
config.register_email_confirm=E-Mail-Bestätigung benötigt zum Registrieren
config.disable_register=Selbstegistrierung deaktivieren
config.enable_openid_signup=OpenID Selbstregistrierung aktivieren
config.enable_openid_signin=OpenID Anmeldung aktivieren
config.show_registration_button=Schaltfläche zum Registrieren anzeigen
config.require_sign_in_view=Seiten nur für angemeldete Benutzer zugänglich
config.mail_notify=E-Mail-Benachrichtigungen aktivieren
config.disable_key_size_check=Prüfung der Mindestschlüssellänge deaktiveren
config.enable_captcha=CAPTCHA aktivieren
config.active_code_lives=Aktivierungscode Lebensdauer
config.reset_password_code_lives=Ablaufdatum des Passworts zurücksetzen
config.default_keep_email_private=E-Mail-Adressen standardmäßig verbergen
config.enable_timetracking=Zeiterfassung aktivieren
config.webhook_config=Webhook-Konfiguration
config.queue_length=Warteschlangenlänge
config.deliver_timeout=Zeitlimit für Zustellung
config.skip_tls_verify=TLS Verifikation überspringen
config.mailer_config=SMTP Mailer Konfiguration
config.mailer_enabled=Aktiviert
config.mailer_disable_helo=HELO Deaktivieren
config.mailer_name=Name
@ -1204,6 +1334,10 @@ config.mailer_host=Host
config.mailer_user=Benutzer
config.mailer_use_sendmail=Sendmail benutzen
config.mailer_sendmail_path=Sendmail-Pfad
config.mailer_sendmail_args=Zusätzliche Argumente für Sendmail
config.send_test_mail=Test-E-Mail senden
config.test_mail_failed=Das Senden der Test-E-Mail an '%s' ist fehlgeschlagen: %v
config.test_mail_sent=Eine Test-E-Mail wurde an '%s' gesendet.
config.oauth_config=OAuth-Konfiguration
config.oauth_enabled=Aktiviert
@ -1307,6 +1441,8 @@ raw_seconds=Sekunden
raw_minutes=Minuten
[dropzone]
default_message=Zum Hochladen hier klicken oder Datei ablegen.
invalid_input_type=Dateien dieses Dateityps können nicht hochgeladen werden.
file_too_big=Dateigröße ({{filesize}} MB) überschreitet die Maximalgröße ({{maxFilesize}} MB).
remove_file=Datei entfernen
@ -1314,6 +1450,8 @@ remove_file=Datei entfernen
notifications=Nachrichten
unread=Ungelesen
read=Gelesen
no_unread=Keine ungelesenen Benachrichtigungen.
no_read=Keine gelesenen Benachrichtigungen.
pin=Benachrichtigung pinnen
mark_as_read=Als gelesen markieren
mark_as_unread=Als ungelesen markieren
@ -1326,4 +1464,6 @@ error.no_gpg_keys_found=Es konnte kein GPG-Schlüssel zu dieser Signatur gefunde
error.not_signed_commit=Kein signierter Commit
[units]
error.no_unit_allowed_repo=Du hast keine Berechtigung auf einen Bereich dieses Repositories zuzugreifen.
error.unit_not_allowed=Du hast keine Berechtigung auf diesen Repository-Bereich zuzugreifen.

@ -85,7 +85,7 @@ err_empty_admin_password = The administrator password cannot be empty.
general_title = General Settings
app_name = Site Title
app_name_helper = Enter your company name here.
app_name_helper = You can enter your company name here.
repo_path = Repository Root Path
repo_path_helper = Remote Git repositories will be saved to this directory.
lfs_path = Git LFS Root Path
@ -121,6 +121,7 @@ federated_avatar_lookup = Enable Federated Avatars
federated_avatar_lookup_popup = Enable federated avatar lookup using Libravatar.
disable_registration = Disable Self-Registration
disable_registration_popup = Disable user self-registration. Only administrators will be able to create new user accounts.
allow_only_external_registration_popup=Enable the registration only through external services.
openid_signin = Enable OpenID Sign-In
openid_signin_popup = Enable user sign-in via OpenID.
openid_signup = Enable OpenID Self-Registration
@ -628,9 +629,9 @@ issues.new.no_milestone = No Milestone
issues.new.clear_milestone = Clear milestone
issues.new.open_milestone = Open Milestones
issues.new.closed_milestone = Closed Milestones
issues.new.assignee = Assignee
issues.new.clear_assignee = Clear assignee
issues.new.no_assignee = No assignee
issues.new.assignees = Assignees
issues.new.clear_assignees = Clear assignees
issues.new.no_assignees = Nobody assigned
issues.no_ref = No Branch/Tag Specified
issues.create = Create Issue
issues.new_label = New Label
@ -743,15 +744,15 @@ issues.cancel_tracking = Cancel
issues.cancel_tracking_history = `cancelled time tracking %s`
issues.time_spent_total = Total Time Spent
issues.time_spent_from_all_authors = `Total Time Spent: %s`
issues.due_date = Due date
issues.invalid_due_date_format = "Due date format is invalid, must be 'yyyy-mm-dd'."
issues.error_modifying_due_date = "An error occured while modifying the due date."
issues.error_removing_due_date = "An error occured while remvoing the due date."
issues.due_date_form = "Due date, format yyyy-mm-dd"
issues.due_date = Due Date
issues.invalid_due_date_format = "Due date format must be 'yyyy-mm-dd'."
issues.error_modifying_due_date = "Failed to modify the due date."
issues.error_removing_due_date = "Failed to remove the due date."
issues.due_date_form = "yyyy-mm-dd"
issues.due_date_form_add = "Add due date"
issues.due_date_form_update = "Update due date"
issues.due_date_form_remove = "Remove due date"
issues.due_date_not_writer = "You need to have at least write access to this repository in order to update the due date for this issue."
issues.due_date_not_writer = "You need repository write access to update an issue's due date."
issues.due_date_not_set = "No due date set."
issues.due_date_added = "added the due date %s %s"
issues.due_date_modified = "modified the due date to %s from %s %s"
@ -1369,8 +1370,8 @@ auths.attribute_name = First Name Attribute
auths.attribute_surname = Surname Attribute
auths.attribute_mail = Email Attribute
auths.attributes_in_bind = Fetch Attributes in Bind DN Context
auths.use_paged_search = Use paged search
auths.search_page_size = Page size
auths.use_paged_search = Use Paged Search
auths.search_page_size = Page Size
auths.filter = User Filter
auths.admin_filter = Admin Filter
auths.ms_ad_sa = MS AD Search Attributes
@ -1459,6 +1460,7 @@ config.db_path_helper = (for "sqlite3" and "tidb")
config.service_config = Service Configuration
config.register_email_confirm = Require Email Confirmation to Register
config.disable_register = Disable Self-Registration
config.allow_only_external_registration = Enable the registration only through external services
config.enable_openid_signup = Enable OpenID Self-Registration
config.enable_openid_signin = Enable OpenID Sign-In
config.show_registration_button = Show Register Button

@ -335,9 +335,6 @@ issues.new.no_milestone=Sin Milestone
issues.new.clear_milestone=Limpiar Milestone
issues.new.open_milestone=Milestones abiertas
issues.new.closed_milestone=Milestones cerradas
issues.new.assignee=Asignado a
issues.new.clear_assignee=Limpiar asignado
issues.new.no_assignee=Sin asignado
issues.create=Crear incidencia
issues.new_label=Nueva Etiqueta
issues.create_label=Crear etiqueta

@ -261,9 +261,6 @@ issues.new.no_milestone=Ei merkkipaalua
issues.new.clear_milestone=Tyhjennä merkkipaalu
issues.new.open_milestone=Avoimet merkkipaalut
issues.new.closed_milestone=Suljetut merkkipaalut
issues.new.assignee=Osoitettu
issues.new.clear_assignee=Tyhjennä osoitettu
issues.new.no_assignee=Ei osoitettua
issues.create=Ilmoita ongelma
issues.new_label=Uusi tunniste
issues.create_label=Luo tunniste

@ -340,9 +340,6 @@ issues.new.no_milestone=Aucun jalon
issues.new.clear_milestone=Effacer le jalon
issues.new.open_milestone=Ouvrir un jalon
issues.new.closed_milestone=Jalons fermés
issues.new.assignee=Affecté à
issues.new.clear_assignee=Supprimer les assignataires
issues.new.no_assignee=Pas d'assignataire
issues.no_ref=Aucune branche/tag spécifiés
issues.create=Créer un ticket
issues.new_label=Nouvelle étiquette

@ -347,9 +347,6 @@ issues.new.no_milestone=Nincs mérföldkő
issues.new.clear_milestone=Mérföldkő eltávolítása
issues.new.open_milestone=Nyitott mérföldkövek
issues.new.closed_milestone=Lezárt mérföldkövek
issues.new.assignee=Megbízott
issues.new.clear_assignee=Megbízott eltávolítása
issues.new.no_assignee=Nincs megbízott
issues.no_ref=Nincsen ág/címke megadva
issues.create=Hibajegy létrehozása
issues.new_label=Új címke

@ -340,9 +340,6 @@ issues.new.no_milestone=Tidak Ada Milestone
issues.new.clear_milestone=Bersihkan milestone
issues.new.open_milestone=Buka Milestone
issues.new.closed_milestone=Tutup Milestone
issues.new.assignee=Menerima
issues.new.clear_assignee=Jelas menerima
issues.new.no_assignee=Tidak ada penerima
issues.no_ref=Tidak Ada Cabang/Tag Ditentukan
issues.create=Buat Masalah
issues.new_label=Label Baru

@ -323,9 +323,6 @@ issues.new.no_milestone=Nessuna milestone
issues.new.clear_milestone=Milestone pulita
issues.new.open_milestone=Apri Milestone
issues.new.closed_milestone=Milestone chiuse
issues.new.assignee=Assegnatario
issues.new.clear_assignee=Cancella l'assegnatario
issues.new.no_assignee=Nessun assegnatario
issues.create=Crea Problema
issues.new_label=Nuova etichetta
issues.create_label=Crea Etichetta

@ -347,9 +347,6 @@ issues.new.no_milestone=マイルストーンなし
issues.new.clear_milestone=マイルストーンをクリア
issues.new.open_milestone=オープン中のマイルストーン
issues.new.closed_milestone=クローズされたマイルストーン
issues.new.assignee=担当者
issues.new.clear_assignee=担当者をクリア
issues.new.no_assignee=担当者なし
issues.no_ref=ブランチ/タグが指定されていません
issues.create=問題を作成
issues.new_label=新しいラベル

@ -323,9 +323,6 @@ issues.new.no_milestone=마일스톤 없음
issues.new.clear_milestone=마일스톤 초기화
issues.new.open_milestone=마일스톤 생성
issues.new.closed_milestone=마일스톤 닫기
issues.new.assignee=담당자
issues.new.clear_assignee=담당자 초기화
issues.new.no_assignee=담당자 없음
issues.no_ref=Branch/Tag 가 지정되어 있지 않습니다.
issues.create=이슈 생성
issues.new_label=새로운 레이블

@ -347,9 +347,6 @@ issues.new.no_milestone=Nav atskaites punktu
issues.new.clear_milestone=Notīrīt atskaites punktus
issues.new.open_milestone=Atvērtie atskaites punktus
issues.new.closed_milestone=Aizvērtie atskaites punkti
issues.new.assignee=Atbildīgais
issues.new.clear_assignee=Noņemt atbildīgo
issues.new.no_assignee=Nav atbildīgā
issues.no_ref=Nav norādīts atzars/tags
issues.create=Pieteikt problēmu
issues.new_label=Jauna etiķete

@ -345,9 +345,6 @@ issues.new.no_milestone=Geen mijlpaal
issues.new.clear_milestone=Verwijder mijlpaal
issues.new.open_milestone=Open mijlpalen
issues.new.closed_milestone=Gesloten mijlpalen
issues.new.assignee=Toegewezen aan
issues.new.clear_assignee=Verwijder verantwoordelijke
issues.new.no_assignee=Geen verantwoordelijke
issues.no_ref=Geen Branch/Tag gespecificeerd
issues.create=Maak probleem
issues.new_label=Nieuw Label

@ -340,9 +340,6 @@ issues.new.no_milestone=Brak kamienia milowego
issues.new.clear_milestone=Wyczyść kamień milowy
issues.new.open_milestone=Otwórz kamienie milowe
issues.new.closed_milestone=Zamknięte kamienie milowe
issues.new.assignee=Przypisany
issues.new.clear_assignee=Usuń przypisanie
issues.new.no_assignee=Brak przypisania
issues.no_ref=Nie określono gałęzi/etykiety
issues.create=Utwórz problem
issues.new_label=Nowa etykieta

@ -81,7 +81,7 @@ err_empty_admin_password=A senha do administrador não pode ser em branco.
general_title=Configurações gerais
app_name=Título do site
app_name_helper=Digite o nome da sua empresa aqui.
app_name_helper=Você pode inserir o nome da empresa aqui.
repo_path=Caminho raíz do repositório
repo_path_helper=Todos os repositórios remotos do Git serão salvos neste diretório.
lfs_path=Caminho raiz do Git LFS
@ -331,6 +331,7 @@ change_username=Seu nome de usuário foi alterado.
change_username_prompt=Nota: as alterações de nome de usuário também mudam sua URL da conta.
continue=Continuar
cancel=Cancelar
language=Idioma
lookup_avatar_by_mail=Procure o avatar do endereço de e-mail
federated_avatar_lookup=Busca de avatar federativo
@ -623,9 +624,9 @@ issues.new.no_milestone=Sem marco
issues.new.clear_milestone=Limpar marco
issues.new.open_milestone=Marcos abertos
issues.new.closed_milestone=Marcos fechados
issues.new.assignee=Responsável
issues.new.clear_assignee=Limpar responsável
issues.new.no_assignee=Não atribuída
issues.new.assignees=Responsáveis
issues.new.clear_assignees=Limpar responsáveis
issues.new.no_assignees=Nenhum responsável
issues.no_ref=Nenhum branch/tag especificado
issues.create=Criar issue
issues.new_label=Nova etiqueta
@ -739,14 +740,14 @@ issues.cancel_tracking_history=`cancelou contador de tempo %s`
issues.time_spent_total=Tempo total gasto
issues.time_spent_from_all_authors=`Tempo total gasto: %s`
issues.due_date=Data limite
issues.invalid_due_date_format=Formato da data limite inválido, deve ser 'aaaa-mm-dd'.
issues.error_modifying_due_date=Ocorreu um erro ao modificar a data limite.
issues.error_removing_due_date=Ocorreu um erro ao remover a data limite.
issues.due_date_form=Data limite, formato aaaa-mm-dd
issues.invalid_due_date_format=Formato da data limite inválido, deve ser 'dd/mm/aaaa'.
issues.error_modifying_due_date=Falha ao modificar a data limite.
issues.error_removing_due_date=Falha ao remover a data limite.
issues.due_date_form=dd/mm/aaaa
issues.due_date_form_add=Adicionar data limite
issues.due_date_form_update=Modificar data limite
issues.due_date_form_remove=Remover data limite
issues.due_date_not_writer=Você precisa ter pelo menos permissão de escrita neste repositório para atualizar a data limite desta issue.
issues.due_date_not_writer=Você deve ter permissão de escrita no repositório para atualizar a data limite de uma issue.
issues.due_date_not_set=Data limite não informada.
issues.due_date_added=adicionou a data limite %s à %s
issues.due_date_modified=modificou a data limite para %s ao invés de %s à %s
@ -798,7 +799,7 @@ milestones.title=Título
milestones.desc=Descrição
milestones.due_date=Prazo (opcional)
milestones.clear=Limpar
milestones.invalid_due_date_format=Formato da data limite deve ser 'aaaa-mm-dd'.
milestones.invalid_due_date_format=Formato da data limite deve ser 'dd/mm/aaaa'.
milestones.create_success=O marco '%s' foi criado.
milestones.edit=Editar marco
milestones.edit_subheader=Marcos organizam as issues e acompanham o progresso.
@ -1351,6 +1352,8 @@ auths.attribute_name=Atributo primeiro nome
auths.attribute_surname=Atributo sobrenome
auths.attribute_mail=Atributo e-mail
auths.attributes_in_bind=Buscar os atributos no contexto de Bind DN
auths.use_paged_search=Use a pesquisa paginada
auths.search_page_size=Tamanho da página
auths.filter=Filtro de usuário
auths.admin_filter=Filtro de administrador
auths.ms_ad_sa=Atributos de pesquisa do MS AD

@ -475,9 +475,6 @@ issues.new.no_milestone=Нет этапа
issues.new.clear_milestone=Очистить этап
issues.new.open_milestone=Открыть этап
issues.new.closed_milestone=Завершенные этапы
issues.new.assignee=Ответственный
issues.new.clear_assignee=Убрать ответственного
issues.new.no_assignee=Нет ответственного
issues.no_ref=Не указана ветка или тэг
issues.create=Добавить задачу
issues.new_label=Новая метка

@ -270,9 +270,6 @@ issues.new.no_milestone=Нема фазе
issues.new.clear_milestone=Уклони фазу
issues.new.open_milestone=Отворене фазе
issues.new.closed_milestone=Затворене фазе
issues.new.assignee=Одговорни
issues.new.clear_assignee=Уклони одговорног
issues.new.no_assignee=Нема одговорних
issues.create=Додај задатак
issues.new_label=Нова лабела
issues.create_label=Креирај лабелу

@ -339,9 +339,6 @@ issues.new.no_milestone=Ingen Milsten
issues.new.clear_milestone=Rensa milstenar
issues.new.open_milestone=Öppna Milstenar
issues.new.closed_milestone=Stängda Milstenar
issues.new.assignee=Förvärvare
issues.new.clear_assignee=Rensa förvärvare
issues.new.no_assignee=Ingen förvärvare
issues.create=Skapa Ärende
issues.new_label=Ny etikett
issues.create_label=Skapa Etikett

@ -338,9 +338,6 @@ issues.new.no_milestone=Kilometre Taşı Yok
issues.new.clear_milestone=Kilometre Taşlarını Temizle
issues.new.open_milestone=Kilometre Taşlarını Aç
issues.new.closed_milestone=Kapanmış Kilometre Taşları
issues.new.assignee=Atanan
issues.new.clear_assignee=Atamayı Temizle
issues.new.no_assignee=Atanan Kişi Yok
issues.no_ref=Bölüm/Etiket Belirtilmedi
issues.create=Sorun Oluştur
issues.new_label=Yeni Etiket

@ -11,7 +11,7 @@ sign_up=Реєстрація
link_account=Прив'язати обліковий запис
link_account_signin_or_signup=Увійдіть з уже існуючими обліковими даними або зареєструйтеся для зв'язку з цим аккаунтом.
register=Реєстрація
website=Web-сайт
website=Веб-сайт
version=Версія
page=Сторінка
template=Шаблон
@ -72,20 +72,28 @@ password=Пароль
db_name=Ім'я бази даних
ssl_mode=SSL
path=Шлях
sqlite_helper=Шлях до файлу для бази даних SQLite3 або TiDB. <br> Введіть абсолютний шлях, якщо ви запускаєте Gitea як сервіс.
err_empty_db_path=Шлях до бази даних SQLite3 або TiDB не може бути порожнім.
err_invalid_tidb_name=Ім'я бази даних TiDB не може містити символи '.' або '-'.
no_admin_and_disable_registration=Ви не можете вимкнути реєстрацію до створення облікового запису адміністратора.
err_empty_admin_password=Пароль адміністратора не може бути порожнім.
general_title=Загальні налаштування
app_name=Назва сайту
repo_path=Кореневий шлях репозиторія
repo_path_helper=Всі вилучені Git репозиторії будуть збережені в цей каталог.
lfs_path=Кореневої шлях Git LFS
lfs_path_helper=У цій папці будуть зберігатися файли Git LFS. Залиште порожнім, щоб відключити LFS.
lfs_path_helper=У цій папці будуть зберігатися файли Git LFS. Залиште порожнім, щоб вимкнути LFS.
run_user=Запуск від імені Користувача
run_user_helper=Введіть ім'я користувача операційної системи, під яким працює Gitea. Зверніть увагу, що цей користувач повинен бути доступ до кореневого шляху репозиторія.
domain=Домен SSH сервера
ssh_port=Порт SSH сервера
ssh_port_helper=Номер порту, який використовує SSH сервер. Залиште порожнім, щоб відключити SSH.
ssh_port_helper=Номер порту, який використовує SSH сервер. Залиште порожнім, щоб вимкнути SSH.
http_port=Gitea HTTP порт
http_port_helper=Номер порту, який буде прослуховуватися Giteas веб-сервером.
app_url=Базова URL-адреса Gitea
log_root_path=Шлях до лог файлу
log_root_path_helper=Файли журналу будуть записані в цей каталог.
optional_title=Додаткові налаштування
email_title=Налаштування Email
@ -93,12 +101,20 @@ smtp_host=SMTP хост
smtp_from=Відправляти Email від імені
mailer_user=SMTP Ім'я кристувача
mailer_password=SMTP Пароль
mail_notify=Дозволити поштові повідомлення
register_confirm=Потрібно підтвердити електронну пошту для реєстрації
mail_notify=Увімкнути сповіщення електронною поштою
offline_mode=Увімкнути локальний режим
disable_gravatar=Вимкнути Gravatar
federated_avatar_lookup=Увімкнути федеративні аватари
federated_avatar_lookup_popup=Увімкнути зовнішний Аватар за допомогою Libravatar.
disable_registration=Вимкнути самостійну реєстрацію
disable_registration_popup=Вимкнути самостійну реєстрацію користувачів, тільки адміністратор може створювати нові облікові записи.
openid_signin=Увімкнути реєстрацію за допомогою OpenID
openid_signin_popup=Увімкнути вхід за допомогою OpenID.
openid_signup=Увімкнути самостійну реєстрацію за допомогою OpenID
enable_captcha=Увімкнути CAPTCHA
enable_captcha_popup=Вимагати перевірку CAPTCHA при самостійній реєстрації користувача.
admin_title=Налаштування облікового запису адміністратора
admin_name=Ім'я кристувача Адміністратора
admin_password=Пароль
confirm_password=Підтвердження пароля
@ -107,8 +123,14 @@ install_btn_confirm=Встановлення Gitea
test_git_failed=Не в змозі перевірити 'git' команду: %v
save_config_failed=Не в змозі зберегти конфігурацію: %v
install_success=Ласкаво просимо! Дякуємо вам за вибір Gitea. Розважайтеся, і будьте обережні!
default_keep_email_private=Приховати адресу електронної пошти за замовчуванням
default_keep_email_private_popup=Приховати адресу електронної пошти нових облікових записів за замовчуванням.
default_allow_create_organization=Дозволити створення організацій за замовчуванням
default_enable_timetracking=Увімкнути відстеження часу за замовчуванням
no_reply_address=Прихований поштовий домен
[home]
uname_holder=Ім'я користувача або Ел. пошта
password_holder=Пароль
switch_dashboard_context=Змінити дошку
my_repos=Мої репозиторії
@ -127,17 +149,20 @@ users=Користувачі
organizations=Організації
search=Пошук
code=Код
repo_no_results=Відповідних репозиторіїв не знайдено.
code_search_results=Результати пошуку '%s'
[auth]
create_new_account=Реєстрація аккаунта
register_helper_msg=Вже зареєстровані? Увійдіть зараз!
disable_register_prompt=Вибачте, можливість реєстрації відключена. Будь ласка, зв'яжіться з адміністратором сайту.
disable_register_mail=Підтвердження реєстрації електронною поштою вимкнено.
remember_me=Запам'ятати мене
forgot_password_title=Забув пароль
forgot_password=Забули пароль?
sign_up_now=Потрібен аккаунт? Зареєструватися.
confirmation_mail_sent_prompt=Новий лист для підтвердження було направлено на <b>%s</b>, будь ласка, перевірте вашу поштову скриньку протягом% s для завершення реєстрації.
reset_password_mail_sent_prompt=Лист для підтвердження було направлено на <b>%s</b>. Будь ласка, перевірте вашу поштову скриньку протягом% s для скидання пароля.
confirmation_mail_sent_prompt=Новий лист для підтвердження було відправлено на <b>%s</b>, будь ласка, перевірте вашу поштову скриньку протягом %s для завершення реєстрації.
reset_password_mail_sent_prompt=Лист для підтвердження було відправлено на <b>%s</b>. Будь ласка, перевірте вашу поштову скриньку протягом %s для скидання пароля.
active_your_account=Активувати обліковий запис
prohibit_login=Вхід заборонений
prohibit_login_desc=Вхід для вашого профілю був заборонений, будь ласка, зв'яжіться з адміністратором сайту.
@ -197,22 +222,33 @@ TreeName=Шлях до файлу
Content=Зміст
require_error=` не може бути пустим.`
git_ref_name_error=` має бути правильним посилальним ім'ям Git.`
git_ref_name_error=` повинен бути правильним посилальним ім'ям Git.`
size_error=` повинен бути розмір %s.`
min_size_error=` повинен бути принаймні %s символів.`
max_size_error=` повинен бути не більш як %s символів.`
email_error=` не є адресою електронної пошти.`
url_error=` не є припустимою URL-Адресою.`
include_error=`повинен бути текст '%s'`
unknown_error=Невідома помилка:
captcha_incorrect=Код CAPTCHA неправильний.
password_not_match=Паролі не співпадають.
username_been_taken=Ім'я користувача вже зайнято.
repo_name_been_taken=Ім'я репозіторію вже використовується.
org_name_been_taken=Назва організації вже зайнято.
team_name_been_taken=Назва команди вже зайнято.
email_been_used=Ця електронна адреса вже використовується.
openid_been_used=OpenID адреса '%s' вже використовується.
username_password_incorrect=Неправильне ім'я користувача або пароль.
user_not_exist=Даний користувач не існує.
auth_failed=Помилка автентифікації: %v
target_branch_not_exist=Цільової гілки не існує.
[user]
change_avatar=Змінити свій аватар…
join_on=Приєднався
repositories=Репозиторії
activity=Публічна активність
@ -241,34 +277,44 @@ uid=Ідентифікатор Uid
public_profile=Загальнодоступний профіль
full_name=Повне ім'я
website=Web-сайт
website=Веб-сайт
location=Місцезнаходження
update_profile=Оновити профіль
update_profile_success=Профіль успішно оновлено.
continue=Продовжити
cancel=Відміна
language=Мова
federated_avatar_lookup=Знайти зовнішній аватар
enable_custom_avatar=Увімкнути користувацькі аватари
choose_new_avatar=Оберіть новий аватар
update_avatar=Оновити аватар
delete_current_avatar=Видалити поточний аватар
update_avatar_success=Ваш аватар був змінений.
change_password=Оновити пароль
old_password=Поточний пароль
new_password=Новий пароль
retype_new_password=Введіть новий пароль ще раз
change_password_success=Ваш пароль був оновлений. Тепер увійдіть в систему, використовуючи новий пароль.
password_change_disabled=Нелокальні акаунти не можуть змінити пароль через Gitea.
emails=Адреса електронної пошти
manage_emails=Керування адресами ел. пошти
email_desc=Ваша основна адреса електронної пошти використовуватиметься для сповіщення та інших операцій.
primary=Основний
primary_email=Зробити основним
delete_email=Видалити
add_new_email=Додати нову адресу електронної пошти
add_email=Додати адресу електронної пошти
keep_email_private=Приховати адресу електронної пошти
keep_email_private_popup=Вашу адресу електронної пошти буде приховано від інших користувачів.
manage_ssh_keys=Керувати SSH ключами
manage_gpg_keys=Керувати GPG ключами
add_key=Додати ключ
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>.
add_new_key=Додати SSH ключ
add_new_gpg_key=Додати GPG ключ
key_id=ID ключа
@ -277,6 +323,7 @@ key_content=Зміст
delete_key=Видалити
ssh_key_deletion=Видалити SSH ключ
gpg_key_deletion=Видалити GPG ключ
gpg_key_deletion_desc=Видалення GPG ключа скасовує перевірку підписаних ним комітів. Продовжити?
add_on=Додано
valid_until=Дійсний до
valid_forever=Дійсний завжди
@ -288,14 +335,18 @@ key_state_desc=Цей ключ використовувався в останн
token_state_desc=Цей токен використовувався в останні 7 днів
show_openid=Показати у профілю
hide_openid=Не показувати у профілі
ssh_disabled=SSH вимкнено
manage_social=Керувати зв'язаними аккаунтами соціальних мереж
unbind=Від'єднати
generate_new_token=Згенерувати новий токен
token_name=Ім'я токену
generate_token=Згенерувати токен
delete_token=Видалити
twofa_disable=Вимкнути двофакторну автентифікацію
or_enter_secret=Або введіть секрет: %s
@ -321,20 +372,28 @@ readme_helper=Виберіть шаблон README.
create_repo=Створити репозиторій
default_branch=Головна гілка
mirror_prune=Очистити
mirror_interval=Інтервал дзеркалювання (доступні значення 'h', 'm', 's')
mirror_address=Клонування з URL-адреси
mirror_last_synced=Остання синхронізація
watchers=Спостерігачі
forks=Форки
pick_reaction=Залиште свою оцінку
form.reach_limit_of_creation=Ви досягли максимальної кількості %d створених репозиторіїв.
need_auth=Клонування авторизації
migrate_type=Тип міграції
migrate_type_helper=Даний репозиторій буде <span class="text blue">дзеркалом</span>
migrate_repo=Перенесення репозиторія
migrate.clone_address=Міграція / клонувати з URL-адреси
migrate.clone_address_desc=URL-адреса HTTP(S) або Git "clone" існуючого репозиторія
migrate.failed=Міграція не вдалася: %v
migrate.lfs_mirror_unsupported=Дзеркалювання LFS об'єктів не підтримується - використовуйте 'git lfs fetch --all' і 'git lfs push --all' вручну.
mirror_from=дзеркало
forked_from=форк від
copy_link=Копіювати
copy_link_error=Натисніть ⌘-C або Ctrl-C, щоб скопіювати
copied=Скопійовано
unwatch=Не стежити
watch=Слідкувати
@ -368,13 +427,16 @@ file_history=Історія
file_view_raw=Перегляд Raw
file_permalink=Постійне посилання
stored_lfs=Збережено з Git LFS
commit_graph=Графік комітів
editor.new_file=Новий файл
editor.upload_file=Завантажити файл
editor.edit_file=Редагування файлу
editor.preview_changes=Попередній перегляд змін
editor.edit_this_file=Редагувати файл
editor.must_be_on_a_branch=Ви повинні бути у гілці щоб зробити, або запропонувати зміни до цього файлу.
editor.delete_this_file=Видалити файл
editor.must_have_write_access=Ви повинні мати доступ на запис щоб запропонувати зміни до цього файлу.
editor.name_your_file=Дайте назву файлу…
editor.or=або
editor.cancel_lower=Скасувати
@ -383,13 +445,17 @@ editor.add_tmpl=Додати '%s/<filename>'
editor.add=Додати '%s'
editor.update=Оновити '%s'
editor.delete=Видалити '%s'
editor.commit_message_desc=Додати необов'язковий розширений опис…
editor.commit_directly_to_this_branch=Зробіть коміт прямо в гілку <strong class="branch-name">%s</strong>.
editor.create_new_branch=Створити <strong>нову гілку</strong> для цього коміту та відкрити запит на злиття.
editor.new_branch_name_desc=Нова назва гілки…
editor.new_branch_name_desc=Ім'я нової гілки…
editor.cancel=Відміна
editor.branch_already_exists=Гілка '%s' вже присутня в репозиторії.
editor.fail_to_update_file=Не вдалося оновити/створити файл '%s' через помилку: %v
editor.upload_files_to_dir=Завантажувати файли до '%s'
commits.commits=Коміти
commits.search=Знайти коміт…
commits.find=Пошук
commits.search_all=Усі гілки
commits.author=Автор
@ -398,27 +464,32 @@ commits.date=Дата
commits.older=Давніше
commits.newer=Новіше
ext_issues=Зов. Проблеми
issues.new=Нова Проблема
issues.new=Нова проблема
issues.new.labels=Мітки
issues.new.no_label=Без Мітки
issues.new.no_label=Без мітки
issues.new.clear_labels=Очистити мітки
issues.new.milestone=Етап
issues.new.no_milestone=Етап відсутній
issues.new.clear_milestone=Очистити етап
issues.new.open_milestone=Активні етапи
issues.new.closed_milestone=Закриті етапи
issues.new.assignee=Виконавець
issues.new.clear_assignee=Прибрати виконавеця
issues.new.no_assignee=Немає виконавеця
issues.create=Створити Проблему
issues.new.assignees=Виконавеці
issues.new.clear_assignees=Прибрати виконавеців
issues.no_ref=Не вказана гілка або тег
issues.create=Створити проблему
issues.new_label=Нова мітка
issues.new_label_placeholder=Назва мітки
issues.new_label_desc_placeholder=Опис
issues.create_label=Створити мітку
issues.label_templates.helper=Оберіть набір міток
issues.label_templates.fail_to_load_file=Не вдалося завантажити файл шаблона мітки '%s': %v
issues.add_label_at=додав(ла) мітку <div class="ui label" style="color: %s\; background-color: %s">%s</div> %s
issues.add_milestone_at=`додав(ла) до <b>%s</b> етапу %s`
issues.deleted_milestone=`(видалено)`
issues.add_assignee_at=`був призначений <b>%s</b> %s`
issues.remove_assignee_at=`видалили із призначених %s`
issues.open_tab=%d відкрито
issues.close_tab=%d закрито
issues.filter_label=Мітка
@ -453,7 +524,7 @@ issues.next=Далі
issues.open_title=Відкрити
issues.closed_title=Закриті
issues.num_comments=%d коментарів
issues.commented_at=`відкоментовано <a href="#%s">%s</a>`
issues.commented_at=`прокоментував(ла) <a href="#%s">%s</a>`
issues.delete_comment_confirm=Ви впевнені, що хочете видалити цей коментар?
issues.no_content=Тут ще немає жодного змісту.
issues.close_issue=Закрити
@ -493,7 +564,11 @@ issues.add_time_short=Додати час
issues.add_time_cancel=Відміна
issues.add_time_hours=Години
issues.add_time_minutes=Хвилини
issues.add_time_sum_to_small=Час не введено.
issues.cancel_tracking=Відміна
issues.due_date=Термін дії
issues.due_date_form_add=Додати дату завершення
issues.due_date_form_remove=Видалити дату завершення
pulls.new=Новий запит на злиття
pulls.compare_changes=Новий запит на злиття
@ -503,7 +578,9 @@ pulls.filter_branch=Фільтр по гілці
pulls.no_results=Результатів не знайдено.
pulls.create=Створити запит на злиття
pulls.title_desc=хоче злити %[1]d комітів з <code>%[2]s</code> до <code>%[3]s</code>
pulls.tab_conversation=Обговорення
pulls.tab_commits=Коміти
pulls.tab_files=Змінені файли
pulls.reopen_to_merge=Будь ласка перевідкрийте цей запит щоб здіснити операцію злиття.
pulls.merged=Злито
pulls.can_auto_merge_desc=Цей запит можна об'єднати автоматично.
@ -525,6 +602,7 @@ milestones.edit=Редагувати етап
milestones.cancel=Відміна
milestones.modify=Оновити етап
ext_wiki=Зов. Wiki
wiki=Wiki
wiki.welcome=Ласкаво просимо до Wiki.
@ -533,6 +611,7 @@ wiki.page=Сторінка
wiki.filter_page=Фільтр сторінок
wiki.new_page=Сторінка
wiki.save_page=Зберегти сторінку
wiki.last_commit_info=%s редагував цю сторінку %s
wiki.edit_page_button=Редагувати
wiki.new_page_button=Нова сторінка
wiki.delete_page_button=Видалити сторінку
@ -546,15 +625,17 @@ activity.period.halfweekly=3 дні
activity.period.weekly=1 тиждень
activity.period.monthly=1 місяць
activity.overview=Огляд
activity.active_prs_count_1=<strong>%d</strong> Активний запити на злиття
activity.active_prs_count_n=<strong>%d</strong> Активні запити на злиття
activity.merged_prs_count_1=Злитий запит на злиття
activity.merged_prs_count_n=Злиті запити на злиття
activity.opened_prs_count_1=Запропонований запит на злиття
activity.opened_prs_count_n=Запропонованих запитів на злиття
activity.title.user_1=%d користувач
activity.title.user_n=%d користувачів
activity.title.user_1=%d користувачем
activity.title.user_n=%d користувачами
activity.title.prs_1=%d Запит на злиття
activity.title.prs_n=%d Запитів на злиття
activity.title.prs_merged_by=%s злито %s
activity.title.prs_opened_by=%s запропоновано %s
activity.merged_prs_label=Злито
activity.opened_prs_label=Запропоновано
@ -564,8 +645,8 @@ activity.closed_issues_count_1=Закрита проблема
activity.closed_issues_count_n=Закриті проблеми
activity.title.issues_1=%d Проблема
activity.title.issues_n=%d Проблеми
activity.title.issues_closed_by=%s закрито %s
activity.title.issues_created_by=%s створено %s
activity.title.issues_closed_by=%s закрита(і) %s
activity.title.issues_created_by=%s створена(і) %s
activity.closed_issue_label=Закриті
activity.new_issues_count_1=Нова Проблема
activity.new_issues_count_n=%d Проблем
@ -573,7 +654,7 @@ activity.new_issue_label=Відкриті
activity.unresolved_conv_label=Відкрити
activity.title.releases_1=%d Реліз
activity.title.releases_n=%d Релізів
activity.title.releases_published_by=%s Опубліковано %s
activity.title.releases_published_by=%s опубліковано %s
activity.published_release_label=Опубліковано
search=Пошук
@ -582,7 +663,7 @@ search.search_repo=Пошук репозиторію
settings=Налаштування
settings.options=Репозиторій
settings.collaboration.admin=Адміністратор
settings.collaboration.write=Написати
settings.collaboration.write=Запис
settings.collaboration.read=Читати
settings.collaboration.undefined=Не визначено
settings.hooks=Веб-хуки
@ -590,7 +671,7 @@ settings.githooks=Git хуки
settings.basic_settings=Базові налаштування
settings.mirror_settings=Налаштування дзеркала
settings.sync_mirror=Синхронізувати зараз
settings.site=Web-сайт
settings.site=Веб-сайт
settings.update_settings=Оновити налаштування
settings.advanced_settings=Додаткові налаштування
settings.wiki_desc=Увімкнути репозиторії Wiki
@ -598,14 +679,21 @@ settings.use_internal_wiki=Використовувати вбудовані Wik
settings.use_external_wiki=Використовувати зовнішні Wiki
settings.external_wiki_url=URL зовнішньої wiki
settings.external_tracker_url=URL зовнішньої системи відстеження проблем
settings.tracker_url_format=Формат URL зовнішнього трекера задач
settings.tracker_issue_style.numeric=Цифровий
settings.tracker_issue_style.alphanumeric=Буквено-цифровий
settings.admin_settings=Налаштування адміністратора
settings.danger_zone=Небезпечна зона
settings.new_owner_has_same_repo=Новий власник вже має репозиторій з такою назвою. Будь ласка, виберіть інше ім'я.
settings.transfer=Переказати новому власнику
settings.convert=Перетворити на звичайний репозиторій
settings.transfer=Передати новому власнику
settings.wiki_delete=Видалити Wiki-дані
settings.confirm_wiki_delete=Видалити Wiki-дані
settings.delete=Видалити цей репозиторій
settings.delete_notices_1=- Цю операцію <strong>НЕ МОЖНА</strong> відмінити.
settings.update_settings_success=Налаштування репозиторію було оновлено.
settings.transfer_owner=Новий власник
settings.make_transfer=Здіснити перенесення
settings.confirm_delete=Видалити репозиторій
settings.delete_collaborator=Видалити
settings.search_user_placeholder=Пошук користувача…
@ -626,6 +714,7 @@ settings.slack_icon_url=URL іконки
settings.discord_username=Ім'я кристувача
settings.discord_icon_url=URL іконки
settings.slack_color=Колір
settings.event_send_everything=Всі події
settings.event_create=Створити
settings.event_create_desc=Гілку або тег створено.
settings.event_pull_request=Запити до злиття
@ -639,7 +728,7 @@ settings.slack_domain=Домен
settings.slack_channel=Канал
settings.deploy_keys=Ключі для розгортування
settings.add_deploy_key=Додати ключ для розгортування
settings.is_writable=Включити доступ для запису
settings.is_writable=Увімкнути доступ для запису
settings.title=Заголовок
settings.deploy_key_content=Зміст
settings.branches=Гілки
@ -653,8 +742,11 @@ settings.delete_protected_branch=Вимкнути захист
settings.choose_branch=Оберіть гілку…
diff.browse_source=Переглянути джерело
diff.parent=джерело
diff.commit=коміт
diff.show_diff_stats=Показати статистику Diff
diff.show_split_view=Розділений перегляд
diff.show_unified_view=Об'єднаний перегляд
diff.stats_desc=<strong> %d змінених файлів</strong> з <strong>%d додано</strong> та <strong>%d видалено</strong>
diff.view_file=Переглянути файл
diff.too_many_files=Деякі файли не було показано, через те що забагато файлів було змінено
@ -668,11 +760,14 @@ release.edit=редагувати
release.ahead=<strong>%d</strong> комітів %s після цього релізу
release.tag_name=Назва тегу
release.target=Ціль
release.tag_helper=Виберіть існуючий тег або створіть новий.
release.title=Заголовок
release.content=Зміст
release.write=Запис
release.preview=Переглянути
release.loading=Завантаження…
release.prerelease_desc=Позначити як пре-реліз
release.prerelease_helper=Позначте цей випуск непридатним для ПРОД використання.
release.cancel=Відміна
release.publish=Опублікувати реліз
release.save_draft=Зберегти чернетку
@ -710,13 +805,17 @@ team_permission_desc=Права доступу
settings=Налаштування
settings.options=Організація
settings.full_name=Повне ім'я
settings.website=Web-сайт
settings.website=Веб-сайт
settings.location=Розташування
settings.update_settings=Оновити налаштування
settings.delete=Видалити організацію
settings.delete_account=Видалити цю організацію
settings.confirm_delete_account=Підтвердіть видалення
members.membership_visibility=Видимість учасника:
members.public_helper=зробити прихованим
members.private=Прихований
members.private_helper=зробити видимим
members.member_role=Роль учасника:
members.owner=Власник
members.member=Учасник
@ -728,6 +827,8 @@ members.invite_now=Запросити зараз
teams.join=Приєднатися
teams.leave=Покинути
teams.read_access=Доступ для читання
teams.write_access=Доступ на запис
teams.admin_access=Доступ адміністратора
teams.settings=Налаштування
teams.members=Учасники команди
teams.update_settings=Оновити налаштування
@ -740,6 +841,7 @@ dashboard=Панель управління
users=Облікові записи користувачів
organizations=Організації
repositories=Репозиторії
authentication=Джерела автентифікації
config=Конфігурація
notices=Сповіщення системи
monitor=Моніторинг
@ -755,20 +857,40 @@ dashboard.delete_inactivate_accounts=Видалити всі неактивні
dashboard.delete_inactivate_accounts_success=Усі неактивні облікові записи успішно видалено.
dashboard.server_uptime=Uptime серверу
dashboard.current_memory_usage=Поточне використання пам'яті
dashboard.total_memory_allocated=Виділено пам'яті загалом
dashboard.memory_obtained=Отримано пам'яті
dashboard.stack_memory_obtained=Зайнято пам'яті стеком
dashboard.mspan_structures_usage=Використання структур MSpan
dashboard.mspan_structures_obtained=Отримано структур MSpan
dashboard.mcache_structures_usage=Використання структур MCache
dashboard.mcache_structures_obtained=Отримано структур MCache
dashboard.profiling_bucket_hash_table_obtained=Отримано хеш-таблиць профілювання
dashboard.gc_metadata_obtained=Отримано метаданих GC
dashboard.other_system_allocation_obtained=Отримання інших виділень пам'яті
dashboard.next_gc_recycle=Наступний цикл GC
users.user_manage_panel=Керування обліковими записами користувачів
users.new_account=Створити обліковий запис
users.name=Ім'я кристувача
users.activated=Активовано
users.admin=Адміністратор
users.repos=Репозиторії
users.created=Створено
users.last_login=Останній вхід
users.send_register_notify=Надіслати повідомлення про реєстрацію користувача
users.edit=Редагувати
users.auth_source=Джерело автентифікації
users.local=Локальні
users.max_repo_creation=Максимальна кількість репозиторіїв
users.max_repo_creation_desc=(Введіть -1, щоб використовувати глобальний ліміт за замовчуванням.)
users.is_activated=Обліковий запис користувача увімкнено
users.prohibit_login=Вимкнути вхід
users.is_admin=Адміністратор
users.allow_git_hook=Може створювати Git хуки
users.allow_import_local=Може імпортувати локальні репозиторії
users.allow_create_organization=Може створювати організацій
users.update_profile=Оновити обліковий запис
users.delete_account=Видалити цей обліковий запис
orgs.org_manage_panel=Керування організаціями
orgs.name=Назва
@ -780,9 +902,12 @@ repos.repo_manage_panel=Керування організаціями
repos.owner=Власник
repos.name=Назва
repos.private=Приватний
repos.stars=В обраному
repos.issues=Проблеми
repos.size=Розмір
auths.auth_manage_panel=Керування джерелом аутентифікації
auths.new=Додати джерело автентифікації
auths.name=Ім'я
auths.type=Тип
auths.enabled=Увімкнено
@ -807,11 +932,27 @@ auths.oauth2_profileURL=URL профілю
auths.oauth2_emailURL=URL електронної пошти
auths.enable_auto_register=Увімкнути автоматичну реєстрацію
auths.tips=Поради
auths.tips.oauth2.general=OAuth2 аутентифікація
auths.tips.oauth2.general.tip=При додаванні нового OAuth2 провайдера, URL адреса переадресації по завершенні аутентифікації повинена виглядати так:<host>/user/oauth2/<Authentication Name>/callback
auths.tip.oauth2_provider=Постачальник OAuth2
auths.tip.dropbox=Додайте новий додаток на https://www.dropbox.com/developers/apps
auths.tip.facebook=Створіть новий додаток на https://developers.facebook.com/apps і додайте модуль "Facebook Login
auths.tip.github=Додайте OAuth додаток на https://github.com/settings/applications/new
auths.tip.gitlab=Додайте новий додаток на https://gitlab.com/profile/applications
auths.tip.openid_connect=Використовуйте OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) для автоматичної настройки входу OAuth
auths.edit=Редагувати джерело автентифікації
auths.update=Оновити джерело автентифікації
auths.delete=Видалити джерело автентифікації
auths.delete_auth_title=Видалити джерело автентифікації
config.server_config=Конфігурація сервера
config.app_name=Назва сайту
config.app_ver=Версія Gitea
config.app_url=Базова URL-адреса Gitea
config.custom_conf=Шлях до файлу конфігурації
config.domain=Домен SSH сервера
config.disable_router_log=Вимкнути логування роутеру
config.run_user=Запуск від імені Користувача
config.run_mode=Режим виконання
config.git_version=Версія Git
config.repo_root_path=Кореневий шлях репозиторія
@ -822,7 +963,14 @@ config.script_type=Тип скрипта
config.ssh_config=Конфігурація SSH
config.ssh_enabled=Увімкнено
config.ssh_domain=Домен сервера
config.ssh_port=Порт
config.ssh_listen_port=Порт що прослуховується
config.ssh_root_path=Шлях до кореню
config.ssh_key_test_path=Шлях до тестового ключа
config.ssh_keygen_path=Шлях до генератора ключів ('ssh-keygen')
config.ssh_minimum_key_size_check=Мінімальний розмір ключа перевірки
config.ssh_minimum_key_sizes=Мінімальні розміри ключів
config.db_config=Конфігурація бази даних
config.db_type=Тип
@ -835,16 +983,32 @@ config.db_path=Шлях
config.db_path_helper=(для "sqlite3" і "tidb")
config.service_config=Конфігурація сервісу
config.register_email_confirm=Потрібно підтвердити електронну пошту для реєстрації
config.disable_register=Вимкнути самостійну реєстрацію
config.enable_openid_signup=Увімкнути самостійну реєстрацію за допомогою OpenID
config.enable_openid_signin=Увімкнути реєстрацію за допомогою OpenID
config.show_registration_button=Показувати кнопку "Реєстрація
config.mail_notify=Увімкнути сповіщення електронною поштою
config.disable_key_size_check=Вимкнути перевірку мінімального розміру ключа
config.enable_captcha=Увімкнути CAPTCHA
config.active_code_lives=Час актуальності кода підтвердження
config.default_keep_email_private=Приховати адресу електронної пошти за замовчуванням
config.default_allow_create_organization=Дозволити створення організацій за замовчуванням
config.default_enable_timetracking=Увімкнути відстеження часу за замовчуванням
config.webhook_config=Конфігурація web-хуків
config.queue_length=Довжина черги
config.deliver_timeout=Затримка доставлення
config.skip_tls_verify=Пропустити перевірку TLS
config.mailer_enabled=Увімкнено
config.mailer_disable_helo=Вимкнути HELO
config.mailer_name=Ім'я
config.mailer_host=Хост
config.mailer_user=Користувач
config.mailer_use_sendmail=Використовувати Sendmail
config.mailer_sendmail_path=Шлях до Sendmail
config.send_test_mail=Відправити тестового листа
config.oauth_config=Конфігурація OAuth
config.oauth_enabled=Увімкнено
@ -856,7 +1020,10 @@ config.cache_conn=Підключення до кешу
config.session_config=Конфігурація сесії
config.session_provider=Провайдер сесії
config.provider_config=Конфігурація постачальника
config.cookie_name=Ім'я файлу cookie
config.enable_set_cookie=Увімкнути встановлення cookie
config.gc_interval_time=Інтервал запуску GC
config.session_life_time=Час життя сесії
config.https_only=Тільки HTTPS
config.cookie_life_time=Час життя cookie-файлу
@ -867,6 +1034,9 @@ config.enable_federated_avatar=Увімкнути зовнішні аватар
config.git_config=Конфігурація git
config.git_disable_diff_highlight=Вимкнути підсвітку синтаксису diff
config.git_max_diff_lines=Максимум рядків на diff (на один файл)
config.git_max_diff_line_characters=Максимум символів на diff (на одну строку)
config.git_max_diff_files=Максимум diff-файлів (для показу)
config.git_gc_args=Аргументи GC
config.git_migrate_timeout=Тайм-аут міграції
config.git_mirror_timeout=Тайм-аут оновлення дзеркала
@ -875,6 +1045,7 @@ config.git_pull_timeout=Тайм-аут операції Pull
config.git_gc_timeout=Тайм-аут операції GC
config.log_config=Конфігурація журналу
config.log_mode=Режим журналювання
monitor.cron=Завдання cron
monitor.name=Ім'я
@ -889,6 +1060,8 @@ monitor.execute_time=Час виконання
notices.system_notice_list=Сповіщення системи
notices.actions=Дії
notices.select_all=Вибрати все
notices.deselect_all=Скасувати виділення
notices.inverse_selection=Інвертувати виділене
notices.delete_selected=Видалити обране
notices.delete_all=Видалити усі cповіщення
notices.type=Тип
@ -899,21 +1072,26 @@ notices.op=Оп.
[action]
create_repo=створено репозиторій <a href="%s">%s</a>
rename_repo=репозиторій перейменовано з <code>%[1]s</code> на <a href="%[2]s">%[3]s</a>
commit_repo=запушено до <a href="%[1]s/src/%[2]s">%[3]s</a> у <a href="%[1]s">%[4]s</a>
create_issue=`відкрито проблему <a href="%s/issues/%s">%s#%[2]s</a>`
commit_repo=виконав(ла) push в <a href="%[1]s/src/%[2]s">%[3]s</a> у <a href="%[1]s">%[4]s</a>
create_issue=`відкрив(ла) проблему <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`закрито проблему <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`повторно відкрито проблему <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`повторно відкрив(ла) проблему <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`створено запити на злиття <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`закрито запит на злиття <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`повторно відкрито запит на злиття <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`відкоментовано проблему <a href="%s/issues/%s">%s#%[2]s</a>`
comment_issue=`прокоментував(ла) проблему <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`запит на злиття злито <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=перенесено репозиторій <code>%s</code> у <a href="%s">%s</a>
push_tag=створив(ла) тег <a href="%s/src/%s">%[2]s</a> в <a href="%[1]s">%[3]s</a>
delete_tag=видалено мітку %[2]s з <a href="%[1]s">%[3]s</a>
delete_branch=видалено гілку %[2]s з <a href="%[1]s">%[3]s</a>
compare_commits=Порівняти %d комітів
[tool]
ago=%s тому
from_now=%s з цього моменту
now=зараз
future=в майбутньому
1s=1 секунда
1m=1 хвилина
1h=1 година
@ -932,6 +1110,7 @@ raw_seconds=секунди
raw_minutes=хвилини
[dropzone]
default_message=Перетягніть файли або натисніть тут, щоб завантажити.
file_too_big=Розмір файлу ({{filesize}} MB), що більше ніж максимальний розмір: ({{maxFilesize}} MB).
remove_file=Видалити файл
@ -939,11 +1118,15 @@ remove_file=Видалити файл
notifications=Сповіщення
unread=Непрочитані
read=Прочитані
no_unread=Немає непрочитаних сповіщень.
no_read=Немає прочитаних сповіщень.
pin=Прикріпити сповіщення
mark_as_read=Позначити як прочитане
mark_as_unread=Позначити як непрочитане
mark_all_as_read=Позначити всі як прочитані
[gpg]
error.not_signed_commit=Непідписаний коміт
[units]

File diff suppressed because it is too large Load Diff

@ -329,9 +329,6 @@ issues.new.no_milestone=未選擇里程碑
issues.new.clear_milestone=清除已選取里程碑
issues.new.open_milestone=開啟中的里程碑
issues.new.closed_milestone=已關閉的里程碑
issues.new.assignee=指派成員
issues.new.clear_assignee=取消指派成員
issues.new.no_assignee=未指派成員
issues.create=建立問題
issues.new_label=建立標籤
issues.create_label=建立標籤

@ -339,9 +339,6 @@ issues.new.no_milestone=未選擇里程碑
issues.new.clear_milestone=清除已選取里程碑
issues.new.open_milestone=開啟中的里程碑
issues.new.closed_milestone=已關閉的里程碑
issues.new.assignee=指派成員
issues.new.clear_assignee=取消指派成員
issues.new.no_assignee=未指派成員
issues.no_ref=未指定分支或標籤
issues.create=建立問題
issues.new_label=建立標籤

665
package-lock.json generated

@ -0,0 +1,665 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"amdefine": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
"dev": true
},
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
"dev": true,
"optional": true
},
"asn1": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
"dev": true,
"optional": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
"dev": true,
"optional": true
},
"aws4": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
"integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==",
"dev": true,
"optional": true
},
"bcrypt-pbkdf": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
"integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
"dev": true,
"optional": true,
"requires": {
"tweetnacl": "0.14.5"
}
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
"dev": true,
"optional": true
},
"clean-css": {
"version": "3.4.28",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz",
"integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=",
"dev": true,
"requires": {
"commander": "2.8.1",
"source-map": "0.4.4"
},
"dependencies": {
"source-map": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
"dev": true,
"requires": {
"amdefine": "1.0.1"
}
}
}
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
"dev": true,
"optional": true
},
"combined-stream": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
"dev": true,
"requires": {
"delayed-stream": "1.0.0"
}
},
"commander": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
"integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
"dev": true,
"requires": {
"graceful-readlink": "1.0.1"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true,
"optional": true
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"dev": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true,
"optional": true
}
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"ecc-jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
"dev": true,
"optional": true,
"requires": {
"jsbn": "0.1.1"
}
},
"errno": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
"integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
"dev": true,
"optional": true,
"requires": {
"prr": "1.0.1"
}
},
"extend": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
"dev": true,
"optional": true
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
"dev": true
},
"fast-deep-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
"dev": true,
"optional": true
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
"dev": true,
"optional": true
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
"dev": true,
"optional": true
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"dev": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true,
"optional": true
}
}
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
"dev": true,
"optional": true
},
"graceful-readlink": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
"integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
"dev": true
},
"image-size": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
"integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
"dev": true,
"optional": true
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"dev": true,
"optional": true
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true,
"optional": true
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"dev": true,
"optional": true
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
"dev": true,
"optional": true
},
"json-schema-traverse": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
"dev": true,
"optional": true
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
"dev": true,
"optional": true
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"dev": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true,
"optional": true
}
}
},
"less": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/less/-/less-3.0.4.tgz",
"integrity": "sha512-q3SyEnPKbk9zh4l36PGeW2fgynKu+FpbhiUNx/yaiBUQ3V0CbACCgb9FzYWcRgI2DJlP6eI4jc8XPrCTi55YcQ==",
"dev": true,
"requires": {
"errno": "0.1.7",
"graceful-fs": "4.1.11",
"image-size": "0.5.5",
"mime": "1.6.0",
"mkdirp": "0.5.1",
"promise": "7.3.1",
"request": "2.85.0",
"source-map": "0.6.1"
},
"dependencies": {
"ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"dev": true,
"optional": true,
"requires": {
"co": "4.6.0",
"fast-deep-equal": "1.1.0",
"fast-json-stable-stringify": "2.0.0",
"json-schema-traverse": "0.3.1"
}
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true,
"optional": true
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
"dev": true,
"optional": true
},
"boom": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
"integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
"dev": true,
"optional": true,
"requires": {
"hoek": "4.2.1"
}
},
"cryptiles": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
"integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
"dev": true,
"optional": true,
"requires": {
"boom": "5.2.0"
},
"dependencies": {
"boom": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
"integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
"dev": true,
"optional": true,
"requires": {
"hoek": "4.2.1"
}
}
}
},
"form-data": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
"dev": true,
"optional": true,
"requires": {
"asynckit": "0.4.0",
"combined-stream": "1.0.6",
"mime-types": "2.1.18"
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
"dev": true,
"optional": true
},
"har-validator": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
"dev": true,
"optional": true,
"requires": {
"ajv": "5.5.2",
"har-schema": "2.0.0"
}
},
"hawk": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
"integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
"dev": true,
"optional": true,
"requires": {
"boom": "4.3.1",
"cryptiles": "3.1.2",
"hoek": "4.2.1",
"sntp": "2.1.0"
}
},
"hoek": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
"integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==",
"dev": true
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"dev": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0",
"jsprim": "1.4.1",
"sshpk": "1.14.1"
}
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"dev": true,
"optional": true
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
"dev": true,
"optional": true
},
"request": {
"version": "2.85.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz",
"integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==",
"dev": true,
"optional": true,
"requires": {
"aws-sign2": "0.7.0",
"aws4": "1.7.0",
"caseless": "0.12.0",
"combined-stream": "1.0.6",
"extend": "3.0.1",
"forever-agent": "0.6.1",
"form-data": "2.3.2",
"har-validator": "5.0.3",
"hawk": "6.0.2",
"http-signature": "1.2.0",
"is-typedarray": "1.0.0",
"isstream": "0.1.2",
"json-stringify-safe": "5.0.1",
"mime-types": "2.1.18",
"oauth-sign": "0.8.2",
"performance-now": "2.1.0",
"qs": "6.5.2",
"safe-buffer": "5.1.2",
"stringstream": "0.0.5",
"tough-cookie": "2.3.4",
"tunnel-agent": "0.6.0",
"uuid": "3.2.1"
}
},
"sntp": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
"integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
"dev": true,
"optional": true,
"requires": {
"hoek": "4.2.1"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"optional": true
}
}
},
"less-plugin-clean-css": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/less-plugin-clean-css/-/less-plugin-clean-css-1.5.1.tgz",
"integrity": "sha1-zFeveqM5iVflbezr5jy2DCNClwM=",
"dev": true,
"requires": {
"clean-css": "3.4.28"
}
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dev": true,
"optional": true
},
"mime-db": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
"integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
"dev": true
},
"mime-types": {
"version": "2.1.18",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
"dev": true,
"requires": {
"mime-db": "1.33.0"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true,
"optional": true
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
},
"oauth-sign": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
"dev": true,
"optional": true
},
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=",
"dev": true,
"optional": true,
"requires": {
"asap": "2.0.6"
}
},
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
"dev": true,
"optional": true
},
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
"dev": true,
"optional": true
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true
},
"sshpk": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz",
"integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=",
"dev": true,
"optional": true,
"requires": {
"asn1": "0.2.3",
"assert-plus": "1.0.0",
"bcrypt-pbkdf": "1.0.1",
"dashdash": "1.14.1",
"ecc-jsbn": "0.1.1",
"getpass": "0.1.7",
"jsbn": "0.1.1",
"tweetnacl": "0.14.5"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true,
"optional": true
}
}
},
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
"integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
"dev": true,
"optional": true
},
"tough-cookie": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
"integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
"dev": true,
"optional": true,
"requires": {
"punycode": "1.4.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "5.1.2"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true,
"optional": true
},
"uuid": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
"integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==",
"dev": true,
"optional": true
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"dev": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "1.3.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true,
"optional": true
}
}
}
}
}

@ -1,7 +1,7 @@
{
"license": "MIT",
"devDependencies": {
"less": "^2.7.2",
"less": "^3.0.4",
"less-plugin-clean-css": "^1.5.1"
}
}

File diff suppressed because one or more lines are too long

@ -179,81 +179,115 @@ function initCommentForm() {
initBranchSelector();
initCommentPreviewTab($('.comment.form'));
// Labels
var $list = $('.ui.labels.list');
var $noSelect = $list.find('.no-select');
var $labelMenu = $('.select-label .menu');
var hasLabelUpdateAction = $labelMenu.data('action') == 'update';
$('.select-label').dropdown('setting', 'onHide', function(){
if (hasLabelUpdateAction) {
location.reload();
}
});
$labelMenu.find('.item:not(.no-select)').click(function () {
if ($(this).hasClass('checked')) {
$(this).removeClass('checked');
$(this).find('.octicon').removeClass('octicon-check');
// Listsubmit
function initListSubmits(selector, outerSelector) {
var $list = $('.ui.' + outerSelector + '.list');
var $noSelect = $list.find('.no-select');
var $listMenu = $('.' + selector + ' .menu');
var hasLabelUpdateAction = $listMenu.data('action') == 'update';
$('.' + selector).dropdown('setting', 'onHide', function(){
hasLabelUpdateAction = $listMenu.data('action') == 'update'; // Update the var
if (hasLabelUpdateAction) {
updateIssuesMeta(
$labelMenu.data('update-url'),
"detach",
$labelMenu.data('issue-id'),
$(this).data('id')
);
location.reload();
}
} else {
$(this).addClass('checked');
$(this).find('.octicon').addClass('octicon-check');
if (hasLabelUpdateAction) {
});
$listMenu.find('.item:not(.no-select)').click(function () {
// we don't need the action attribute when updating assignees
if (selector == 'select-assignees-modify') {
// UI magic. We need to do this here, otherwise it would destroy the functionality of
// adding/removing labels
if ($(this).hasClass('checked')) {
$(this).removeClass('checked');
$(this).find('.octicon').removeClass('octicon-check');
} else {
$(this).addClass('checked');
$(this).find('.octicon').addClass('octicon-check');
}
updateIssuesMeta(
$labelMenu.data('update-url'),
"attach",
$labelMenu.data('issue-id'),
$listMenu.data('update-url'),
"",
$listMenu.data('issue-id'),
$(this).data('id')
);
$listMenu.data('action', 'update'); // Update to reload the page when we updated items
return false;
}
}
var labelIds = [];
$(this).parent().find('.item').each(function () {
if ($(this).hasClass('checked')) {
labelIds.push($(this).data('id'));
$($(this).data('id-selector')).removeClass('hide');
$(this).removeClass('checked');
$(this).find('.octicon').removeClass('octicon-check');
if (hasLabelUpdateAction) {
updateIssuesMeta(
$listMenu.data('update-url'),
"detach",
$listMenu.data('issue-id'),
$(this).data('id')
);
}
} else {
$($(this).data('id-selector')).addClass('hide');
$(this).addClass('checked');
$(this).find('.octicon').addClass('octicon-check');
if (hasLabelUpdateAction) {
updateIssuesMeta(
$listMenu.data('update-url'),
"attach",
$listMenu.data('issue-id'),
$(this).data('id')
);
}
}
var listIds = [];
$(this).parent().find('.item').each(function () {
if ($(this).hasClass('checked')) {
listIds.push($(this).data('id'));
$($(this).data('id-selector')).removeClass('hide');
} else {
$($(this).data('id-selector')).addClass('hide');
}
});
if (listIds.length == 0) {
$noSelect.removeClass('hide');
} else {
$noSelect.addClass('hide');
}
$($(this).parent().data('id')).val(listIds.join(","));
return false;
});
if (labelIds.length == 0) {
$listMenu.find('.no-select.item').click(function () {
if (hasLabelUpdateAction || selector == 'select-assignees-modify') {
updateIssuesMeta(
$listMenu.data('update-url'),
"clear",
$listMenu.data('issue-id'),
""
);
$listMenu.data('action', 'update'); // Update to reload the page when we updated items
}
$(this).parent().find('.item').each(function () {
$(this).removeClass('checked');
$(this).find('.octicon').removeClass('octicon-check');
});
$list.find('.item').each(function () {
$(this).addClass('hide');
});
$noSelect.removeClass('hide');
} else {
$noSelect.addClass('hide');
}
$($(this).parent().data('id')).val(labelIds.join(","));
return false;
});
$labelMenu.find('.no-select.item').click(function () {
if (hasLabelUpdateAction) {
updateIssuesMeta(
$labelMenu.data('update-url'),
"clear",
$labelMenu.data('issue-id'),
""
);
}
$($(this).parent().data('id')).val('');
$(this).parent().find('.item').each(function () {
$(this).removeClass('checked');
$(this).find('.octicon').removeClass('octicon-check');
});
}
$list.find('.item').each(function () {
$(this).addClass('hide');
});
$noSelect.removeClass('hide');
$($(this).parent().data('id')).val('');
});
// Init labels and assignees
initListSubmits('select-label', 'labels');
initListSubmits('select-assignees', 'assignees');
initListSubmits('select-assignees-modify', 'assignees');
function selectItem(select_id, input_id) {
var $menu = $(select_id + ' .menu');
@ -2227,7 +2261,11 @@ function initTopicbar() {
alert(res.message);
} else {
viewDiv.children(".topic").remove();
if (topics.length == 0) {
return
}
var topicArray = topics.split(",");
var last = viewDiv.children("a").last();
for (var i=0;i < topicArray.length; i++) {
$('<div class="ui green basic label topic" style="cursor:pointer;">'+topicArray[i]+'</div>').insertBefore(last)

@ -152,6 +152,10 @@ pre, code {
}
}
&.floating.label {
z-index: 10;
}
&.menu,
&.vertical.menu,
&.segment {
@ -167,6 +171,14 @@ pre, code {
font-size: .92857143rem;
}
&.dropdown .menu>.item>.floating.label {
z-index: 11;
}
&.dropdown .menu .menu>.item>.floating.label {
z-index: 21;
}
.text {
&.red {
color: #d95c5c !important;

@ -119,8 +119,11 @@
}
.octicon {
float: left;
margin-left: -5px;
margin-right: -7px;
margin: 5px -7px 0 -5px;
width: 16px;
}
.text{
margin-left: 0.9em;
}
.menu {
max-height: 300px;

@ -1605,7 +1605,7 @@
}
}
},
"/repos/{owner}/{repo}/issue/{index}/comments": {
"/repos/{owner}/{repo}/issues": {
"get": {
"produces": [
"application/json"
@ -1613,8 +1613,8 @@
"tags": [
"issue"
],
"summary": "List all comments on an issue",
"operationId": "issueGetComments",
"summary": "List a repository's issues",
"operationId": "issueListIssues",
"parameters": [
{
"type": "string",
@ -1630,29 +1630,32 @@
"in": "path",
"required": true
},
{
"type": "string",
"description": "whether issue is open or closed",
"name": "state",
"in": "query"
},
{
"type": "integer",
"description": "index of the issue",
"name": "id",
"in": "path",
"required": true
"description": "page number of requested issues",
"name": "page",
"in": "query"
},
{
"type": "string",
"description": "if provided, only comments updated since the specified time are returned.",
"name": "string",
"description": "search string",
"name": "q",
"in": "query"
}
],
"responses": {
"200": {
"$ref": "#/responses/CommentList"
"$ref": "#/responses/IssueList"
}
}
}
},
"/repos/{owner}/{repo}/issue/{index}/labels": {
"put": {
},
"post": {
"consumes": [
"application/json"
],
@ -1662,8 +1665,8 @@
"tags": [
"issue"
],
"summary": "Replace an issue's labels",
"operationId": "issueReplaceLabels",
"summary": "Create an issue",
"operationId": "issueCreateIssue",
"parameters": [
{
"type": "string",
@ -1679,39 +1682,31 @@
"in": "path",
"required": true
},
{
"type": "integer",
"description": "index of the issue",
"name": "index",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/IssueLabelsOption"
"$ref": "#/definitions/CreateIssueOption"
}
}
],
"responses": {
"200": {
"$ref": "#/responses/LabelList"
"201": {
"$ref": "#/responses/Issue"
}
}
},
"post": {
"consumes": [
"application/json"
],
}
},
"/repos/{owner}/{repo}/issues/comments": {
"get": {
"produces": [
"application/json"
],
"tags": [
"issue"
],
"summary": "Add a label to an issue",
"operationId": "issueAddLabel",
"summary": "List all comments in a repository",
"operationId": "issueGetRepoComments",
"parameters": [
{
"type": "string",
@ -1728,35 +1723,26 @@
"required": true
},
{
"type": "integer",
"description": "index of the issue",
"name": "index",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/IssueLabelsOption"
}
"type": "string",
"description": "if provided, only comments updated since the provided time are returned.",
"name": "string",
"in": "query"
}
],
"responses": {
"200": {
"$ref": "#/responses/LabelList"
"$ref": "#/responses/CommentList"
}
}
},
}
},
"/repos/{owner}/{repo}/issues/comments/{id}": {
"delete": {
"produces": [
"application/json"
],
"tags": [
"issue"
],
"summary": "Remove all labels from an issue",
"operationId": "issueClearLabels",
"summary": "Delete a comment",
"operationId": "issueDeleteComment",
"parameters": [
{
"type": "string",
@ -1774,8 +1760,8 @@
},
{
"type": "integer",
"description": "index of the issue",
"name": "index",
"description": "id of comment to delete",
"name": "id",
"in": "path",
"required": true
}
@ -1785,18 +1771,19 @@
"$ref": "#/responses/empty"
}
}
}
},
"/repos/{owner}/{repo}/issue/{index}/labels/{id}": {
"delete": {
},
"patch": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"issue"
],
"summary": "Remove a label from an issue",
"operationId": "issueRemoveLabel",
"summary": "Edit a comment",
"operationId": "issueEditComment",
"parameters": [
{
"type": "string",
@ -1814,27 +1801,27 @@
},
{
"type": "integer",
"description": "index of the issue",
"name": "index",
"description": "id of the comment to edit",
"name": "id",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "id of the label to remove",
"name": "id",
"in": "path",
"required": true
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/EditIssueCommentOption"
}
}
],
"responses": {
"204": {
"$ref": "#/responses/empty"
"200": {
"$ref": "#/responses/Comment"
}
}
}
},
"/repos/{owner}/{repo}/issues": {
"/repos/{owner}/{repo}/issues/{index}": {
"get": {
"produces": [
"application/json"
@ -1842,8 +1829,8 @@
"tags": [
"issue"
],
"summary": "List a repository's issues",
"operationId": "issueListIssues",
"summary": "Get an issue",
"operationId": "issueGetIssue",
"parameters": [
{
"type": "string",
@ -1859,32 +1846,21 @@
"in": "path",
"required": true
},
{
"type": "string",
"description": "whether issue is open or closed",
"name": "state",
"in": "query"
},
{
"type": "integer",
"description": "page number of requested issues",
"name": "page",
"in": "query"
},
{
"type": "string",
"description": "search string",
"name": "q",
"in": "query"
"description": "index of the issue to get",
"name": "index",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"$ref": "#/responses/IssueList"
"$ref": "#/responses/Issue"
}
}
},
"post": {
"patch": {
"consumes": [
"application/json"
],
@ -1894,8 +1870,8 @@
"tags": [
"issue"
],
"summary": "Create an issue",
"operationId": "issueCreateIssue",
"summary": "Edit an issue",
"operationId": "issueEditIssue",
"parameters": [
{
"type": "string",
@ -1911,11 +1887,18 @@
"in": "path",
"required": true
},
{
"type": "integer",
"description": "index of the issue to edit",
"name": "index",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/CreateIssueOption"
"$ref": "#/definitions/EditIssueOption"
}
}
],
@ -1926,7 +1909,7 @@
}
}
},
"/repos/{owner}/{repo}/issues/comments": {
"/repos/{owner}/{repo}/issues/{index}/comments": {
"get": {
"produces": [
"application/json"
@ -1934,8 +1917,8 @@
"tags": [
"issue"
],
"summary": "List all comments in a repository",
"operationId": "issueGetRepoComments",
"summary": "List all comments on an issue",
"operationId": "issueGetComments",
"parameters": [
{
"type": "string",
@ -1951,9 +1934,16 @@
"in": "path",
"required": true
},
{
"type": "integer",
"description": "index of the issue",
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "if provided, only comments updated since the provided time are returned.",
"description": "if provided, only comments updated since the specified time are returned.",
"name": "string",
"in": "query"
}
@ -1963,15 +1953,64 @@
"$ref": "#/responses/CommentList"
}
}
},
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"issue"
],
"summary": "Add a comment to an issue",
"operationId": "issueCreateComment",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "index of the issue",
"name": "id",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/CreateIssueOption"
}
}
],
"responses": {
"201": {
"$ref": "#/responses/Comment"
}
}
}
},
"/repos/{owner}/{repo}/issues/comments/{id}": {
"/repos/{owner}/{repo}/issues/{index}/comments/{id}": {
"delete": {
"tags": [
"issue"
],
"summary": "Delete a comment",
"operationId": "issueDeleteComment",
"operationId": "issueDeleteCommentDeprecated",
"deprecated": true,
"parameters": [
{
"type": "string",
@ -1987,6 +2026,13 @@
"in": "path",
"required": true
},
{
"type": "integer",
"description": "this parameter is ignored",
"name": "index",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "id of comment to delete",
@ -2012,7 +2058,8 @@
"issue"
],
"summary": "Edit a comment",
"operationId": "issueEditComment",
"operationId": "issueEditCommentDeprecated",
"deprecated": true,
"parameters": [
{
"type": "string",
@ -2028,6 +2075,13 @@
"in": "path",
"required": true
},
{
"type": "integer",
"description": "this parameter is ignored",
"name": "index",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "id of the comment to edit",
@ -2050,7 +2104,7 @@
}
}
},
"/repos/{owner}/{repo}/issues/{index}": {
"/repos/{owner}/{repo}/issues/{index}/labels": {
"get": {
"produces": [
"application/json"
@ -2058,8 +2112,8 @@
"tags": [
"issue"
],
"summary": "Get an issue",
"operationId": "issueGetIssue",
"summary": "Get an issue's labels",
"operationId": "issueGetLabels",
"parameters": [
{
"type": "string",
@ -2077,7 +2131,7 @@
},
{
"type": "integer",
"description": "index of the issue to get",
"description": "index of the issue",
"name": "index",
"in": "path",
"required": true
@ -2085,11 +2139,14 @@
],
"responses": {
"200": {
"$ref": "#/responses/Issue"
"$ref": "#/responses/LabelList"
},
"404": {
"$ref": "#/responses/notFound"
}
}
},
"patch": {
"put": {
"consumes": [
"application/json"
],
@ -2099,8 +2156,8 @@
"tags": [
"issue"
],
"summary": "Edit an issue",
"operationId": "issueEditIssue",
"summary": "Replace an issue's labels",
"operationId": "issueReplaceLabels",
"parameters": [
{
"type": "string",
@ -2118,7 +2175,7 @@
},
{
"type": "integer",
"description": "index of the issue to edit",
"description": "index of the issue",
"name": "index",
"in": "path",
"required": true
@ -2127,18 +2184,16 @@
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/EditIssueOption"
"$ref": "#/definitions/IssueLabelsOption"
}
}
],
"responses": {
"201": {
"$ref": "#/responses/Issue"
"200": {
"$ref": "#/responses/LabelList"
}
}
}
},
"/repos/{owner}/{repo}/issues/{index}/comments": {
},
"post": {
"consumes": [
"application/json"
@ -2149,8 +2204,8 @@
"tags": [
"issue"
],
"summary": "Add a comment to an issue",
"operationId": "issueCreateComment",
"summary": "Add a label to an issue",
"operationId": "issueAddLabel",
"parameters": [
{
"type": "string",
@ -2169,7 +2224,7 @@
{
"type": "integer",
"description": "index of the issue",
"name": "id",
"name": "index",
"in": "path",
"required": true
},
@ -2177,25 +2232,25 @@
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/CreateIssueOption"
"$ref": "#/definitions/IssueLabelsOption"
}
}
],
"responses": {
"201": {
"$ref": "#/responses/Comment"
"200": {
"$ref": "#/responses/LabelList"
}
}
}
},
"/repos/{owner}/{repo}/issues/{index}/comments/{id}": {
},
"delete": {
"produces": [
"application/json"
],
"tags": [
"issue"
],
"summary": "Delete a comment",
"operationId": "issueDeleteCommentDeprecated",
"deprecated": true,
"summary": "Remove all labels from an issue",
"operationId": "issueClearLabels",
"parameters": [
{
"type": "string",
@ -2213,17 +2268,10 @@
},
{
"type": "integer",
"description": "this parameter is ignored",
"description": "index of the issue",
"name": "index",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "id of comment to delete",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
@ -2231,20 +2279,18 @@
"$ref": "#/responses/empty"
}
}
},
"patch": {
"consumes": [
"application/json"
],
}
},
"/repos/{owner}/{repo}/issues/{index}/labels/{id}": {
"delete": {
"produces": [
"application/json"
],
"tags": [
"issue"
],
"summary": "Edit a comment",
"operationId": "issueEditCommentDeprecated",
"deprecated": true,
"summary": "Remove a label from an issue",
"operationId": "issueRemoveLabel",
"parameters": [
{
"type": "string",
@ -2262,72 +2308,22 @@
},
{
"type": "integer",
"description": "this parameter is ignored",
"description": "index of the issue",
"name": "index",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "id of the comment to edit",
"description": "id of the label to remove",
"name": "id",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/EditIssueCommentOption"
}
}
],
"responses": {
"200": {
"$ref": "#/responses/Comment"
}
}
}
},
"/repos/{owner}/{repo}/issues/{index}/labels": {
"get": {
"produces": [
"application/json"
],
"tags": [
"issue"
],
"summary": "Get an issue's labels",
"operationId": "issueGetLabels",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "index of the issue",
"name": "index",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"$ref": "#/responses/LabelList"
},
"404": {
"$ref": "#/responses/notFound"
"204": {
"$ref": "#/responses/empty"
}
}
}

@ -178,25 +178,22 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
DeadlineUnix: deadlineUnix,
}
if ctx.Repo.IsWriter() {
if len(form.Assignee) > 0 {
assignee, err := models.GetUserByName(form.Assignee)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", form.Assignee))
} else {
ctx.Error(500, "GetUserByName", err)
}
return
}
issue.AssigneeID = assignee.ID
// Get all assignee IDs
assigneeIDs, err := models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
} else {
ctx.Error(500, "AddAssigneeByName", err)
}
issue.MilestoneID = form.Milestone
} else {
form.Labels = nil
return
}
if err := models.NewIssue(ctx.Repo.Repository, issue, form.Labels, nil); err != nil {
if err := models.NewIssue(ctx.Repo.Repository, issue, form.Labels, assigneeIDs, nil); err != nil {
if models.IsErrUserDoesNotHaveAccessToRepo(err) {
ctx.Error(400, "UserDoesNotHaveAccessToRepo", err)
return
}
ctx.Error(500, "NewIssue", err)
return
}
@ -209,7 +206,6 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
}
// Refetch from database to assign some automatic values
var err error
issue, err = models.GetIssueByID(issue.ID)
if err != nil {
ctx.Error(500, "GetIssueByID", err)
@ -272,6 +268,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
issue.Content = *form.Body
}
// Update the deadline
var deadlineUnix util.TimeStamp
if form.Deadline != nil && !form.Deadline.IsZero() {
deadlineUnix = util.TimeStamp(form.Deadline.Unix())
@ -282,28 +279,28 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
return
}
if ctx.Repo.IsWriter() && form.Assignee != nil &&
(issue.Assignee == nil || issue.Assignee.LowerName != strings.ToLower(*form.Assignee)) {
if len(*form.Assignee) == 0 {
issue.AssigneeID = 0
} else {
assignee, err := models.GetUserByName(*form.Assignee)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Error(422, "", fmt.Sprintf("assignee does not exist: [name: %s]", *form.Assignee))
} else {
ctx.Error(500, "GetUserByName", err)
}
return
}
issue.AssigneeID = assignee.ID
// Add/delete assignees
// Deleting is done the Github way (quote from their api documentation):
// https://developer.github.com/v3/issues/#edit-an-issue
// "assignees" (array): Logins for Users to assign to this issue.
// Pass one or more user logins to replace the set of assignees on this Issue.
// Send an empty array ([]) to clear all assignees from the Issue.
if ctx.Repo.IsWriter() && (form.Assignees != nil || form.Assignee != nil) {
oneAssignee := ""
if form.Assignee != nil {
oneAssignee = *form.Assignee
}
if err = models.UpdateIssueUserByAssignee(issue); err != nil {
ctx.Error(500, "UpdateIssueUserByAssignee", err)
err = models.UpdateAPIAssignee(issue, oneAssignee, form.Assignees, ctx.User)
if err != nil {
ctx.Error(500, "UpdateAPIAssignee", err)
return
}
}
if ctx.Repo.IsWriter() && form.Milestone != nil &&
issue.MilestoneID != *form.Milestone {
oldMilestoneID := issue.MilestoneID

@ -15,7 +15,7 @@ import (
// ListIssueComments list all the comments of an issue
func ListIssueComments(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/issue/{index}/comments issue issueGetComments
// swagger:operation GET /repos/{owner}/{repo}/issues/{index}/comments issue issueGetComments
// ---
// summary: List all comments on an issue
// produces:

@ -58,7 +58,7 @@ func ListIssueLabels(ctx *context.APIContext) {
// AddIssueLabels add labels for an issue
func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
// swagger:operation POST /repos/{owner}/{repo}/issue/{index}/labels issue issueAddLabel
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/labels issue issueAddLabel
// ---
// summary: Add a label to an issue
// consumes:
@ -129,7 +129,7 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
// DeleteIssueLabel delete a label for an issue
func DeleteIssueLabel(ctx *context.APIContext) {
// swagger:operation DELETE /repos/{owner}/{repo}/issue/{index}/labels/{id} issue issueRemoveLabel
// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/labels/{id} issue issueRemoveLabel
// ---
// summary: Remove a label from an issue
// produces:
@ -193,7 +193,7 @@ func DeleteIssueLabel(ctx *context.APIContext) {
// ReplaceIssueLabels replace labels for an issue
func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
// swagger:operation PUT /repos/{owner}/{repo}/issue/{index}/labels issue issueReplaceLabels
// swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/labels issue issueReplaceLabels
// ---
// summary: Replace an issue's labels
// consumes:
@ -264,7 +264,7 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
// ClearIssueLabels delete all the labels for an issue
func ClearIssueLabels(ctx *context.APIContext) {
// swagger:operation DELETE /repos/{owner}/{repo}/issue/{index}/labels issue issueClearLabels
// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/labels issue issueClearLabels
// ---
// summary: Remove all labels from an issue
// produces:

@ -211,26 +211,6 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
milestoneID = milestone.ID
}
if len(form.Assignee) > 0 {
assigneeUser, err := models.GetUserByName(form.Assignee)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Error(422, "", fmt.Sprintf("assignee does not exist: [name: %s]", form.Assignee))
} else {
ctx.Error(500, "GetUserByName", err)
}
return
}
assignee, err := repo.GetAssigneeByID(assigneeUser.ID)
if err != nil {
ctx.Error(500, "GetAssigneeByID", err)
return
}
assigneeID = assignee.ID
}
patch, err := headGitRepo.GetPatch(prInfo.MergeBase, headBranch)
if err != nil {
ctx.Error(500, "GetPatch", err)
@ -266,7 +246,22 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
Type: models.PullRequestGitea,
}
if err := models.NewPullRequest(repo, prIssue, labelIDs, []string{}, pr, patch); err != nil {
// Get all assignee IDs
assigneeIDs, err := models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
} else {
ctx.Error(500, "AddAssigneeByName", err)
}
return
}
if err := models.NewPullRequest(repo, prIssue, labelIDs, []string{}, pr, patch, assigneeIDs); err != nil {
if models.IsErrUserDoesNotHaveAccessToRepo(err) {
ctx.Error(400, "UserDoesNotHaveAccessToRepo", err)
return
}
ctx.Error(500, "NewPullRequest", err)
return
} else if err := pr.PushToBaseRepo(); err != nil {
@ -335,6 +330,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
issue.Content = form.Body
}
// Update Deadline
var deadlineUnix util.TimeStamp
if form.Deadline != nil && !form.Deadline.IsZero() {
deadlineUnix = util.TimeStamp(form.Deadline.Unix())
@ -345,28 +341,27 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
return
}
if ctx.Repo.IsWriter() && len(form.Assignee) > 0 &&
(issue.Assignee == nil || issue.Assignee.LowerName != strings.ToLower(form.Assignee)) {
if len(form.Assignee) == 0 {
issue.AssigneeID = 0
} else {
assignee, err := models.GetUserByName(form.Assignee)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Error(422, "", fmt.Sprintf("assignee does not exist: [name: %s]", form.Assignee))
} else {
ctx.Error(500, "GetUserByName", err)
}
return
}
issue.AssigneeID = assignee.ID
}
// Add/delete assignees
if err = models.UpdateIssueUserByAssignee(issue); err != nil {
ctx.Error(500, "UpdateIssueUserByAssignee", err)
// Deleting is done the Github way (quote from their api documentation):
// https://developer.github.com/v3/issues/#edit-an-issue
// "assignees" (array): Logins for Users to assign to this issue.
// Pass one or more user logins to replace the set of assignees on this Issue.
// Send an empty array ([]) to clear all assignees from the Issue.
if ctx.Repo.IsWriter() && (form.Assignees != nil || len(form.Assignee) > 0) {
err = models.UpdateAPIAssignee(issue, form.Assignee, form.Assignees, ctx.User)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
} else {
ctx.Error(500, "UpdateAPIAssignee", err)
}
return
}
}
if ctx.Repo.IsWriter() && form.Milestone != 0 &&
issue.MilestoneID != form.Milestone {
oldMilestoneID := issue.MilestoneID

@ -112,6 +112,7 @@ func Install(ctx *context.Context) {
form.EnableOpenIDSignIn = setting.Service.EnableOpenIDSignIn
form.EnableOpenIDSignUp = setting.Service.EnableOpenIDSignUp
form.DisableRegistration = setting.Service.DisableRegistration
form.AllowOnlyExternalRegistration = setting.Service.AllowOnlyExternalRegistration
form.EnableCaptcha = setting.Service.EnableCaptcha
form.RequireSignInView = setting.Service.RequireSignInView
form.DefaultKeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate
@ -304,6 +305,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
cfg.Section("openid").Key("ENABLE_OPENID_SIGNIN").SetValue(com.ToStr(form.EnableOpenIDSignIn))
cfg.Section("openid").Key("ENABLE_OPENID_SIGNUP").SetValue(com.ToStr(form.EnableOpenIDSignUp))
cfg.Section("service").Key("DISABLE_REGISTRATION").SetValue(com.ToStr(form.DisableRegistration))
cfg.Section("service").Key("ALLOW_ONLY_EXTERNAL_REGISTRATION").SetValue(com.ToStr(form.AllowOnlyExternalRegistration))
cfg.Section("service").Key("ENABLE_CAPTCHA").SetValue(com.ToStr(form.EnableCaptcha))
cfg.Section("service").Key("REQUIRE_SIGNIN_VIEW").SetValue(com.ToStr(form.RequireSignInView))
cfg.Section("service").Key("DEFAULT_KEEP_EMAIL_PRIVATE").SetValue(com.ToStr(form.DefaultKeepEmailPrivate))

@ -364,7 +364,7 @@ func NewIssue(ctx *context.Context) {
}
// ValidateRepoMetas check and returns repository's meta informations
func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64, int64, int64) {
func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64, []int64, int64) {
var (
repo = ctx.Repo.Repository
err error
@ -372,11 +372,11 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64
labels := RetrieveRepoMetas(ctx, ctx.Repo.Repository)
if ctx.Written() {
return nil, 0, 0
return nil, nil, 0
}
if !ctx.Repo.IsWriter() {
return nil, 0, 0
return nil, nil, 0
}
var labelIDs []int64
@ -385,7 +385,7 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64
if len(form.LabelIDs) > 0 {
labelIDs, err = base.StringsToInt64s(strings.Split(form.LabelIDs, ","))
if err != nil {
return nil, 0, 0
return nil, nil, 0
}
labelIDMark := base.Int64sToMap(labelIDs)
@ -407,23 +407,35 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64
ctx.Data["Milestone"], err = repo.GetMilestoneByID(milestoneID)
if err != nil {
ctx.ServerError("GetMilestoneByID", err)
return nil, 0, 0
return nil, nil, 0
}
ctx.Data["milestone_id"] = milestoneID
}
// Check assignee.
assigneeID := form.AssigneeID
if assigneeID > 0 {
ctx.Data["Assignee"], err = repo.GetAssigneeByID(assigneeID)
// Check assignees
var assigneeIDs []int64
if len(form.AssigneeIDs) > 0 {
assigneeIDs, err = base.StringsToInt64s(strings.Split(form.AssigneeIDs, ","))
if err != nil {
ctx.ServerError("GetAssigneeByID", err)
return nil, 0, 0
return nil, nil, 0
}
// Check if the passed assignees actually exists and has write access to the repo
for _, aID := range assigneeIDs {
_, err = repo.GetUserIfHasWriteAccess(aID)
if err != nil {
ctx.ServerError("GetUserIfHasWriteAccess", err)
return nil, nil, 0
}
}
ctx.Data["assignee_id"] = assigneeID
}
return labelIDs, milestoneID, assigneeID
// Keep the old assignee id thingy for compatibility reasons
if form.AssigneeID > 0 {
assigneeIDs = append(assigneeIDs, form.AssigneeID)
}
return labelIDs, assigneeIDs, milestoneID
}
// NewIssuePost response for creating new issue
@ -440,7 +452,7 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
attachments []string
)
labelIDs, milestoneID, assigneeID := ValidateRepoMetas(ctx, form)
labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form)
if ctx.Written() {
return
}
@ -460,11 +472,14 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
PosterID: ctx.User.ID,
Poster: ctx.User,
MilestoneID: milestoneID,
AssigneeID: assigneeID,
Content: form.Content,
Ref: form.Ref,
}
if err := models.NewIssue(repo, issue, labelIDs, attachments); err != nil {
if err := models.NewIssue(repo, issue, labelIDs, assigneeIDs, attachments); err != nil {
if models.IsErrUserDoesNotHaveAccessToRepo(err) {
ctx.Error(400, "UserDoesNotHaveAccessToRepo", err.Error())
return
}
ctx.ServerError("NewIssue", err)
return
}
@ -702,8 +717,8 @@ func ViewIssue(ctx *context.Context) {
comment.Milestone = ghostMilestone
}
} else if comment.Type == models.CommentTypeAssignees {
if err = comment.LoadAssignees(); err != nil {
ctx.ServerError("LoadAssignees", err)
if err = comment.LoadAssigneeUser(); err != nil {
ctx.ServerError("LoadAssigneeUser", err)
return
}
} else if comment.Type == models.CommentTypeCode || comment.Type == models.CommentTypeReview {
@ -917,13 +932,20 @@ func UpdateIssueAssignee(ctx *context.Context) {
}
assigneeID := ctx.QueryInt64("id")
action := ctx.Query("action")
for _, issue := range issues {
if issue.AssigneeID == assigneeID {
continue
}
if err := issue.ChangeAssignee(ctx.User, assigneeID); err != nil {
ctx.ServerError("ChangeAssignee", err)
return
switch action {
case "clear":
if err := models.DeleteNotPassedAssignee(issue, ctx.User, []*models.User{}); err != nil {
ctx.ServerError("ClearAssignees", err)
return
}
default:
if err := issue.ChangeAssignee(ctx.User, assigneeID); err != nil {
ctx.ServerError("ChangeAssignee", err)
return
}
}
}
ctx.JSON(200, map[string]interface{}{

@ -785,7 +785,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
return
}
labelIDs, milestoneID, assigneeID := ValidateRepoMetas(ctx, form)
labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form)
if ctx.Written() {
return
}
@ -821,7 +821,6 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
PosterID: ctx.User.ID,
Poster: ctx.User,
MilestoneID: milestoneID,
AssigneeID: assigneeID,
IsPull: true,
Content: form.Content,
}
@ -838,7 +837,12 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
}
// FIXME: check error in the case two people send pull request at almost same time, give nice error prompt
// instead of 500.
if err := models.NewPullRequest(repo, pullIssue, labelIDs, attachments, pullRequest, patch); err != nil {
if err := models.NewPullRequest(repo, pullIssue, labelIDs, attachments, pullRequest, patch, assigneeIDs); err != nil {
if models.IsErrUserDoesNotHaveAccessToRepo(err) {
ctx.Error(400, "UserDoesNotHaveAccessToRepo", err.Error())
return
}
ctx.ServerError("NewPullRequest", err)
return
} else if err := pullRequest.PushToBaseRepo(); err != nil {

@ -21,7 +21,11 @@ func TopicPost(ctx *context.Context) {
return
}
topics := strings.Split(ctx.Query("topics"), ",")
var topics = make([]string, 0)
var topicsStr = strings.TrimSpace(ctx.Query("topics"))
if len(topicsStr) > 0 {
topics = strings.Split(topicsStr, ",")
}
err := models.SaveTopics(ctx.Repo.Repository.ID, topics...)
if err != nil {

@ -741,7 +741,8 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
if setting.Service.DisableRegistration {
//Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
if !setting.Service.ShowRegistrationButton {
ctx.Error(403)
return
}

@ -0,0 +1,66 @@
APP_NAME = Gitea: Go Git Service
RUN_USER = root
RUN_MODE = prod
CUSTOM_PATH = SNAP_DIR_DATA/custom
[server]
DOMAIN = localhost
PROTOCOL = http
HTTP_PORT = 3001
ROOT_URL = http://localhost:3001/
DISABLE_SSH = false
SSH_PORT = 22
STATIC_ROOT_PATH = SNAP_DIR_DATA/static
APP_DATA_PATH = SNAP_DIR_COMMON/data
SSH_ROOT = SNAP_DIR_COMMON/ssh
SSH_KEY_TEST_PATH = SNAP_DIR_DATA/sshkeytest
[database]
DB_TYPE = sqlite3
PATH = SNAP_DIR_COMMON/gitea.db
[repository]
ROOT = SNAP_DIR_COMMON/repositories/data
[repository.upload]
ENABLED = true
ALLOWED_TYPES = "image/jpeg|image/png"
FILE_MAX_SIZE = 10
MAX_FILES = 5
TEMP_PATH = SNAP_DIR_COMMON/repositories/tmp
[release.attachment]
PATH = SNAP_DIR_COMMON/releases/attachments
[smartypants]
ENABLED = true
[indexer]
ISSUE_INDEXER_PATH = SNAP_DIR_COMMON/indexers/issues.bleve
[mailer]
ENABLED = false
[service]
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = false
DISABLE_REGISTRATION = false
ENABLE_CAPTCHA = false
REQUIRE_SIGNIN_VIEW = false
[picture]
AVATAR_UPLOAD_PATH = SNAP_DIR_COMMON/pictures/avatars
DISABLE_GRAVATAR = true
ENABLE_FEDERATED_AVATAR = false
[attachment]
PATH = SNAP_DIR_COMMON/attachments
[session]
PROVIDER = memory
[log]
MODE = file
LEVEL = Trace
ROOT_PATH = SNAP_DIR_COMMON/log

@ -0,0 +1,126 @@
#!/bin/bash
if snapctl get gitea.snap.custom; then
cdir=$(snapctl get gitea.snap.custom)
else
cdir=$SNAP_COMMON
fi
cfg="$cdir/conf/app.ini"
bak="$cdir/conf/app.ini.bak-$(date -Ins)"
basecfg="$SNAP/snap/helpers/app.ini"
smp="$SNAP/gitea/custom/conf/app.ini.sample"
function toSnap() {
OIFS=$IFS
IFS='
'
category="none"
src="$cfg"
[[ "$1" = "init" ]] && src="$smp"
[[ "$1" = "snap" ]] && src="$basecfg"
for l in $(sed 's_;\([A-Z]*\)_\1_g' $src | grep -v -e '^;' -e '^$'); do
if echo $l | grep -q '^[[]'; then
category=$(CatToSnap "$l")
elif echo $l | grep -q '^[A-Z]'; then
option=$(OptToSnap "$l")
value=$(ValToSnap "$l")
if [[ $category = "none" ]]; then
snapctl set "$option=$value"
else
snapctl set "$category.$option=$value"
fi
fi
done
IFS=$OIFS
}
function toIni() {
OIFS=$IFS
IFS='
'
category="none"; option="none"; catUnset=true
src=$smp
[[ -f $cfg ]] && src="$cfg"
tmpIni="$cfg.tmp"
[[ -f $src ]] && cp "$src" "$tmpIni"
cp $tmpIni $bak
echo '' > $cfg
for l in $(grep -v -e '^;' -e '^$' $tmpIni); do
if echo $l | grep -q '^[[]'; then
category=$(CatToSnap "$l")
catUnset=true
elif echo $l | grep -q '^[A-Z]'; then
option=$(OptToSnap "$l")
if [[ $category = "none" ]]; then
value=$(snapctl get $option)
echo $(OptToIni "$option") = $value >> $cfg
else
value=$(snapctl get $category.$option)
if $catUnset; then
echo "" >> $cfg
echo "[$(CatToIni "$category")]" >> $cfg
catUnset=false
fi
echo $(OptToIni "$option") = $value >> $cfg
fi
fi
done;
IFS=$OIFS
}
function CatToSnap {
ret=$(echo "$1" \
| grep -oP '[A-Za-z0-9._]+' \
| sed 's|\.|-|g' \
| sed 's|_|99|g')
echo $ret
}
function OptToSnap {
ret=$(echo "$1" \
| grep -oP '^[A-Z_]+' \
| tr '[:upper:]' '[:lower:]' \
| sed 's|_|-|g')
echo $ret
}
function ValToSnap {
ret=$(echo "$1" \
| grep -oP '=.*$' \
| sed 's_^= __g' \
| sed 's_^=__g' \
| sed "s|SNAP_DIR_DATA|$SDATA|g" \
| sed "s|SNAP_DIR_COMMON|$SCOMMON|g" \
| sed 's|{}||g')
echo $ret
}
function CatToIni {
ret=$(echo "$1" \
| sed 's|-|.|g' \
| sed 's|\ |_|g' \
| sed 's|99|_|g')
echo $ret
}
function OptToIni {
ret=$(echo "$1" \
| tr '[:lower:]' '[:upper:]' \
| sed 's|-|_|g')
echo $ret
}
[[ "$1" = "configure" ]] \
&& toIni \
&& exit 0
[[ "$1" = "install" ]] \
&& echo "Initial Configuration..." \
&& mkdir -p $SNAP_COMMON/conf \
&& toSnap init \
&& toSnap snap \
&& toIni sample \
&& exit 0
[[ "$1" = "save" ]] \
&& echo "Saving current config..." \
&& toSnap \
&& exit 0

@ -0,0 +1,23 @@
#!/bin/bash
if ! env | grep -q root; then
echo "
+----------------------------------------+
| You are not running gitea as root. |
| This is required for the snap package. |
| Please re-run as root. |
+----------------------------------------+
"
$SNAP/gitea/gitea --help
exit 1
fi
# Set usernames for gitea
export USERNAME=root
export USER=root
export GITEA_WORK_DIR=$(snapctl get gitea.snap.workdir)
export GITEA_CUSTOM=$(snapctl get gitea.snap.custom)
$SNAP/bin/gconfig save
cd $SNAP/gitea; ./gitea $@

@ -0,0 +1,3 @@
#!/bin/bash
$SNAP/bin/gconfig configure

@ -0,0 +1,45 @@
#!/bin/bash
export SDATA=$(echo $SNAP_DATA | sed "s|$SNAP_REVISION|current|")
export SCOMMON="$SNAP_COMMON"
export isRoot=`true`
snapctl set gitea.snap.workdir="$SDATA/custom"
snapctl set gitea.snap.custom="$SCOMMON"
function mkDirCommon(){
for dir in $@; do
mkdir -p "$SCOMMON/$dir"
done
}
function mkdirData(){
for dir in $@; do
mkdir -p "$SDATA/$dir"
if [ -d $SNAP/$dir ]; then
cp -r --preserve=mode \
$SNAP/$dir/* \
$SNAP/$dir/.[a-zA-Z0-9-]* \
$SDATA/$dir/ 2> $SCOMMON/log/snap-mkdirData.log
fi
done
}
mkDirCommon pictures \
repositories \
attachments \
data \
log
mkdirData certs \
sshkeytest \
custom/conf \
static/templates \
static/scripts \
static/public
[[ -f $SNAP_COMMON/conf/app.ini ]] || $SNAP/bin/gconfig install
# Configure Git to use the right templates
mkdir -p $SDATA/git/
cp -r --preserve=mode $SNAP/usr/share/git-core/templates $SDATA/git/
$SNAP/usr/bin/git config --global init.templateDir $SDATA/git/templates/

@ -0,0 +1,121 @@
name: gitea
summary: Gitea - A painless self-hosted Git service
description: |
The goal of this project is to make the easiest, fastest, and most painless
way of setting up a self-hosted Git service. With Go, this can be done with
an independent binary distribution across ALL platforms that Go supports,
including Linux, Mac OS X, Windows and ARM.
type: app
icon: public/img/gitea-lg.png
confinement: strict
grade: stable
version: 'git'
apps:
gitea:
command: bin/gitea
plugs: [network, network-bind]
web:
command: bin/gitea web
daemon: simple
plugs: [network, network-bind]
serv:
command: bin/gitea serv
plugs: [network, network-bind]
admin:
command: bin/gitea admin
plugs: [network, network-bind]
cert:
command: bin/gitea cert
hook:
command: bin/gitea hook
plugs: [network, network-bind]
dump:
command: bin/gitea dump
plugs: [home]
help:
command: bin/gitea --help
version:
command: bin/gitea --version
sqlite:
command: usr/bin/sqlite3
parts:
go:
source-tag: go1.8.3
prime:
- -*
gitea:
plugin: nil
source: .
source-type: git
after: [ go ]
stage-packages: [ git, sqlite3, openssh-client ]
build-packages: [ libpam0g-dev, libsqlite3-dev]
prepare: |
export PATH=$SNAPCRAFT_PART_INSTALL/../../go/install/bin:$PATH
export GOPATH=$SNAPCRAFT_PART_INSTALL/../go
export bld=$SNAPCRAFT_PART_INSTALL/../build
export src=$SNAPCRAFT_PART_INSTALL/../src
mkdir -p $GOPATH/src/code.gitea.io/gitea
cp -r $src/* $GOPATH/src/code.gitea.io/gitea
build: |
export PATH=$SNAPCRAFT_PART_INSTALL/../go/bin/:$SNAPCRAFT_PART_INSTALL/../../go/install/bin:$PATH
export GOPATH=$SNAPCRAFT_PART_INSTALL/../go
go get -u github.com/jteeuwen/go-bindata/...
cd $GOPATH/src/code.gitea.io/gitea
TAGS="bindata sqlite pam cert" make generate build
install: |
# Set Convenience Variables
src=$SNAPCRAFT_PART_INSTALL/../go/src/code.gitea.io/gitea
giteaBase=$SNAPCRAFT_PART_INSTALL/gitea
scurrent=/var/snap/$SNAPCRAFT_PROJECT_NAME/current
scommon=/var/snap/$SNAPCRAFT_PROJECT_NAME/common
# Copy build artifact and necessary files
mkdir -p $giteaBase/conf
# Workaround for gitea ignoring APP_DATA_PATH in app.ini after snap update.
ln -s $scurrent/custom $giteaBase/custom
ln -s $scommon/data $giteaBase/data
# Workaround for cmd/certs not knowing how to put files somewhere else
ln -s $scurrent/cert.pem $giteaBase/cert.pem
ln -s $scurrent/key.pem $giteaBase/key.pem
# Copy static content
mkdir -p $SNAPCRAFT_PART_INSTALL/static
cp $src/gitea $giteaBase/
cp -r $src/LICENSE \
$src/templates \
$src/public \
$src/scripts \
$SNAPCRAFT_PART_INSTALL/static/
cp -r $src/README.md \
$src/LICENSE \
$src/custom \
$SNAPCRAFT_PART_INSTALL/
prime:
- -etc
- -usr/lib/systemd
- -usr/lib/gcc
- -usr/lib/sasl2
- -usr/lib/x86_64-linux-gnu/krb5
- -usr/share/apport
- -usr/share/bash-completion
- -usr/share/doc
- -usr/share/git-core/contrib
- -usr/share/man
- -usr/share/upstart
- -var
helpers:
plugin: dump
source: snap/helpers
organize:
simple_launcher.sh: bin/gitea
app.ini: gitea/snapApp.ini
configuration.sh: bin/gconfig
prime:
- bin/gitea
- bin/gconfig
- gitea/snapApp.ini

@ -114,6 +114,8 @@
<dd><i class="fa fa{{if .Service.RegisterEmailConfirm}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.disable_register"}}</dt>
<dd><i class="fa fa{{if .Service.DisableRegistration}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.allow_only_external_registration"}}</dt>
<dd><i class="fa fa{{if .Service.AllowOnlyExternalRegistration}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.show_registration_button"}}</dt>
<dd><i class="fa fa{{if .Service.ShowRegistrationButton}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.enable_openid_signup"}}</dt>

@ -17,9 +17,13 @@
</div>
</div>
{{if .DescriptionHTML}}<p class="has-emoji">{{.DescriptionHTML}}</p>{{end}}
<div>
{{range .Topics}}<div class="ui green basic label topic">{{.}}</div>{{end}}
</div>
{{if .Topics }}
<div>
{{range .Topics}}
{{if ne . "" }}<div class="ui green basic label topic">{{.}}</div>{{end}}
{{end}}
</div>
{{end}}
<p class="time">{{$.i18n.Tr "org.repo_updated"}} {{TimeSinceUnix .UpdatedUnix $.i18n.Lang}}</p>
</div>
{{else}}

@ -200,6 +200,12 @@
<input name="disable_registration" type="checkbox" {{if .disable_registration}}checked{{end}}>
</div>
</div>
<div class="inline field">
<div class="ui checkbox" id="allow-only-external-registration">
<label class="poping up" data-content="{{.i18n.Tr "install.allow_only_external_registration_popup"}}"><strong>{{.i18n.Tr "install.allow_only_external_registration_popup"}}</strong></label>
<input name="allow_only_external_registration" type="checkbox" {{if .allow_only_external_registration}}checked{{end}}>
</div>
</div>
<div class="inline field">
<div class="ui checkbox" id="enable-openid-signup">
<label class="poping up" data-content="{{.i18n.Tr "install.openid_signup_popup"}}"><strong>{{.i18n.Tr "install.openid_signup"}}</strong></label>

@ -48,7 +48,7 @@
<div class="ui tabs container">
<div class="ui tabular stackable menu navbar">
{{if .Repository.UnitEnabled $.UnitTypeCode}}
<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/branch/{{.BranchName}}{{end}}">
<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL}}{{end}}">
<i class="octicon octicon-code"></i> {{.i18n.Tr "repo.code"}}
</a>
{{end}}

@ -156,7 +156,7 @@
</div>
</div>
<!-- Assignee -->
<!-- Assignees -->
<div class="ui {{if not .Assignees}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.issues.action_assignee"}}
@ -220,9 +220,9 @@
<span class="octicon octicon-calendar"></span>
<span{{if .IsOverdue}} class="overdue"{{end}}>{{.DeadlineUnix.FormatShort}}</span>
{{end}}
{{if .Assignee}}
<a class="ui right assignee poping up" href="{{.Assignee.HomeLink}}" data-content="{{.Assignee.Name}}" data-variation="inverted" data-position="left center">
<img class="ui avatar image" src="{{.Assignee.RelAvatarLink}}">
{{range .Assignees}}
<a class="ui right assignee poping up" href="{{.HomeLink}}" data-content="{{.Name}}" data-variation="inverted" data-position="left center">
<img class="ui avatar image" src="{{.RelAvatarLink}}">
</a>
{{end}}
</p>

@ -97,27 +97,56 @@
<div class="ui divider"></div>
<input id="assignee_id" name="assignee_id" type="hidden" value="{{.assignee_id}}">
<input id="assignee_ids" name="assignee_ids" type="hidden" value="{{.assignee_ids}}">
<div class="ui {{if not .Assignees}}disabled{{end}} floating jump select-assignees dropdown">
<span class="text">
<strong>{{.i18n.Tr "repo.issues.new.assignees"}}</strong>
<span class="octicon octicon-gear"></span>
</span>
<div class="filter menu" data-id="#assignee_ids">
<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_assignees"}}</div>
{{range .Assignees}}
<a class="item" href="#" data-id="{{.ID}}" data-id-selector="#assignee_{{.ID}}">
<span class="octicon"></span>
<span class="text">
<img class="ui avatar image" src="{{.RelAvatarLink}}"> {{.Name}}
</span>
</a>
{{end}}
</div>
</div>
<div class="ui assignees list">
<span class="no-select item {{if .HasSelectedLabel}}hide{{end}}">
{{.i18n.Tr "repo.issues.new.no_assignees"}}
</span>
{{range .Assignees}}
<a style="padding: 5px;color:rgba(0, 0, 0, 0.87);" class="hide item" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}">
<img class="ui avatar image" src="{{.RelAvatarLink}}" style="vertical-align: middle;">&nbsp;{{.Name}}
</a>
{{end}}
</div>
<!-- input id="assignee_ids" name="assignee_ids" type="hidden" value="{{.assignee_id}}">
<div class="ui {{if not .Assignees}}disabled{{end}} floating jump select-assignee dropdown">
<span class="text">
<strong>{{.i18n.Tr "repo.issues.new.assignee"}}</strong>
<strong>{{.i18n.Tr "repo.issues.new.assignees"}}</strong>
<span class="octicon octicon-gear"></span>
</span>
<div class="menu">
<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_assignee"}}</div>
<div class="filter menu">
<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_assignees"}}</div>
{{range .Assignees}}
<div class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?assignee={{.ID}}" data-avatar="{{.RelAvatarLink}}"><img src="{{.RelAvatarLink}}"> {{.Name}}</div>
{{end}}
</div>
</div>
<div class="ui select-assignee list">
<span class="no-select item {{if .Assignee}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_assignee"}}</span>
<span class="no-select item {{if .Assignee}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_assignees"}}</span>
<div class="selected">
{{if .Assignee}}
<a class="item" href="{{.RepoLink}}/issues?assignee={{.Assignee.ID}}"><img class="ui avatar image" src="{{.Assignee.RelAvatarLink}}"> {{.Assignee.Name}}</a>
{{end}}
</div>
</div>
</div>-->
</div>
</div>
</form>

@ -118,15 +118,29 @@
{{else if eq .Type 9}}
<div class="event">
<span class="octicon octicon-primitive-dot"></span>
{{if gt .AssigneeID 0}}{{if eq .Poster.ID .AssigneeID}}<a class="ui avatar image" href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}">
</a> <span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a> {{$.i18n.Tr "repo.issues.self_assign_at" $createdStr | Safe}} </span>
{{else}}<a class="ui avatar image" href="{{.Assignee.HomeLink}}">
<img src="{{.Assignee.RelAvatarLink}}">
</a><span class="text grey"><a href="{{.Assignee.HomeLink}}">{{.Assignee.Name}}</a> {{$.i18n.Tr "repo.issues.add_assignee_at" .Poster.Name $createdStr | Safe}} </span>{{end}}{{else if gt .OldAssigneeID 0}}
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}">
</a> <span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a> {{$.i18n.Tr "repo.issues.remove_assignee_at" $createdStr | Safe}} </span>{{end}}
{{if gt .AssigneeID 0}}
{{if .RemovedAssignee}}
<a class="ui avatar image" href="{{.Assignee.HomeLink}}">
<img src="{{.Assignee.RelAvatarLink}}">
</a>
<span class="text grey">
<a href="{{.Assignee.HomeLink}}">{{.Assignee.Name}}</a>
{{$.i18n.Tr "repo.issues.remove_assignee_at" $createdStr | Safe}}
</span>
{{else}}
<a class="ui avatar image" href="{{.Assignee.HomeLink}}">
<img src="{{.Assignee.RelAvatarLink}}">
</a>
<span class="text grey">
<a href="{{.Assignee.HomeLink}}">{{.Assignee.Name}}</a>
{{if eq .Poster.ID .AssigneeID}}
{{$.i18n.Tr "repo.issues.self_assign_at" $createdStr | Safe}}
{{else}}
{{$.i18n.Tr "repo.issues.add_assignee_at" .Poster.Name $createdStr | Safe}}
{{end}}
</span>
{{end}}
{{end}}
</div>
{{else if eq .Type 10}}
<div class="event">

@ -68,23 +68,40 @@
<div class="ui divider"></div>
<input id="assignee_id" name="assignee_id" type="hidden" value="{{.assignee_id}}">
<div class="ui {{if not .IsRepositoryWriter}}disabled{{end}} floating jump select-assignee dropdown">
<div class="ui {{if not .IsRepositoryWriter}}disabled{{end}} floating jump select-assignees-modify dropdown">
<span class="text">
<strong>{{.i18n.Tr "repo.issues.new.assignee"}}</strong>
<strong>{{.i18n.Tr "repo.issues.new.assignees"}}</strong>
<span class="octicon octicon-gear"></span>
</span>
<div class="menu" data-action="update" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/assignee">
<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_assignee"}}</div>
<div class="filter menu" data-action="" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/assignee">
<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_assignees"}}</div>
{{range .Assignees}}
<div class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?assignee={{.ID}}" data-avatar="{{.RelAvatarLink}}"><img src="{{.RelAvatarLink}}"> {{.Name}}</div>
{{$AssigneeID := .ID}}
<a class="item{{range $.Issue.Assignees}}
{{if eq .ID $AssigneeID}}
checked
{{end}}
{{end}}" href="#" data-id="{{.ID}}" data-id-selector="#assignee_{{.ID}}">
<span class="octicon{{range $.Issue.Assignees}}
{{if eq .ID $AssigneeID}}
octicon-check
{{end}}
{{end}}"></span>
<span class="text">
<img class="ui avatar image" src="{{.RelAvatarLink}}"> {{.Name}}
</span>
</a>
{{end}}
</div>
</div>
<div class="ui select-assignee list">
<span class="no-select item {{if .Issue.Assignee}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_assignee"}}</span>
<div class="ui assignees list">
<span class="no-select item {{if .Issue.Assignees}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_assignees"}}</span>
<div class="selected">
{{if .Issue.Assignee}}
<a class="item" href="{{$.RepoLink}}/issues?assignee={{.Issue.Assignee.ID}}"><img class="ui avatar image" src="{{.Issue.Assignee.RelAvatarLink}}"> {{.Issue.Assignee.Name}}</a>
{{range .Issue.Assignees}}
<div class="item" style="margin-bottom: 10px;">
<a href="{{$.RepoLink}}/issues?assignee={{.ID}}"><img class="ui avatar image" src="{{.RelAvatarLink}}">&nbsp;{{.Name}}</a>
</div>
{{end}}
</div>
</div>
@ -194,7 +211,7 @@
<div class="ui divider"></div>
<span class="text"><strong>{{.i18n.Tr "repo.issues.due_date"}}</strong></span>
{{if gt .Issue.DeadlineUnix 0}}
{{if ne .Issue.DeadlineUnix 0}}
<p>
<span class="octicon octicon-calendar"></span>
{{.Issue.DeadlineUnix.FormatShort}}
@ -212,12 +229,12 @@
{{end}}
{{if and .IsSigned .IsRepositoryWriter}}
<form method="POST"{{if gt .Issue.DeadlineUnix 0}}style="display: none;"{{end}}} id="add_deadline_form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/deadline/update" class="ui action input fluid">
<form method="POST" {{if ne .Issue.DeadlineUnix 0}}style="display: none;"{{end}} id="add_deadline_form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/deadline/update" class="ui action input fluid">
{{$.CsrfTokenHtml}}
<div class="ui fluid action input">
<input required placeholder="{{.i18n.Tr "repo.issues.due_date_form"}}" {{if gt .Issue.DeadlineUnix 0}}value="{{.Issue.DeadlineUnix.Format "2006-01-02"}}"{{end}} type="date" name="date" style="min-width: 13.9rem;border-radius: 4px 0 0 4px;border-right: 0;white-space: nowrap;">
<input required placeholder="{{.i18n.Tr "repo.issues.due_date_form"}}" {{if ne .Issue.DeadlineUnix 0 }}value="{{.Issue.DeadlineUnix.Format "2006-01-02"}}"{{end}} type="date" name="date" style="min-width: 13.9rem;border-radius: 4px 0 0 4px;border-right: 0;white-space: nowrap;">
<button class="ui green icon button">
{{if gt .Issue.DeadlineUnix 0}}
{{if ne .Issue.DeadlineUnix 0}}
<i class="edit icon"></i>
{{else}}
<i class="plus icon"></i>

@ -70,7 +70,7 @@ type Store struct {
}
type xormSession struct {
ID string `xorm:"VARCHAR(400) PK NAME 'id'"`
ID string `xorm:"VARCHAR(100) PK NAME 'id'"`
Data string `xorm:"TEXT"`
CreatedUnix util.TimeStamp `xorm:"created"`
UpdatedUnix util.TimeStamp `xorm:"updated"`

@ -648,10 +648,10 @@
"revisionTime": "2016-10-16T15:41:25Z"
},
{
"checksumSHA1": "/X7eCdN7MX8zgCjA9s0ktzgTPlA=",
"checksumSHA1": "1zVWfGXRsQi0EuZydyBHgkhl7tU=",
"path": "github.com/lafriks/xormstore",
"revision": "3a80a383a04b29ec2e1bf61279dd948aa809335b",
"revisionTime": "2018-04-09T10:45:24Z"
"revision": "9cab149ea91875cf056211bd6ef82379fce9cb67",
"revisionTime": "2018-05-10T21:06:47Z"
},
{
"checksumSHA1": "Vxvfs8mukr9GOLSuGIPU4ODyOZc=",

Loading…
Cancel
Save