diff --git a/.drone.yml b/.drone.yml
index 6ab40a39d..8e571640b 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -4,7 +4,7 @@ workspace:
clone:
git:
- image: plugins/git:1
+ image: plugins/git:next
depth: 50
tags: true
diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample
index ef88e5c32..f823f68e4 100644
--- a/custom/conf/app.ini.sample
+++ b/custom/conf/app.ini.sample
@@ -601,9 +601,9 @@ ko-KR = ko
[U2F]
; Two Factor authentication with security keys
; https://developers.yubico.com/U2F/App_ID.html
-APP_ID = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s
+APP_ID = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
; Comma seperated list of truisted facets
-TRUSTED_FACETS = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s
+TRUSTED_FACETS = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
; Extension mapping to highlight class
; e.g. .toml=ini
diff --git a/models/issue_watch.go b/models/issue_watch.go
index 69e218af0..3e7d24821 100644
--- a/models/issue_watch.go
+++ b/models/issue_watch.go
@@ -71,3 +71,15 @@ func getIssueWatchers(e Engine, issueID int64) (watches []*IssueWatch, err error
Find(&watches)
return
}
+
+func removeIssueWatchersByRepoID(e Engine, userID int64, repoID int64) error {
+ iw := &IssueWatch{
+ IsWatching: false,
+ }
+ _, err := e.
+ Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID).
+ Cols("is_watching", "updated_unix").
+ Where("`issue_watch`.user_id = ?", userID).
+ Update(iw)
+ return err
+}
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 1300065ab..2537e5712 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -186,6 +186,8 @@ var migrations = []Migration{
NewMigration("add u2f", addU2FReg),
// v66 -> v67
NewMigration("add login source id column for public_key table", addLoginSourceIDToPublicKeyTable),
+ // v67 -> v68
+ NewMigration("remove stale watches", removeStaleWatches),
}
// Migrate database to current version
diff --git a/models/migrations/v67.go b/models/migrations/v67.go
new file mode 100644
index 000000000..278221919
--- /dev/null
+++ b/models/migrations/v67.go
@@ -0,0 +1,158 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/go-xorm/xorm"
+)
+
+func removeStaleWatches(x *xorm.Engine) error {
+ type Watch struct {
+ ID int64
+ UserID int64
+ RepoID int64
+ }
+
+ type IssueWatch struct {
+ ID int64
+ UserID int64
+ RepoID int64
+ IsWatching bool
+ }
+
+ type Repository struct {
+ ID int64
+ IsPrivate bool
+ OwnerID int64
+ }
+
+ type Access struct {
+ UserID int64
+ RepoID int64
+ Mode int
+ }
+
+ const (
+ // AccessModeNone no access
+ AccessModeNone int = iota // 0
+ // AccessModeRead read access
+ AccessModeRead // 1
+ )
+
+ accessLevel := func(userID int64, repo *Repository) (int, error) {
+ mode := AccessModeNone
+ if !repo.IsPrivate {
+ mode = AccessModeRead
+ }
+
+ if userID == 0 {
+ return mode, nil
+ }
+
+ if userID == repo.OwnerID {
+ return 4, nil
+ }
+
+ a := &Access{UserID: userID, RepoID: repo.ID}
+ if has, err := x.Get(a); !has || err != nil {
+ return mode, err
+ }
+ return a.Mode, nil
+ }
+
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+
+ repoCache := make(map[int64]*Repository)
+ err := x.BufferSize(setting.IterateBufferSize).Iterate(new(Watch),
+ func(idx int, bean interface{}) error {
+ watch := bean.(*Watch)
+
+ repo := repoCache[watch.RepoID]
+ if repo == nil {
+ repo = &Repository{
+ ID: watch.RepoID,
+ }
+ if _, err := x.Get(repo); err != nil {
+ return err
+ }
+ repoCache[watch.RepoID] = repo
+ }
+
+ // Remove watches from now unaccessible repositories
+ mode, err := accessLevel(watch.UserID, repo)
+ if err != nil {
+ return err
+ }
+ has := AccessModeRead <= mode
+ if has {
+ return nil
+ }
+
+ if _, err = sess.Delete(&Watch{0, watch.UserID, repo.ID}); err != nil {
+ return err
+ }
+ _, err = sess.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repo.ID)
+
+ return err
+ })
+ if err != nil {
+ return err
+ }
+
+ repoCache = make(map[int64]*Repository)
+ err = x.BufferSize(setting.IterateBufferSize).
+ Distinct("issue_watch.user_id", "issue.repo_id").
+ Join("INNER", "issue", "issue_watch.issue_id = issue.id").
+ Where("issue_watch.is_watching = ?", true).
+ Iterate(new(IssueWatch),
+ func(idx int, bean interface{}) error {
+ watch := bean.(*IssueWatch)
+
+ repo := repoCache[watch.RepoID]
+ if repo == nil {
+ repo = &Repository{
+ ID: watch.RepoID,
+ }
+ if _, err := x.Get(repo); err != nil {
+ return err
+ }
+ repoCache[watch.RepoID] = repo
+ }
+
+ // Remove issue watches from now unaccssible repositories
+ mode, err := accessLevel(watch.UserID, repo)
+ if err != nil {
+ return err
+ }
+ has := AccessModeRead <= mode
+ if has {
+ return nil
+ }
+
+ iw := &IssueWatch{
+ IsWatching: false,
+ }
+
+ _, err = sess.
+ Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", watch.RepoID).
+ Cols("is_watching", "updated_unix").
+ Where("`issue_watch`.user_id = ?", watch.UserID).
+ Update(iw)
+
+ return err
+
+ })
+ if err != nil {
+ return err
+ }
+
+ return sess.Commit()
+}
diff --git a/models/models.go b/models/models.go
index ddf784dee..5743f1862 100644
--- a/models/models.go
+++ b/models/models.go
@@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
@@ -184,6 +185,18 @@ func parsePostgreSQLHostPort(info string) (string, string) {
return host, port
}
+func getPostgreSQLConnectionString(DBHost, DBUser, DBPasswd, DBName, DBParam, DBSSLMode string) (connStr string) {
+ host, port := parsePostgreSQLHostPort(DBHost)
+ if host[0] == '/' { // looks like a unix socket
+ connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s",
+ url.PathEscape(DBUser), url.PathEscape(DBPasswd), port, DBName, DBParam, DBSSLMode, host)
+ } else {
+ connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s",
+ url.PathEscape(DBUser), url.PathEscape(DBPasswd), host, port, DBName, DBParam, DBSSLMode)
+ }
+ return
+}
+
func parseMSSQLHostPort(info string) (string, string) {
host, port := "127.0.0.1", "1433"
if strings.Contains(info, ":") {
@@ -214,14 +227,7 @@ func getEngine() (*xorm.Engine, error) {
DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param)
}
case "postgres":
- host, port := parsePostgreSQLHostPort(DbCfg.Host)
- if host[0] == '/' { // looks like a unix socket
- connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s",
- url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), port, DbCfg.Name, Param, DbCfg.SSLMode, host)
- } else {
- connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s",
- url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), host, port, DbCfg.Name, Param, DbCfg.SSLMode)
- }
+ connStr = getPostgreSQLConnectionString(DbCfg.Host, DbCfg.User, DbCfg.Passwd, DbCfg.Name, Param, DbCfg.SSLMode)
case "mssql":
host, port := parseMSSQLHostPort(DbCfg.Host)
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, DbCfg.Name, DbCfg.User, DbCfg.Passwd)
diff --git a/models/models_test.go b/models/models_test.go
index 649b1e02e..7016fdb4b 100644
--- a/models/models_test.go
+++ b/models/models_test.go
@@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
@@ -53,3 +54,42 @@ func Test_parsePostgreSQLHostPort(t *testing.T) {
assert.Equal(t, test.Port, port)
}
}
+
+func Test_getPostgreSQLConnectionString(t *testing.T) {
+ tests := []struct {
+ Host string
+ Port string
+ User string
+ Passwd string
+ Name string
+ Param string
+ SSLMode string
+ Output string
+ }{
+ {
+ Host: "/tmp/pg.sock",
+ Port: "4321",
+ User: "testuser",
+ Passwd: "space space !#$%^^%^```-=?=",
+ Name: "gitea",
+ Param: "",
+ SSLMode: "false",
+ Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/giteasslmode=false&host=/tmp/pg.sock",
+ },
+ {
+ Host: "localhost",
+ Port: "1234",
+ User: "pgsqlusername",
+ Passwd: "I love Gitea!",
+ Name: "gitea",
+ Param: "",
+ SSLMode: "true",
+ Output: "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/giteasslmode=true",
+ },
+ }
+
+ for _, test := range tests {
+ connStr := getPostgreSQLConnectionString(test.Host, test.User, test.Passwd, test.Name, test.Param, test.SSLMode)
+ assert.Equal(t, test.Output, connStr)
+ }
+}
diff --git a/models/org_team.go b/models/org_team.go
index 9d8a03141..5ea6e76cd 100644
--- a/models/org_team.go
+++ b/models/org_team.go
@@ -178,6 +178,11 @@ func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (e
if err = watchRepo(e, teamUser.UID, repo.ID, false); err != nil {
return err
}
+
+ // Remove all IssueWatches a user has subscribed to in the repositories
+ if err := removeIssueWatchersByRepoID(e, teamUser.UID, repo.ID); err != nil {
+ return err
+ }
}
return nil
@@ -374,11 +379,34 @@ func DeleteTeam(t *Team) error {
return err
}
+ if err := t.getMembers(sess); err != nil {
+ return err
+ }
+
// Delete all accesses.
for _, repo := range t.Repos {
if err := repo.recalculateTeamAccesses(sess, t.ID); err != nil {
return err
}
+
+ // Remove watches from all users and now unaccessible repos
+ for _, user := range t.Members {
+ has, err := hasAccess(sess, user.ID, repo, AccessModeRead)
+ if err != nil {
+ return err
+ } else if has {
+ continue
+ }
+
+ if err = watchRepo(sess, user.ID, repo.ID, false); err != nil {
+ return err
+ }
+
+ // Remove all IssueWatches a user has subscribed to in the repositories
+ if err = removeIssueWatchersByRepoID(sess, user.ID, repo.ID); err != nil {
+ return err
+ }
+ }
}
// Delete team-repo
@@ -518,6 +546,10 @@ func AddTeamMember(team *Team, userID int64) error {
if err := repo.recalculateTeamAccesses(sess, 0); err != nil {
return err
}
+
+ if err = watchRepo(sess, userID, repo.ID, true); err != nil {
+ return err
+ }
}
return sess.Commit()
@@ -558,6 +590,23 @@ func removeTeamMember(e *xorm.Session, team *Team, userID int64) error {
if err := repo.recalculateTeamAccesses(e, 0); err != nil {
return err
}
+
+ // Remove watches from now unaccessible
+ has, err := hasAccess(e, userID, repo, AccessModeRead)
+ if err != nil {
+ return err
+ } else if has {
+ continue
+ }
+
+ if err = watchRepo(e, userID, repo.ID, false); err != nil {
+ return err
+ }
+
+ // Remove all IssueWatches a user has subscribed to in the repositories
+ if err := removeIssueWatchersByRepoID(e, userID, repo.ID); err != nil {
+ return err
+ }
}
// Check if the user is a member of any team in the organization.
diff --git a/models/repo.go b/models/repo.go
index f4923cf4a..7f2be502a 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -1851,6 +1851,9 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
if _, err = sess.In("issue_id", issueIDs).Delete(&Reaction{}); err != nil {
return err
}
+ if _, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{}); err != nil {
+ return err
+ }
attachments := make([]*Attachment, 0, 5)
if err = sess.
diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go
index 0448149e6..9d2935d58 100644
--- a/models/repo_collaboration.go
+++ b/models/repo_collaboration.go
@@ -172,5 +172,14 @@ func (repo *Repository) DeleteCollaboration(uid int64) (err error) {
return err
}
+ if err = watchRepo(sess, uid, repo.ID, false); err != nil {
+ return err
+ }
+
+ // Remove all IssueWatches a user has subscribed to in the repository
+ if err := removeIssueWatchersByRepoID(sess, uid, repo.ID); err != nil {
+ return err
+ }
+
return sess.Commit()
}
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index d618d2c9a..4b1b85f92 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -588,7 +588,7 @@ editor.edit_file=Datei bearbeiten
editor.preview_changes=Vorschau der Änderungen
editor.cannot_edit_non_text_files=Binärdateien können nicht im Webinterface bearbeitet werden.
editor.edit_this_file=Datei bearbeiten
-editor.must_be_on_a_branch=Du musst dich in einer Branch befinden, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen.
+editor.must_be_on_a_branch=Du musst dich in einem Branch befinden, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen.
editor.fork_before_edit=Du musst dieses Repository forken, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen.
editor.delete_this_file=Datei löschen
editor.must_have_write_access=Du benötigst Schreibzugriff, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen.
@@ -1085,16 +1085,16 @@ settings.protect_whitelist_search_users=Benutzer suchen…
settings.protect_whitelist_teams=Teams, die pushen dürfen:
settings.protect_whitelist_search_teams=Suche nach Teams…
settings.protect_merge_whitelist_committers=Merge-Whitelist aktivieren
-settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist Pull-Requests in diese Branch zu mergen.
+settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist Pull-Requests in diesen Branch zu mergen.
settings.protect_merge_whitelist_users=Nutzer, die mergen dürfen:
settings.protect_merge_whitelist_teams=Teams, die mergen dürfen:
settings.add_protected_branch=Schutz aktivieren
settings.delete_protected_branch=Schutz deaktivieren
settings.update_protect_branch_success=Branch-Schutz für den Branch „%s“ wurde geändert.
settings.remove_protected_branch_success=Branch-Schutz für den Branch „%s“ wurde deaktiviert.
-settings.protected_branch_deletion=Brach-Schutz deaktivieren
+settings.protected_branch_deletion=Branch-Schutz deaktivieren
settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren?
-settings.default_branch_desc=Wähle eine Standardbranch für Pull-Requests und Code-Commits:
+settings.default_branch_desc=Wähle einen Standardbranch für Pull-Requests und Code-Commits:
settings.choose_branch=Wähle einen Branch …
settings.no_protected_branch=Es gibt keine geschützten Branches.
@@ -1238,7 +1238,7 @@ teams.update_settings=Einstellungen aktualisieren
teams.delete_team=Team löschen
teams.add_team_member=Teammitglied hinzufügen
teams.delete_team_title=Team löschen
-teams.delete_team_desc=Das Löschen eines Teams wiederruft den Repository-Zugriff für seine Mitglieder. Fortfahren?
+teams.delete_team_desc=Das Löschen eines Teams widerruft den Repository-Zugriff für seine Mitglieder. Fortfahren?
teams.delete_team_success=Das Team wurde gelöscht.
teams.read_permission_desc=Dieses Team hat Lesezugriff: Mitglieder können Team-Repositories einsehen und klonen.
teams.write_permission_desc=Dieses Team hat Schreibzugriff: Mitglieder können Team-Repositories einsehen und darauf pushen.
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index b3e8a2fe0..8f90f3a3f 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -204,7 +204,7 @@ non_local_account=Нелокальні акаунти не можуть змін
verify=Підтвердити
scratch_code=Одноразовий пароль
use_scratch_code=Використовувати одноразовий пароль
-twofa_scratch_used=Ви використовували одноразовий пароль. Ви були перенаправлені на сторінку налаштувань для генерації нового коду або відключення двуфакторной аутентифікації.
+twofa_scratch_used=Ви використовували одноразовий пароль. Ви були перенаправлені на сторінку налаштувань для генерації нового коду або відключення двуфакторної автентифікації.
twofa_passcode_incorrect=Ваш пароль є невірним. Якщо ви втратили пристрій, використовуйте ваш одноразовий пароль.
twofa_scratch_token_incorrect=Невірний одноразовий пароль.
login_userpass=Увійти
@@ -399,20 +399,25 @@ generate_token=Згенерувати токен
delete_token=Видалити
access_token_deletion=Видалити токен доступу
-twofa_desc=Двофакторна аутентифікація підвищує безпеку вашого облікового запису.
+twofa_desc=Двофакторна автентифікація підвищує безпеку вашого облікового запису.
twofa_is_enrolled=Ваш обліковий запис на даний час використовує двофакторну автентифікацію.
twofa_disable=Вимкнути двофакторну автентифікацію
+twofa_scratch_token_regenerate=Перестворити токен одноразового пароля
twofa_enroll=Увімкнути двофакторну автентифікацію
+twofa_disable_note=При необхідності можна відключити двофакторну автентифікацію.
+regenerate_scratch_token_desc=Якщо ви втратили свій токен одноразового пароля або вже використовували його для входу, ви можете скинути його тут.
twofa_disabled=Двофакторна автентифікація вимкнена.
-scan_this_image=Проскануйте це зображення вашим додатком для двуфакторної аутентифікації:
+scan_this_image=Проскануйте це зображення вашим додатком для двуфакторної автентифікації:
or_enter_secret=Або введіть секрет: %s
passcode_invalid=Некоректний пароль. Спробуй ще раз.
+u2f_desc=Ключами безпеки є апаратні пристрої, що містять криптографічні ключі. Вони можуть використовуватися для двофакторної автентифікації. Ключ безпеки повинен підтримувати стандарт FIDO U2F.
u2f_register_key=Додати ключ безпеки
u2f_nickname=Псевдонім
u2f_delete_key=Видалити ключ безпеки
manage_account_links=Керування обліковими записами
+manage_account_links_desc=Ці зовнішні акаунти прив'язані до вашого аккаунту Gitea.
remove_account_link=Видалити облікові записи
orgs_none=Ви не є учасником будь-якої організації.
@@ -428,6 +433,7 @@ owner=Власник
repo_name=Назва репозиторію
visibility=Видимість
visiblity_helper=Зробити репозиторій приватним
+visiblity_fork_helper=(Зміна цього вплине на всі форки.)
clone_helper=Потрібна допомога у клонуванні? Відвідайте Допомогу.
fork_repo=Форкнути репозиторій
fork_from=Форк з
@@ -607,6 +613,7 @@ issues.filter_sort.leastcomment=Найменш коментовані
issues.filter_sort.moststars=Найбільш обраних
issues.filter_sort.feweststars=Найменш обраних
issues.filter_sort.mostforks=Найбільше форків
+issues.filter_sort.fewestforks=Найменше форків
issues.action_open=Відкрити
issues.action_close=Закрити
issues.action_label=Мітка
@@ -971,6 +978,7 @@ topic.done=Готово
[org]
org_name_holder=Назва організації
org_full_name_holder=Повна назва організації
+org_name_helper=Назва організації має бути простою та зрозумілою.
create_org=Створити організацію
repo_updated=Оновлено
people=Учасники
@@ -1133,7 +1141,7 @@ repos.forks=Форки
repos.issues=Проблеми
repos.size=Розмір
-auths.auth_manage_panel=Керування джерелом аутентифікації
+auths.auth_manage_panel=Керування джерелом автентифікації
auths.new=Додати джерело автентифікації
auths.name=Ім'я
auths.type=Тип
@@ -1170,8 +1178,8 @@ auths.oauth2_profileURL=URL профілю
auths.oauth2_emailURL=URL електронної пошти
auths.enable_auto_register=Увімкнути автоматичну реєстрацію
auths.tips=Поради
-auths.tips.oauth2.general=OAuth2 аутентифікація
-auths.tips.oauth2.general.tip=При додаванні нового OAuth2 провайдера, URL адреса переадресації по завершенні аутентифікації повинена виглядати так:/user/oauth2//callback
+auths.tips.oauth2.general=OAuth2 автентифікація
+auths.tips.oauth2.general.tip=При додаванні нового OAuth2 провайдера, URL адреса переадресації по завершенні автентифікації повинена виглядати так:/user/oauth2//callback
auths.tip.oauth2_provider=Постачальник OAuth2
auths.tip.dropbox=Додайте новий додаток на https://www.dropbox.com/developers/apps
auths.tip.facebook=Створіть новий додаток на https://developers.facebook.com/apps і додайте модуль "Facebook Login
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 658f133a2..4e59ed08e 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -213,6 +213,7 @@ send_reset_mail=单击此处(重新)发送您的密码重置邮件
reset_password=重置密码
invalid_code=此确认密钥无效或已过期。
reset_password_helper=单击此处重置密码
+password_too_short=密码长度不能少于 %d 位。
non_local_account=非本地帐户不能通过 Gitea 的 web 界面更改密码。
verify=验证
scratch_code=验证口令
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 211d8045a..7be39166d 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
@@ -165,7 +166,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
// "$ref": "#/responses/Issue"
var deadlineUnix util.TimeStamp
- if form.Deadline != nil {
+ if form.Deadline != nil && ctx.Repo.IsWriter() {
deadlineUnix = util.TimeStamp(form.Deadline.Unix())
}
@@ -178,15 +179,22 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
DeadlineUnix: deadlineUnix,
}
- // Get all assignee IDs
- assigneeIDs, err := models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
- if err != nil {
- if models.IsErrUserNotExist(err) {
- ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
- } else {
- ctx.Error(500, "AddAssigneeByName", err)
+ var assigneeIDs = make([]int64, 0)
+ var err error
+ if ctx.Repo.IsWriter() {
+ issue.MilestoneID = form.Milestone
+ assigneeIDs, err = models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
+ if err != nil {
+ if models.IsErrUserNotExist(err) {
+ ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
+ } else {
+ ctx.Error(500, "AddAssigneeByName", err)
+ }
+ return
}
- return
+ } else {
+ // setting labels is not allowed if user is not a writer
+ form.Labels = make([]int64, 0)
}
if err := models.NewIssue(ctx.Repo.Repository, issue, form.Labels, assigneeIDs, nil); err != nil {