Browse Source

Merge branch 'develop' of github.com:tjyang/gogs into develop

pull/2990/head
T.J. Yang 9 years ago
parent
commit
08cfc3d1bd
  1. 2
      README.md
  2. 3
      conf/license/ISC license
  3. 0
      conf/locale/locale_bg-BG.ini
  4. 12
      conf/locale/locale_de-DE.ini
  5. 2
      conf/locale/locale_es-ES.ini
  6. 0
      conf/locale/locale_fi-FI.ini
  7. 2
      conf/locale/locale_fr-FR.ini
  8. 24
      conf/locale/locale_it-IT.ini
  9. 6
      conf/locale/locale_ja-JP.ini
  10. 0
      conf/locale/locale_lv-LV.ini
  11. 82
      conf/locale/locale_nl-NL.ini
  12. 0
      conf/locale/locale_pl-PL.ini
  13. 0
      conf/locale/locale_pt-BR.ini
  14. 2
      conf/locale/locale_ru-RU.ini
  15. 0
      conf/locale/locale_zh-CN.ini
  16. 62
      conf/locale/locale_zh-HK.ini
  17. 26
      docker/README.md
  18. 2
      gogs.go
  19. 36
      models/issue.go
  20. 612
      models/org.go
  21. 618
      models/org_team.go
  22. 52
      modules/bindata/bindata.go
  23. 2
      modules/context/api.go
  24. 14
      modules/context/api_org.go
  25. 5
      modules/template/template.go
  26. 8
      public/js/gogs.js
  27. 49
      routers/api/v1/admin/org_repo.go
  28. 34
      routers/api/v1/admin/org_team.go
  29. 53
      routers/api/v1/api.go
  30. 12
      routers/api/v1/org/org.go
  31. 7
      routers/api/v1/org/team.go
  32. 2
      routers/repo/download.go
  33. 2
      routers/user/home.go
  34. 2
      templates/.VERSION
  35. 6
      templates/repo/issue/list.tmpl

2
README.md

@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true) ![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true)
##### Current version: 0.9.15 ##### Current version: 0.9.20
| Web | UI | Preview | | Web | UI | Preview |
|:-------------:|:-------:|:-------:| |:-------------:|:-------:|:-------:|

3
conf/license/ISC license

@ -1,6 +1,5 @@
ISC License: ISC License:
Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") Copyright (c) Year(s), Company or Person's Name
Copyright (c) 1995-2003 by Internet Software Consortium
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

0
conf/locale/locale_bg-BG.ini

12
conf/locale/locale_de-DE.ini

@ -326,7 +326,7 @@ delete_account=Konto löschen
delete_prompt=Diese Aktion wird Ihr Konto dauerhaft löschen und kann <strong>NICHT</strong> rückgängig gemacht werden! delete_prompt=Diese Aktion wird Ihr Konto dauerhaft löschen und kann <strong>NICHT</strong> rückgängig gemacht werden!
confirm_delete_account=Löschvorgang bestätigen confirm_delete_account=Löschvorgang bestätigen
delete_account_title=Konto löschen delete_account_title=Konto löschen
delete_account_desc=Sie sind dabei dieses Konto dauerhaft zu löschen, möchten Sie wirklich fortfahren? delete_account_desc=Sie sind dabei dieses Konto dauerhaft zu löschen. Möchten Sie wirklich fortfahren?
[repo] [repo]
owner=Besitzer owner=Besitzer
@ -342,7 +342,7 @@ fork_from=Fork von
fork_visiblity_helper=Die Sichtbarkeit von geforkten Repositories ist nicht veränderbar. fork_visiblity_helper=Die Sichtbarkeit von geforkten Repositories ist nicht veränderbar.
repo_desc=Beschreibung repo_desc=Beschreibung
repo_lang=Sprache repo_lang=Sprache
repo_lang_helper=.gitignore Dateien auswählen repo_lang_helper=Wählen Sie eine .gitignore-Datei aus
license=Lizenz license=Lizenz
license_helper=Wählen Sie eine Lizenz aus license_helper=Wählen Sie eine Lizenz aus
readme=Readme readme=Readme
@ -505,11 +505,11 @@ pulls.merged_title_desc=hat %[1]d Commits von <code>%[2]s</code> nach <code>%[3]
pulls.tab_conversation=Diskussion pulls.tab_conversation=Diskussion
pulls.tab_commits=Commits pulls.tab_commits=Commits
pulls.tab_files=Geänderte Dateien pulls.tab_files=Geänderte Dateien
pulls.reopen_to_merge=Bitte diesen Pull-Request wiedereröffnen, um die Merge-Operation auszuführen. pulls.reopen_to_merge=Bitte diesen Pull-Request wieder eröffnen, um die Merge-Operation auszuführen.
pulls.merged=Zusammengeführt pulls.merged=Zusammengeführt
pulls.has_merged=Dieser Pull-Request wurde erfolgreich zusammengeführt! pulls.has_merged=Dieser Pull-Request wurde erfolgreich zusammengeführt!
pulls.data_broken=Die Daten dieses Pull-Requests sind defekt, da Fork-Informationen gelöscht wurden. pulls.data_broken=Die Daten dieses Pull-Requests sind defekt, da Fork-Informationen gelöscht wurden.
pulls.is_checking=Die Konfliktprüfung ist in Arbeit. Bitte aktualisieren Sie die Seite in wenigen Momenten. pulls.is_checking=Die Konfliktprüfung läuft noch. Bitte aktualisieren Sie die Seite in wenigen Augenblicken.
pulls.can_auto_merge_desc=Dieser Pull-Request kann automatisch zusammengeführt werden. pulls.can_auto_merge_desc=Dieser Pull-Request kann automatisch zusammengeführt werden.
pulls.cannot_auto_merge_desc=Dieser Pull-Request kann nicht automatisch zusammengeführt werden, da es Konflikte gibt. pulls.cannot_auto_merge_desc=Dieser Pull-Request kann nicht automatisch zusammengeführt werden, da es Konflikte gibt.
pulls.cannot_auto_merge_helper=Bitte manuell zusammenführen, um die Konflikte zu lösen. pulls.cannot_auto_merge_helper=Bitte manuell zusammenführen, um die Konflikte zu lösen.
@ -938,7 +938,7 @@ auths.pam_service_name=PAM Dienstname
auths.enable_auto_register=Automatische Registrierung aktivieren auths.enable_auto_register=Automatische Registrierung aktivieren
auths.tips=Tipps auths.tips=Tipps
auths.edit=Authentifizierungseinstellungen bearbeiten auths.edit=Authentifizierungseinstellungen bearbeiten
auths.activated=Diese Authentifizierung ist aktiviert auths.activated=Diese Authentifizierung ist aktiv
auths.new_success=Neue Authentifizierung '%s' wurde erfolgreich hinzugefügt. auths.new_success=Neue Authentifizierung '%s' wurde erfolgreich hinzugefügt.
auths.update_success=Die Authentifizierungseinstellungen wurden erfolgreich aktualisiert. auths.update_success=Die Authentifizierungseinstellungen wurden erfolgreich aktualisiert.
auths.update=Authentifizierungseinstellungen aktualisieren auths.update=Authentifizierungseinstellungen aktualisieren
@ -946,7 +946,7 @@ auths.delete=Diese Authentifizierung löschen
auths.delete_auth_title=Authentifizierung löschen auths.delete_auth_title=Authentifizierung löschen
auths.delete_auth_desc=Diese Authentifizierung wird gelöscht. Möchten Sie fortfahren? auths.delete_auth_desc=Diese Authentifizierung wird gelöscht. Möchten Sie fortfahren?
auths.still_in_used=Diese Authentifizierung wird noch von einigen Benutzern verwendet. Bitte löschen Sie diese Benutzer oder ändern Sie deren Anmeldetyp. auths.still_in_used=Diese Authentifizierung wird noch von einigen Benutzern verwendet. Bitte löschen Sie diese Benutzer oder ändern Sie deren Anmeldetyp.
auths.deletion_success=Authentifizierung wurde erfolgreich entfernt! auths.deletion_success=Authentifizierung wurde erfolgreich gelöscht!
config.server_config=Serverkonfiguration config.server_config=Serverkonfiguration
config.app_name=Anwendungsname config.app_name=Anwendungsname

2
conf/locale/locale_es-ES.ini

@ -384,7 +384,7 @@ unstar=Eliminar destacado
star=Destacar star=Destacar
fork=Fork fork=Fork
no_desc=Sin Descripción no_desc=Sin descripción
quick_guide=Guía Rápida quick_guide=Guía Rápida
clone_this_repo=Clonar este repositorio clone_this_repo=Clonar este repositorio
create_new_repo_command=Crear un nuevo repositorio desde línea de comandos create_new_repo_command=Crear un nuevo repositorio desde línea de comandos

0
conf/locale/locale_fi-FI.ini

2
conf/locale/locale_fr-FR.ini

@ -57,7 +57,7 @@ password=Mot de passe
db_name=Nom de base de données db_name=Nom de base de données
db_helper=Veuillez utiliser le moteur INNODB avec le jeu de caractères utf8_general_ci pour MySQL. db_helper=Veuillez utiliser le moteur INNODB avec le jeu de caractères utf8_general_ci pour MySQL.
ssl_mode=Mode SSL ssl_mode=Mode SSL
path=Chemin path=Emplacement
sqlite_helper=Le chemin du fichier de base de données SQLite3 ou TiDB. <br>Utilisez un chemin absolu lorsque vous démarrez en tant que service. sqlite_helper=Le chemin du fichier de base de données SQLite3 ou TiDB. <br>Utilisez un chemin absolu lorsque vous démarrez en tant que service.
err_empty_db_path=Le chemin de la base de données SQLite3 ou TiDB ne peut être vide. err_empty_db_path=Le chemin de la base de données SQLite3 ou TiDB ne peut être vide.
err_invalid_tidb_name=Le nom de la base de données TiDB ne peut contenir les caractères "." ou "-". err_invalid_tidb_name=Le nom de la base de données TiDB ne peut contenir les caractères "." ou "-".

24
conf/locale/locale_it-IT.ini

@ -467,8 +467,8 @@ issues.close_comment_issue=Commenta e chiudi
issues.reopen_issue=Riapri issues.reopen_issue=Riapri
issues.reopen_comment_issue=Commenta e riapri issues.reopen_comment_issue=Commenta e riapri
issues.create_comment=Commento issues.create_comment=Commento
issues.closed_at=`closed <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at=`chiuso <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`riaperto <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.poster=Autore issues.poster=Autore
issues.collaborator=Collaboratori issues.collaborator=Collaboratori
@ -500,8 +500,8 @@ pulls.no_results=Nessun risultato trovato.
pulls.nothing_to_compare=Non c'è niente da confrontare perchè i branch base e head uguali. pulls.nothing_to_compare=Non c'è niente da confrontare perchè i branch base e head uguali.
pulls.has_pull_request=`E' già presente una pull request tra questi due trargets: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>` pulls.has_pull_request=`E' già presente una pull request tra questi due trargets: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
pulls.create=Crea Pull Request pulls.create=Crea Pull Request
pulls.title_desc=wants to merge %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> pulls.title_desc=vorrebbe unire %[1]d commit da <code>%[2]s</code> a <code>%[3]s</code>
pulls.merged_title_desc=merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s pulls.merged_title_desc=ha unito %[1]d commit da <code>%[2]s</code> a <code>%[3]s</code> %[4]s
pulls.tab_conversation=Conversazione pulls.tab_conversation=Conversazione
pulls.tab_commits=Commit pulls.tab_commits=Commit
pulls.tab_files=File modificati pulls.tab_files=File modificati
@ -574,7 +574,7 @@ settings.external_wiki_url=URL Wiki esterno
settings.external_wiki_url_desc=I visitatori verranno reindirizzati all'URL quando cliccano sulla scheda. settings.external_wiki_url_desc=I visitatori verranno reindirizzati all'URL quando cliccano sulla scheda.
settings.issues_desc=Abilita l'issue tracker builtin leggero settings.issues_desc=Abilita l'issue tracker builtin leggero
settings.use_external_issue_tracker=Utilizza gestore di problemi esterno settings.use_external_issue_tracker=Utilizza gestore di problemi esterno
settings.tracker_url_format=External Issue Tracker URL Format settings.tracker_url_format=Formato URL Gestore Problemi Esterno
settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index. settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index.
settings.pulls_desc=Abilita le pull requests per accettare contributi pubblici settings.pulls_desc=Abilita le pull requests per accettare contributi pubblici
settings.danger_zone=Zona Pericolosa settings.danger_zone=Zona Pericolosa
@ -583,7 +583,7 @@ settings.convert=Converti in Repository Regolare
settings.convert_desc=Puoi convertire questo mirror in un repository regolare. Questa operazione non può essere annullata. settings.convert_desc=Puoi convertire questo mirror in un repository regolare. Questa operazione non può essere annullata.
settings.convert_notices_1=- Questa operazione non potrà essere annullata e convertirà questo mirror in un repository regolare. settings.convert_notices_1=- Questa operazione non potrà essere annullata e convertirà questo mirror in un repository regolare.
settings.convert_confirm=Conferma la conversione settings.convert_confirm=Conferma la conversione
settings.convert_succeed=Repository has been converted to regular type successfully. settings.convert_succeed=Il repository è stato convertito con successo al formato normale.
settings.transfer=Trasferisci proprietà settings.transfer=Trasferisci proprietà
settings.transfer_desc=Trasferisci questa repository a un altro utente o a un'organizzazione nella quale hai diritti d'amministratore. settings.transfer_desc=Trasferisci questa repository a un altro utente o a un'organizzazione nella quale hai diritti d'amministratore.
settings.transfer_notices_1=- You will lose access if new owner is a individual user. settings.transfer_notices_1=- You will lose access if new owner is a individual user.
@ -591,7 +591,7 @@ settings.transfer_notices_2=- You will conserve access if new owner is an organi
settings.transfer_form_title=Per favore inserisci le informazioni seguenti per confermare l'operazione: settings.transfer_form_title=Per favore inserisci le informazioni seguenti per confermare l'operazione:
settings.wiki_delete=Elimina i dati della Wiki settings.wiki_delete=Elimina i dati della Wiki
settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain. settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain.
settings.wiki_delete_notices_1=- This will delete and disable the wiki for %s settings.wiki_delete_notices_1=Questo eliminerà e disabiliterà la wiki per %s
settings.wiki_deletion_success=I dati della wiki del repository sono stati eliminati con successo. settings.wiki_deletion_success=I dati della wiki del repository sono stati eliminati con successo.
settings.delete=Elimina questo repository settings.delete=Elimina questo repository
settings.delete_desc=Una volta che hai cancellato il repository, non puoi tornare indietro. Si prega di fare attenzione. settings.delete_desc=Una volta che hai cancellato il repository, non puoi tornare indietro. Si prega di fare attenzione.
@ -619,7 +619,7 @@ settings.add_webhook=Aggiungi Webhook
settings.hooks_desc=I Webhooks sono molto simili a un basilare evento trigger HTTP POST. Ogni volta che qualcosa si verifica in Gogs, tratteremo la notifica all'host di destinazione specificato. Ulteriori informazioni in questa <a target="_blank" href="%s">Guida ai Webhooks</a>. settings.hooks_desc=I Webhooks sono molto simili a un basilare evento trigger HTTP POST. Ogni volta che qualcosa si verifica in Gogs, tratteremo la notifica all'host di destinazione specificato. Ulteriori informazioni in questa <a target="_blank" href="%s">Guida ai Webhooks</a>.
settings.webhook_deletion=Elimina Webhook settings.webhook_deletion=Elimina Webhook
settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue? settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue?
settings.webhook_deletion_success=Webhook has been deleted successfully! settings.webhook_deletion_success=Il Webhook è stato eliminato con successo!
settings.webhook.test_delivery=Test di consegna settings.webhook.test_delivery=Test di consegna
settings.webhook.test_delivery_desc=Send a fake push event delivery to test your webhook settings settings.webhook.test_delivery_desc=Send a fake push event delivery to test your webhook settings
settings.webhook.test_delivery_success=Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history. settings.webhook.test_delivery_success=Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history.
@ -1067,7 +1067,7 @@ comment_issue=`ha commentato il problema <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request=`merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=ha trasferito il repository <code>%s</code> a <a href="%s">%s</a> transfer_repo=ha trasferito il repository <code>%s</code> a <a href="%s">%s</a>
push_tag=ha pushato il tag <a href="%s/src/%s">%[2]s</a> a <a href="%[1]s">%[3]s</a> push_tag=ha pushato il tag <a href="%s/src/%s">%[2]s</a> a <a href="%[1]s">%[3]s</a>
compare_commits=View comparison for these %d commits compare_commits=Visualizza comparazione tra questi %d commit
[tool] [tool]
ago=fa ago=fa
@ -1091,8 +1091,8 @@ raw_seconds=secondi
raw_minutes=minuti raw_minutes=minuti
[dropzone] [dropzone]
default_message=Drop files here or click to upload. default_message=Trascina i file qui o clicca per caricare.
invalid_input_type=You can't upload files of this type. invalid_input_type=Non è possibile caricare file di questo tipo.
file_too_big=File size ({{filesize}} MB) exceeds maximum size ({{maxFilesize}} MB). file_too_big=La dimensione del file ({{filesize}} MB) supera la dimensione massima ({{maxFilesize}} MB).
remove_file=Rimuovi file remove_file=Rimuovi file

6
conf/locale/locale_ja-JP.ini

@ -133,8 +133,8 @@ issues.in_your_repos=あなたのリポジトリ
[explore] [explore]
repos=リポジトリ repos=リポジトリ
users=Users users=ユーザ
search=Search search=検索
[auth] [auth]
create_new_account=新規アカウントを作成 create_new_account=新規アカウントを作成
@ -371,7 +371,7 @@ migrate.permission_denied=ローカル リポジトリをインポートする
migrate.invalid_local_path=ローカルパスが無効です。存在しないかディレクトリではありません。 migrate.invalid_local_path=ローカルパスが無効です。存在しないかディレクトリではありません。
migrate.failed=移行に失敗しました: %v migrate.failed=移行に失敗しました: %v
mirror_from=mirror of mirror_from=同期ミラー
forked_from=フォーク元 forked_from=フォーク元
fork_from_self=すでにあなたの所有しているリポジトリはフォークできません fork_from_self=すでにあなたの所有しているリポジトリはフォークできません
copy_link=コピー copy_link=コピー

0
conf/locale/locale_lv-LV.ini

82
conf/locale/locale_nl-NL.ini

@ -471,7 +471,7 @@ issues.closed_at=`gesloten om <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`heropend om <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`heropend om <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at='verwees naar dit probleem vanuit een commit <a id="%[1]s" href="#%[1]s"> %[2]s'</a> issues.commit_ref_at='verwees naar dit probleem vanuit een commit <a id="%[1]s" href="#%[1]s"> %[2]s'</a>
issues.poster=Poster issues.poster=Poster
issues.collaborator=Collaborator issues.collaborator=Medewerker
issues.owner=Eigenaar issues.owner=Eigenaar
issues.sign_up_for_free=Gratis aanmelden issues.sign_up_for_free=Gratis aanmelden
issues.sign_in_require_desc=om deel te nemen in deze conversatie. Heeft u al een account? <a href="%s">Meld u aan om te reageren</a> issues.sign_in_require_desc=om deel te nemen in deze conversatie. Heeft u al een account? <a href="%s">Meld u aan om te reageren</a>
@ -512,9 +512,9 @@ pulls.data_broken=Omdat informatie over de fork is verwijderd, zijn de gegevens
pulls.is_checking=Controle van conflicten is nog bezig, ververs deze pagina in enkele ogenblikken. pulls.is_checking=Controle van conflicten is nog bezig, ververs deze pagina in enkele ogenblikken.
pulls.can_auto_merge_desc=Dit pull-request kan automatisch samengevoegd worden. pulls.can_auto_merge_desc=Dit pull-request kan automatisch samengevoegd worden.
pulls.cannot_auto_merge_desc=Dit pull-request kan niet worden gemerged omdat er conflicten zijn. pulls.cannot_auto_merge_desc=Dit pull-request kan niet worden gemerged omdat er conflicten zijn.
pulls.cannot_auto_merge_helper=Please merge manually in order to resolve the conflicts. pulls.cannot_auto_merge_helper=Gelieve beide versies manueel samen te voegen om de conflicten op te lossen.
pulls.merge_pull_request=Samenvoegen van pull verzoek pulls.merge_pull_request=Samenvoegen van pull verzoek
pulls.open_unmerged_pull_exists=`You can't perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.` pulls.open_unmerged_pull_exists=U kan de bewerking 'heropenen' niet uitvoeren omdat er al een pull-aanvraag (#%d) is van dezelfde repository met dezelfde informatie. Voeg deze eerst samen.
milestones.new=Nieuwe mijlpaal milestones.new=Nieuwe mijlpaal
milestones.open_tab=%d geopend milestones.open_tab=%d geopend
@ -553,7 +553,7 @@ wiki.last_commit_info=%s heeft deze pagina aangepast %s
wiki.edit_page_button=Bewerken wiki.edit_page_button=Bewerken
wiki.new_page_button=Nieuwe pagina wiki.new_page_button=Nieuwe pagina
wiki.delete_page_button=Verwijder pagina wiki.delete_page_button=Verwijder pagina
wiki.delete_page_notice_1=This will delete the page <code>"%s"</code>. Please be certain. wiki.delete_page_notice_1=Dit zal pagina <code>"%s"</code> verwijderen. Weet u het zeker?
wiki.page_already_exists=Er bestaat al een wiki-pagina met deze naam. wiki.page_already_exists=Er bestaat al een wiki-pagina met deze naam.
wiki.pages=Pagina’s wiki.pages=Pagina’s
wiki.last_updated=Laatst bijgewerkt: %s wiki.last_updated=Laatst bijgewerkt: %s
@ -566,40 +566,40 @@ settings.githooks=Git-hooks
settings.basic_settings=Basis instellingen settings.basic_settings=Basis instellingen
settings.site=Officiële site settings.site=Officiële site
settings.update_settings=Instellingen bewerken settings.update_settings=Instellingen bewerken
settings.change_reponame_prompt=This change will affect how links relate to the repository. settings.change_reponame_prompt=Deze verandering zal gevolgen hebben voor hoe links zich verhouden tot de repository.
settings.advanced_settings=Geavanceerde opties settings.advanced_settings=Geavanceerde opties
settings.wiki_desc=Enable wiki to allow people write documents settings.wiki_desc=Wiki inschakelen, om mensen documenten te laten schrijven
settings.use_external_wiki=Externe wiki gebruiken settings.use_external_wiki=Externe wiki gebruiken
settings.external_wiki_url=Externe wiki-URL settings.external_wiki_url=Externe wiki-URL
settings.external_wiki_url_desc=Bezoekers worden doorgestuurd naar de URL als ze op het tabblad klikken. settings.external_wiki_url_desc=Bezoekers worden doorgestuurd naar de URL als ze op het tabblad klikken.
settings.issues_desc=Ingebouwde compacte issuetracker inschakelen settings.issues_desc=Ingebouwde compacte issuetracker inschakelen
settings.use_external_issue_tracker=Externe issuetracker gebruiken settings.use_external_issue_tracker=Externe issuetracker gebruiken
settings.tracker_url_format=URL-formaat externe issuetracker settings.tracker_url_format=URL-formaat externe issuetracker
settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index. settings.tracker_url_format_desc=U kan de aanduidingen <code>{user} {repo} {index}</code> gebruiken voor de gebruikersnaam, de naam van de repository en de lijst van open tickets.
settings.pulls_desc=Enable pull requests to accept public contributions settings.pulls_desc=Schakel 'pull request' in om publieke bijdragen te mogelijk te maken
settings.danger_zone=Gevaren zone settings.danger_zone=Gevaren zone
settings.new_owner_has_same_repo=De nieuwe eigenaar heeft al een repositorie met deze naam settings.new_owner_has_same_repo=De nieuwe eigenaar heeft al een repositorie met deze naam
settings.convert=Converteren naar gewone repository settings.convert=Converteren naar gewone repository
settings.convert_desc=U kunt deze mirror converteren naar een gewone repository. Dit kan niet ongedaan worden gemaakt. settings.convert_desc=U kunt deze mirror converteren naar een gewone repository. Dit kan niet ongedaan worden gemaakt.
settings.convert_notices_1=- This operation will convert this repository mirror into a regular repository and cannot be undone. settings.convert_notices_1=- Deze operatie zet de mirror repository om in een gewone repository en dit kan niet ongedaan gemaakt worden.
settings.convert_confirm=Conversie bevestigen settings.convert_confirm=Conversie bevestigen
settings.convert_succeed=Deze repository is geconverteerd naar een normale repository. settings.convert_succeed=Deze repository is geconverteerd naar een normale repository.
settings.transfer=Eigendom overdragen settings.transfer=Eigendom overdragen
settings.transfer_desc=Draag deze repo over aan een andere gebruiker of een organisatie waar u beheerders rechten heeft. settings.transfer_desc=Draag deze repo over aan een andere gebruiker of een organisatie waar u beheerders rechten heeft.
settings.transfer_notices_1=- You will lose access if new owner is a individual user. settings.transfer_notices_1=- U verliest toegang als de nieuwe gebruiker een individuele gebruiker is.
settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners. settings.transfer_notices_2=- U behoudt toegang indien de nieuwe eigenaar een organisatie is en U één van de eigenaren van de organisatie bent.
settings.transfer_form_title=Please enter following information to confirm your operation: settings.transfer_form_title=Voer de volgende informatie in om de bewerking te bevestigen:
settings.wiki_delete=Erase Wiki Data settings.wiki_delete=Wiki gegevens verwijderen
settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain. settings.wiki_delete_desc=Als U wiki informatie wist gaat deze onherroepelijk verloren. Bent U zeker?
settings.wiki_delete_notices_1=- This will delete and disable the wiki for %s settings.wiki_delete_notices_1=- Deze operatie wist de wiki voor %s en schakelt de wiki uit
settings.wiki_deletion_success=Repository wiki data have been erased successfully. settings.wiki_deletion_success=De repository met wiki data is succesvol gewist.
settings.delete=Verwijder deze repositorie settings.delete=Verwijder deze repositorie
settings.delete_desc=Als u eenmaal een repositorie verwijderd is er geen weg terug. Gelieve zeker te zijn van uw acties. settings.delete_desc=Als u eenmaal een repositorie verwijderd is er geen weg terug. Gelieve zeker te zijn van uw acties.
settings.delete_notices_1=- This operation <strong>CANNOT</strong> be undone. settings.delete_notices_1=- Deze bewerking kan <strong>NIET</strong> ongedaan gemaakt worden.
settings.delete_notices_2=- This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators. settings.delete_notices_2=- Deze bewerking verwijdert permanent alle informatie van deze repository met inbegrip van Git gegevens, tickets, opmerkingen en de toegang van medewerkers.
settings.delete_notices_fork_1=- If this repository is public, all forks will become independent after deletion. settings.delete_notices_fork_1=- Als deze repository publiek is worden alle vorken onafhankelijk na verwijdering.
settings.delete_notices_fork_2=- If this repository is private, all forks will be removed at the same time. settings.delete_notices_fork_2=- Als deze repository privé is worden alle vorken ook verwijderd.
settings.delete_notices_fork_3=- If you want to keep all forks after deletion, please change visibility of this repository to public first. settings.delete_notices_fork_3=- Als U alle vorken na verwijderen van deze repository wil behouden, verander dan eerste de zichtbaarheid van deze repository naar 'publiek'.
settings.deletion_success=Repository is succesvol verwijderd! settings.deletion_success=Repository is succesvol verwijderd!
settings.update_settings_success=Repositorie instellingen zijn succesvol bijgewerkt. settings.update_settings_success=Repositorie instellingen zijn succesvol bijgewerkt.
settings.transfer_owner=Nieuwe eigenaar settings.transfer_owner=Nieuwe eigenaar
@ -609,20 +609,20 @@ settings.confirm_delete=Bevestig verwijdering
settings.add_collaborator=Nieuwe medewerker toevoegen settings.add_collaborator=Nieuwe medewerker toevoegen
settings.add_collaborator_success=medewerker is toegevoegd. settings.add_collaborator_success=medewerker is toegevoegd.
settings.delete_collaborator=Verwijderen settings.delete_collaborator=Verwijderen
settings.collaborator_deletion=Collaborator Deletion settings.collaborator_deletion=Verwijder Medewerker
settings.collaborator_deletion_desc=This user will no longer have collaboration access to this repository after deletion. Do you want to continue? settings.collaborator_deletion_desc=Deze gebruiker zal niet langer toegang hebben tot deze repository. Wilt U doorgaan?
settings.remove_collaborator_success=medewerker is verwijderd. settings.remove_collaborator_success=medewerker is verwijderd.
settings.search_user_placeholder=Zoek gebruiker... settings.search_user_placeholder=Zoek gebruiker...
settings.org_not_allowed_to_be_collaborator=Organization is not allowed to be added as a collaborator. settings.org_not_allowed_to_be_collaborator=De organisatie kan niet toegevoegd worden als medewerker.
settings.user_is_org_member=Gebruiker is lid van de organisatie die als een medewerker kan niet worden toegevoegd. settings.user_is_org_member=Gebruiker is lid van de organisatie die als een medewerker kan niet worden toegevoegd.
settings.add_webhook=Webhook toevoegen settings.add_webhook=Webhook toevoegen
settings.hooks_desc=Webhooks dat de externe diensten om kennisgevingen te ontvangen wanneer bepaalde gebeurtenissen op Gogs plaatsvinden. Wanneer de opgegeven gebeurtenissen plaatsvinden, sturen we een POST-aanvraag naar elk van de URL's die u opgeeft. Meer informatie vindt u in onze <a target="_blank" href="%s"> Webhooks gids</a>. settings.hooks_desc=Webhooks dat de externe diensten om kennisgevingen te ontvangen wanneer bepaalde gebeurtenissen op Gogs plaatsvinden. Wanneer de opgegeven gebeurtenissen plaatsvinden, sturen we een POST-aanvraag naar elk van de URL's die u opgeeft. Meer informatie vindt u in onze <a target="_blank" href="%s"> Webhooks gids</a>.
settings.webhook_deletion=Webhook verwijderen settings.webhook_deletion=Webhook verwijderen
settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue? settings.webhook_deletion_desc=Verwijderen van deze webhook zal de informatie en alle geschiedenis verwijderen. Wilt u doorgaan?
settings.webhook_deletion_success=Webhook is succesvol verwijderd! settings.webhook_deletion_success=Webhook is succesvol verwijderd!
settings.webhook.test_delivery=Test-bezorging settings.webhook.test_delivery=Test-bezorging
settings.webhook.test_delivery_desc=Send a fake push event delivery to test your webhook settings settings.webhook.test_delivery_desc=Stuur een nep push bericht om de webhook te testen
settings.webhook.test_delivery_success=Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history. settings.webhook.test_delivery_success=De test webhook is toegevoegd aan de wachtrij. Het kan enkele seconden duren voor deze in de geschiedenis wordt weergegeven.
settings.webhook.request=Verzoek settings.webhook.request=Verzoek
settings.webhook.response=Antwoord settings.webhook.response=Antwoord
settings.webhook.headers=Headers settings.webhook.headers=Headers
@ -662,7 +662,7 @@ settings.slack_domain=Slack domein
settings.slack_channel=Slack kanaal settings.slack_channel=Slack kanaal
settings.deploy_keys=Installeer sleutels settings.deploy_keys=Installeer sleutels
settings.add_deploy_key=Toevoegen deploy sleutel settings.add_deploy_key=Toevoegen deploy sleutel
settings.deploy_key_desc=Deploy keys have read-only access. They are not the same as personal account SSH keys. settings.deploy_key_desc=Sleutels voor uitrol hebben enkel leesrechten. Ze zijn niet dezelfde als de SSH sleutels van persoonlijke accounts.
settings.no_deploy_keys=U hebt nog geen deploy sleutels toegevoegd. settings.no_deploy_keys=U hebt nog geen deploy sleutels toegevoegd.
settings.title=Titel settings.title=Titel
settings.deploy_key_content=Inhoud settings.deploy_key_content=Inhoud
@ -678,8 +678,8 @@ diff.parent=bovenliggende
diff.commit=commit diff.commit=commit
diff.data_not_available=Diff gegevens niet beschikbaar. diff.data_not_available=Diff gegevens niet beschikbaar.
diff.show_diff_stats=Toon Diff Stats diff.show_diff_stats=Toon Diff Stats
diff.show_split_view=Split View diff.show_split_view=Zij-aan-zij weergave
diff.show_unified_view=Unified View diff.show_unified_view=Gecombineerde weergave
diff.stats_desc=<strong>%d gewijzigde bestanden</strong> met <strong>toevoegingen van %d</strong> en <strong>%d verwijderingen</strong> diff.stats_desc=<strong>%d gewijzigde bestanden</strong> met <strong>toevoegingen van %d</strong> en <strong>%d verwijderingen</strong>
diff.bin=BIN diff.bin=BIN
diff.view_file=Bestand weergeven diff.view_file=Bestand weergeven
@ -692,7 +692,7 @@ release.stable=Stabiel
release.edit=bewerken release.edit=bewerken
release.ahead=<strong>%d</strong> aanpassingen aan %s sinds deze versie release.ahead=<strong>%d</strong> aanpassingen aan %s sinds deze versie
release.source_code=Broncode release.source_code=Broncode
release.new_subheader=Publish releases to iterate product. release.new_subheader=Publiceer releases om te itereren.
release.edit_subheader=Een gedetailleerd changelog helpt gebruikers te begrijpen wat er is verbeterd in deze release. release.edit_subheader=Een gedetailleerd changelog helpt gebruikers te begrijpen wat er is verbeterd in deze release.
release.tag_name=Tagnaam release.tag_name=Tagnaam
release.target=Doel release.target=Doel
@ -710,7 +710,7 @@ release.save_draft=Concept opslaan
release.edit_release=Release bewerken release.edit_release=Release bewerken
release.delete_release=Deze release verwijderen release.delete_release=Deze release verwijderen
release.deletion=Release verwijderen release.deletion=Release verwijderen
release.deletion_desc=Deleting this release will delete the corresponding Git tag. Do you want to continue? release.deletion_desc=Als deze release verwijdert, worden de bijbehorende Git tag ook gewist. Wilt u doorgaan?
release.deletion_success=Release is verwijderd! release.deletion_success=Release is verwijderd!
release.tag_name_already_exist=Versie met deze naam bestaat al. release.tag_name_already_exist=Versie met deze naam bestaat al.
release.downloads=Downloads release.downloads=Downloads
@ -764,7 +764,7 @@ members.owner=Eigenaar
members.member=Lid members.member=Lid
members.remove=Verwijderen members.remove=Verwijderen
members.leave=Verlaat members.leave=Verlaat
members.invite_desc=Add a new member to %s: members.invite_desc=Voeg nieuw lid toe aan %s:
members.invite_now=Nu uitnodigen members.invite_now=Nu uitnodigen
teams.join=Lid worden teams.join=Lid worden
@ -874,7 +874,7 @@ users.edit=Bewerken
users.auth_source=Authenticatiebron users.auth_source=Authenticatiebron
users.local=Lokaal users.local=Lokaal
users.auth_login_name=Authenticatie-loginnaam users.auth_login_name=Authenticatie-loginnaam
users.password_helper=Leave it empty to remain unchanged. users.password_helper=Laat leeg om ongewijzigd te blijven.
users.update_profile_success=Profiel is succesvol bijgewerkt. users.update_profile_success=Profiel is succesvol bijgewerkt.
users.edit_account=Bewerk account users.edit_account=Bewerk account
users.max_repo_creation=Maximum Repository Creation Limit users.max_repo_creation=Maximum Repository Creation Limit
@ -914,11 +914,11 @@ auths.domain=Domein
auths.host=Host auths.host=Host
auths.port=Poort auths.port=Poort
auths.bind_dn=Binden DN auths.bind_dn=Binden DN
auths.bind_password=Bind Password auths.bind_password=Bind wachtwoord
auths.bind_password_helper=Warning: This password is stored in plain text. Do not use a high privileged account. auths.bind_password_helper=Opgelet: Dit wachtwoord wordt opgeslagen als leesbare tekst. Gebruik geen account met verhoogde rechten.
auths.user_base=User Search Base auths.user_base=User Search Base
auths.user_dn=User DN auths.user_dn=User DN
auths.attribute_username=Username attribute auths.attribute_username=Gebruikersnaam attribuut
auths.attribute_username_placeholder=Leave empty to use sign-in form field value for user name. auths.attribute_username_placeholder=Leave empty to use sign-in form field value for user name.
auths.attribute_name=Voornaam attribuut auths.attribute_name=Voornaam attribuut
auths.attribute_surname=Achternaam attribuut auths.attribute_surname=Achternaam attribuut
@ -937,9 +937,9 @@ auths.skip_tls_verify=TLS-verificatie overslaan
auths.pam_service_name=PAM servicenaam auths.pam_service_name=PAM servicenaam
auths.enable_auto_register=Activeer automatische registratie auths.enable_auto_register=Activeer automatische registratie
auths.tips=Tips auths.tips=Tips
auths.edit=Edit Authentication Setting auths.edit=Verificatie-instelling bewerken
auths.activated=Deze autorisatiemethode is geactiveerd auths.activated=Deze autorisatiemethode is geactiveerd
auths.new_success=New authentication '%s' has been added successfully. auths.new_success=Nieuwe authenticatie '%s' werd toegevoegd.
auths.update_success=Authentication setting has been updated successfully. auths.update_success=Authentication setting has been updated successfully.
auths.update=Authenticatie-instellingen bijwerken auths.update=Authenticatie-instellingen bijwerken
auths.delete=Deze authenticatiewijze verwijderen auths.delete=Deze authenticatiewijze verwijderen
@ -1040,13 +1040,13 @@ monitor.start=Starttijd
monitor.execute_time=Uitvoertijd monitor.execute_time=Uitvoertijd
notices.system_notice_list=Systeem aankondigingen notices.system_notice_list=Systeem aankondigingen
notices.view_detail_header=View Notice Detail notices.view_detail_header=Bekijk bericht details
notices.actions=Acties notices.actions=Acties
notices.select_all=Alles selecteren notices.select_all=Alles selecteren
notices.deselect_all=Alles deselecteren notices.deselect_all=Alles deselecteren
notices.inverse_selection=Selectie omkeren notices.inverse_selection=Selectie omkeren
notices.delete_selected=Selectie verwijderen notices.delete_selected=Selectie verwijderen
notices.delete_all=Delete All Notices notices.delete_all=Verwijder alle berichten
notices.type=Type notices.type=Type
notices.type_1=Opslagplaats notices.type_1=Opslagplaats
notices.desc=Beschrijving notices.desc=Beschrijving

0
conf/locale/locale_pl-PL.ini

0
conf/locale/locale_pt-BR.ini

2
conf/locale/locale_ru-RU.ini

@ -676,7 +676,7 @@ settings.deploy_key_deletion_success=Ключ развертывания усп
diff.browse_source=Просмотр исходного кода diff.browse_source=Просмотр исходного кода
diff.parent=Родитель diff.parent=Родитель
diff.commit=Сommit diff.commit=Сommit
diff.data_not_available=Данные Diff не доступны. diff.data_not_available=Данные Diff недоступны.
diff.show_diff_stats=Показать статистику Diff diff.show_diff_stats=Показать статистику Diff
diff.show_split_view=Разделённый вид diff.show_split_view=Разделённый вид
diff.show_unified_view=Единый вид diff.show_unified_view=Единый вид

0
conf/locale/locale_zh-CN.ini

62
conf/locale/locale_zh-HK.ini

@ -38,7 +38,7 @@ settings=設定
your_profile=個人資料 your_profile=個人資料
your_settings=用戶設定 your_settings=用戶設定
activities=Activities activities=活動
pull_requests=合併請求 pull_requests=合併請求
issues=問題 issues=問題
@ -58,7 +58,7 @@ db_name=資料庫名稱
db_helper=如果您使用 MySQL,請使用 INNODB 引擎以及 utf8_general_ci 字符集。 db_helper=如果您使用 MySQL,請使用 INNODB 引擎以及 utf8_general_ci 字符集。
ssl_mode=SSL 模式 ssl_mode=SSL 模式
path=數據庫文件路徑 path=數據庫文件路徑
sqlite_helper=The file path of SQLite3 or TiDB database. <br>Please use absolute path when you start as service. sqlite_helper=SQLite3 或 TiDB 資料庫檔案路徑。<br>作為以服務執行時,請使用絕對路徑。
err_empty_db_path=SQLite3 或 TiDB 的數據庫路徑不能為空。 err_empty_db_path=SQLite3 或 TiDB 的數據庫路徑不能為空。
err_invalid_tidb_name=TiDB 數據庫名稱不允許包含字符 "." 或 "-" 。 err_invalid_tidb_name=TiDB 數據庫名稱不允許包含字符 "." 或 "-" 。
no_admin_and_disable_registration=您不能夠在未創建管理員用戶的情況下禁止註冊。 no_admin_and_disable_registration=您不能夠在未創建管理員用戶的情況下禁止註冊。
@ -79,8 +79,8 @@ http_port=HTTP 端口號
http_port_helper=應用監聽的端口號 http_port_helper=應用監聽的端口號
app_url=應用程式網址 app_url=應用程式網址
app_url_helper=該設置影響 HTTP/HTTPS 複製地址和一些郵箱中的連結。 app_url_helper=該設置影響 HTTP/HTTPS 複製地址和一些郵箱中的連結。
log_root_path=Log Path log_root_path=日誌路徑
log_root_path_helper=Directory to write log files to. log_root_path_helper=寫入日誌檔目錄
optional_title=可選設置 optional_title=可選設置
email_title=電子郵件服務設定 email_title=電子郵件服務設定
@ -117,7 +117,7 @@ run_user_not_match=執行系統用戶非當前用戶:%s -> %s
save_config_failed=應用配置保存失敗:%v save_config_failed=應用配置保存失敗:%v
invalid_admin_setting=管理員帳戶設置不正確:%v invalid_admin_setting=管理員帳戶設置不正確:%v
install_success=您好!我們很高興您選擇使用 Gogs,祝您使用愉快,代碼從此無 BUG! install_success=您好!我們很高興您選擇使用 Gogs,祝您使用愉快,代碼從此無 BUG!
invalid_log_root_path=Log root path is invalid: %v invalid_log_root_path=日誌根目錄無效: %v
[home] [home]
uname_holder=用戶名或郵箱 uname_holder=用戶名或郵箱
@ -133,8 +133,8 @@ issues.in_your_repos=屬於該用戶倉庫的
[explore] [explore]
repos=探索倉庫 repos=探索倉庫
users=Users users=用戶
search=Search search=搜索
[auth] [auth]
create_new_account=創建帳戶 create_new_account=創建帳戶
@ -259,7 +259,7 @@ cancel=取消操作
enable_custom_avatar=啟動自定義頭像 enable_custom_avatar=啟動自定義頭像
choose_new_avatar=選擇新的頭像 choose_new_avatar=選擇新的頭像
update_avatar=更新頭像設置 update_avatar=更新頭像設置
delete_current_avatar=Delete Current Avatar delete_current_avatar=刪除當前頭像
uploaded_avatar_not_a_image=上傳的文件不是一張圖片! uploaded_avatar_not_a_image=上傳的文件不是一張圖片!
update_avatar_success=您的頭像設置更新成功! update_avatar_success=您的頭像設置更新成功!
@ -382,7 +382,7 @@ unwatch=取消關注
watch=關註 watch=關註
unstar=取消讚好 unstar=取消讚好
star=讚好 star=讚好
fork=派生 fork=複刻
no_desc=暫無描述 no_desc=暫無描述
quick_guide=快速幫助 quick_guide=快速幫助
@ -471,7 +471,7 @@ issues.closed_at=`於 <a id="%[1]s" href="#%[1]s">%[2]s</a> 關閉`
issues.reopened_at=`於 <a id="%[1]s" href="#%[1]s">%[2]s</a> 重新開啟` issues.reopened_at=`於 <a id="%[1]s" href="#%[1]s">%[2]s</a> 重新開啟`
issues.commit_ref_at=`在代碼提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中引用了該問題` issues.commit_ref_at=`在代碼提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中引用了該問題`
issues.poster=發佈者 issues.poster=發佈者
issues.collaborator=Collaborator issues.collaborator=協同者
issues.owner=所有者 issues.owner=所有者
issues.sign_up_for_free=免費註冊 issues.sign_up_for_free=免費註冊
issues.sign_in_require_desc=及加入到對話當中。如果您已經註冊,可以直接 <a href="%s">登錄及評論</a> issues.sign_in_require_desc=及加入到對話當中。如果您已經註冊,可以直接 <a href="%s">登錄及評論</a>
@ -488,7 +488,7 @@ issues.label_modify=修改標籤
issues.label_deletion=刪除標籤 issues.label_deletion=刪除標籤
issues.label_deletion_desc=刪除該標籤將會移除所有問題中相關的訊息。是否繼續? issues.label_deletion_desc=刪除該標籤將會移除所有問題中相關的訊息。是否繼續?
issues.label_deletion_success=標籤刪除成功! issues.label_deletion_success=標籤刪除成功!
issues.num_participants=%d Participants issues.num_participants=%d 參與者
pulls.new=創建合併請求 pulls.new=創建合併請求
pulls.compare_changes=對比文件變化 pulls.compare_changes=對比文件變化
@ -552,8 +552,8 @@ wiki.save_page=保存頁面
wiki.last_commit_info=%s 於 %s 修改了此頁面 wiki.last_commit_info=%s 於 %s 修改了此頁面
wiki.edit_page_button=修改 wiki.edit_page_button=修改
wiki.new_page_button=新的頁面 wiki.new_page_button=新的頁面
wiki.delete_page_button=Delete Page wiki.delete_page_button=刪除頁面
wiki.delete_page_notice_1=This will delete the page <code>"%s"</code>. Please be certain. wiki.delete_page_notice_1=這將刪除頁面 <code>"%s"</code>。請三思而後行。
wiki.page_already_exists=相同名稱的 Wiki 頁面已經存在。 wiki.page_already_exists=相同名稱的 Wiki 頁面已經存在。
wiki.pages=所有頁面 wiki.pages=所有頁面
wiki.last_updated=最後更新於 %s wiki.last_updated=最後更新於 %s
@ -579,8 +579,8 @@ settings.tracker_url_format_desc=您可以使用 <code>{user} {repo} {index}</co
settings.pulls_desc=啟用合併請求以接受社區貢獻 settings.pulls_desc=啟用合併請求以接受社區貢獻
settings.danger_zone=危險操作區 settings.danger_zone=危險操作區
settings.new_owner_has_same_repo=新的倉庫擁有者已經存在同名倉庫! settings.new_owner_has_same_repo=新的倉庫擁有者已經存在同名倉庫!
settings.convert=Convert To Regular Repository settings.convert=轉換為正規倉庫
settings.convert_desc=You can convert this mirror to a regular repository. This cannot be reversed. settings.convert_desc=您可以將此鏡像轉成正規倉庫。此動做不可逆。
settings.convert_notices_1=- This operation will convert this repository mirror into a regular repository and cannot be undone. settings.convert_notices_1=- This operation will convert this repository mirror into a regular repository and cannot be undone.
settings.convert_confirm=Confirm Conversion settings.convert_confirm=Confirm Conversion
settings.convert_succeed=Repository has been converted to regular type successfully. settings.convert_succeed=Repository has been converted to regular type successfully.
@ -589,9 +589,9 @@ settings.transfer_desc=您可以將倉庫轉移至您擁有管理員權限的帳
settings.transfer_notices_1=- 如果您將倉庫轉移給個人用戶,您將會丟失操作權限。 settings.transfer_notices_1=- 如果您將倉庫轉移給個人用戶,您將會丟失操作權限。
settings.transfer_notices_2=- 如果您將倉庫轉移給您是所有者的組織,您的操作權限將被保留。 settings.transfer_notices_2=- 如果您將倉庫轉移給您是所有者的組織,您的操作權限將被保留。
settings.transfer_form_title=請輸入以下信息以確認您的操作: settings.transfer_form_title=請輸入以下信息以確認您的操作:
settings.wiki_delete=Erase Wiki Data settings.wiki_delete=刪除 Wiki 資料
settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain. settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain.
settings.wiki_delete_notices_1=- This will delete and disable the wiki for %s settings.wiki_delete_notices_1=- 將刪除和停用 %s 的 wiki
settings.wiki_deletion_success=Repository wiki data have been erased successfully. settings.wiki_deletion_success=Repository wiki data have been erased successfully.
settings.delete=刪除本倉庫 settings.delete=刪除本倉庫
settings.delete_desc=刪除倉庫操作不可逆轉,請三思而後行。 settings.delete_desc=刪除倉庫操作不可逆轉,請三思而後行。
@ -608,7 +608,7 @@ settings.transfer_succeed=倉庫所有權轉移成功!
settings.confirm_delete=確認刪除倉庫 settings.confirm_delete=確認刪除倉庫
settings.add_collaborator=增加新的協作者 settings.add_collaborator=增加新的協作者
settings.add_collaborator_success=成功添加新的協作者! settings.add_collaborator_success=成功添加新的協作者!
settings.delete_collaborator=Delete settings.delete_collaborator=刪除
settings.collaborator_deletion=Collaborator Deletion settings.collaborator_deletion=Collaborator Deletion
settings.collaborator_deletion_desc=This user will no longer have collaboration access to this repository after deletion. Do you want to continue? settings.collaborator_deletion_desc=This user will no longer have collaboration access to this repository after deletion. Do you want to continue?
settings.remove_collaborator_success=被操作的協作者已經被收回權限! settings.remove_collaborator_success=被操作的協作者已經被收回權限!
@ -963,17 +963,17 @@ config.log_file_root_path=日志文件根目錄
config.script_type=腳本類型 config.script_type=腳本類型
config.reverse_auth_user=反向代理認證 config.reverse_auth_user=反向代理認證
config.ssh_config=SSH Configuration config.ssh_config=SSH 配置
config.ssh_enabled=Enabled config.ssh_enabled=已啟用
config.ssh_start_builtin_server=Start Builtin Server config.ssh_start_builtin_server=啟動內建伺服器
config.ssh_domain=Domain config.ssh_domain=Domain
config.ssh_port=Port config.ssh_port=
config.ssh_listen_port=Listen Port config.ssh_listen_port=監聽埠
config.ssh_root_path=Root Path config.ssh_root_path=根路徑
config.ssh_key_test_path=Key Test Path config.ssh_key_test_path=金鑰測試路徑
config.ssh_keygen_path=Keygen ('ssh-keygen') Path config.ssh_keygen_path=金鑰產生 (' ssh-keygen ') 路徑
config.ssh_minimum_key_size_check=Minimum Key Size Check config.ssh_minimum_key_size_check=金鑰最小大小檢查
config.ssh_minimum_key_sizes=Minimum Key Sizes config.ssh_minimum_key_sizes=金鑰最小大小
config.db_config=數據庫配置 config.db_config=數據庫配置
config.db_type=數據庫類型 config.db_type=數據庫類型
@ -1004,9 +1004,9 @@ config.mailer_disable_helo=禁用 HELO 操作
config.mailer_name=發送者名稱 config.mailer_name=發送者名稱
config.mailer_host=郵件主機地址 config.mailer_host=郵件主機地址
config.mailer_user=發送者帳號 config.mailer_user=發送者帳號
config.send_test_mail=Send Test Email config.send_test_mail=發送測試郵件
config.test_mail_failed=Fail to send test email to '%s': %v config.test_mail_failed=無法向 '%s' 發送測試郵件: %v
config.test_mail_sent=Test email has been sent to '%s'. config.test_mail_sent=測試電子郵件已發送到 '%s'。
config.oauth_config=社交帳號配置 config.oauth_config=社交帳號配置
config.oauth_enabled=啟用服務 config.oauth_enabled=啟用服務
config.cache_config=Cache 配置 config.cache_config=Cache 配置

26
docker/README.md

@ -43,9 +43,11 @@ If you're more comfortable with mounting data to a data container, the commands
``` ```
# Create data container # Create data container
docker run --name=gogs-data --entrypoint /bin/true gogs/gogs docker run --name=gogs-data --entrypoint /bin/true gogs/gogs
# Use `docker run` for the first time. # Use `docker run` for the first time.
docker run --name=gogs --volumes-from gogs-data -p 10022:22 -p 10080:3000 gogs/gogs docker run --name=gogs --volumes-from gogs-data -p 10022:22 -p 10080:3000 gogs/gogs
``` ```
#### Using Docker 1.9 Volume command #### Using Docker 1.9 Volume command
``` ```
@ -71,9 +73,27 @@ Most of settings are obvious and easy to understand, but there are some settings
Full documentation of application settings can be found [here](http://gogs.io/docs/advanced/configuration_cheat_sheet.html). Full documentation of application settings can be found [here](http://gogs.io/docs/advanced/configuration_cheat_sheet.html).
### Crond ### Container options
Please set environment variable `RUN_CROND` to be `true` or `1` in order to start `crond` inside the container. This container have some options available via environment variables, these options are opt-in features that can help the administration of this container:
- **SOCAT_LINK**:
- <u>Possible value:</u>
`true`, `false`, `1`, `0`
- <u>Default:</u>
`true`
- <u>Action:</u>
Bind linked docker container to localhost socket using socat.
Any exported port from a linked container will be binded to the matching port on localhost.
- <u>Disclaimer:</u>
As this option rely on the environment variable created by docker when a container is linked, this option should be deactivated in managed environment such as Rancher or Kubernetes (set to `0` or `false`)
- **RUN_CROND**:
- <u>Possible value:</u>
`true`, `false`, `1`, `0`
- <u>Default:</u>
`false`
- <u>Action:</u>
Request crond to be run inside the container. Its default configuration will periodically run all scripts from `/etc/periodic/${period}` but custom crontabs can be added to `/var/spool/cron/crontabs/`.
## Upgrade ## Upgrade

2
gogs.go

@ -17,7 +17,7 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const APP_VER = "0.9.15.0323" const APP_VER = "0.9.20.0404"
func init() { func init() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())

36
models/issue.go

@ -5,7 +5,6 @@
package models package models
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -513,7 +512,7 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
if len(opts.RepoIDs) == 0 { if len(opts.RepoIDs) == 0 {
return make([]*Issue, 0), nil return make([]*Issue, 0), nil
} }
sess.Where("issue.repo_id IN ("+strings.Join(base.Int64sToStrings(opts.RepoIDs), ",")+")").And("issue.is_closed=?", opts.IsClosed) sess.In("repo_id", base.Int64sToStrings(opts.RepoIDs)).And("issue.is_closed=?", opts.IsClosed)
} else { } else {
sess.Where("issue.is_closed=?", opts.IsClosed) sess.Where("issue.is_closed=?", opts.IsClosed)
} }
@ -548,27 +547,16 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
} }
labelIDs := base.StringsToInt64s(strings.Split(opts.Labels, ",")) labelIDs := base.StringsToInt64s(strings.Split(opts.Labels, ","))
if len(labelIDs) > 0 { if len(labelIDs) > 1 {
validJoin := false sess.Join("INNER", "issue_label", "issue.id = issue_label.issue_id").In("issue_label.label_id", labelIDs)
queryStr := "issue.id=issue_label.issue_id"
for _, id := range labelIDs {
if id == 0 {
continue
}
validJoin = true
queryStr += " AND issue_label.label_id=" + com.ToStr(id)
}
if validJoin {
sess.Join("INNER", "issue_label", queryStr)
}
} }
if opts.IsMention { if opts.IsMention {
queryStr := "issue.id=issue_user.issue_id AND issue_user.is_mentioned=1" sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id").And("issue_user.is_mentioned = ?", true)
if opts.UserID > 0 { if opts.UserID > 0 {
queryStr += " AND issue_user.uid=" + com.ToStr(opts.UserID) sess.And("issue_user.uid = ?", opts.UserID)
} }
sess.Join("INNER", "issue_user", queryStr)
} }
issues := make([]*Issue, 0, setting.IssuePagingNum) issues := make([]*Issue, 0, setting.IssuePagingNum)
@ -684,18 +672,8 @@ func GetIssueUserPairsByRepoIds(rids []int64, isClosed bool, page int) ([]*Issue
return []*IssueUser{}, nil return []*IssueUser{}, nil
} }
buf := bytes.NewBufferString("")
for _, rid := range rids {
buf.WriteString("repo_id=")
buf.WriteString(com.ToStr(rid))
buf.WriteString(" OR ")
}
cond := strings.TrimSuffix(buf.String(), " OR ")
ius := make([]*IssueUser, 0, 10) ius := make([]*IssueUser, 0, 10)
sess := x.Limit(20, (page-1)*20).Where("is_closed=?", isClosed) sess := x.Limit(20, (page-1)*20).Where("is_closed=?", isClosed).In("repo_id", rids)
if len(cond) > 0 {
sess.And(cond)
}
err := sess.Find(&ius) err := sess.Find(&ius)
return ius, err return ius, err
} }

612
models/org.go

@ -426,618 +426,6 @@ func RemoveOrgUser(orgId, uid int64) error {
return sess.Commit() return sess.Commit()
} }
// ___________
// \__ ___/___ _____ _____
// | |_/ __ \\__ \ / \
// | |\ ___/ / __ \| Y Y \
// |____| \___ >____ /__|_| /
// \/ \/ \/
const OWNER_TEAM = "Owners"
// Team represents a organization team.
type Team struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"INDEX"`
LowerName string
Name string
Description string
Authorize AccessMode
Repos []*Repository `xorm:"-"`
Members []*User `xorm:"-"`
NumRepos int
NumMembers int
}
// IsOwnerTeam returns true if team is owner team.
func (t *Team) IsOwnerTeam() bool {
return t.Name == OWNER_TEAM
}
// IsTeamMember returns true if given user is a member of team.
func (t *Team) IsMember(uid int64) bool {
return IsTeamMember(t.OrgID, t.ID, uid)
}
func (t *Team) getRepositories(e Engine) (err error) {
teamRepos := make([]*TeamRepo, 0, t.NumRepos)
if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil {
return fmt.Errorf("get team-repos: %v", err)
}
t.Repos = make([]*Repository, 0, len(teamRepos))
for i := range teamRepos {
repo, err := getRepositoryByID(e, teamRepos[i].RepoID)
if err != nil {
return fmt.Errorf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err)
}
t.Repos = append(t.Repos, repo)
}
return nil
}
// GetRepositories returns all repositories in team of organization.
func (t *Team) GetRepositories() error {
return t.getRepositories(x)
}
func (t *Team) getMembers(e Engine) (err error) {
t.Members, err = getTeamMembers(e, t.ID)
return err
}
// GetMembers returns all members in team of organization.
func (t *Team) GetMembers() (err error) {
return t.getMembers(x)
}
// AddMember adds new member to team of organization.
func (t *Team) AddMember(uid int64) error {
return AddTeamMember(t.OrgID, t.ID, uid)
}
// RemoveMember removes member from team of organization.
func (t *Team) RemoveMember(uid int64) error {
return RemoveTeamMember(t.OrgID, t.ID, uid)
}
func (t *Team) hasRepository(e Engine, repoID int64) bool {
return hasTeamRepo(e, t.OrgID, t.ID, repoID)
}
// HasRepository returns true if given repository belong to team.
func (t *Team) HasRepository(repoID int64) bool {
return t.hasRepository(x, repoID)
}
func (t *Team) addRepository(e Engine, repo *Repository) (err error) {
if err = addTeamRepo(e, t.OrgID, t.ID, repo.ID); err != nil {
return err
}
t.NumRepos++
if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
return fmt.Errorf("update team: %v", err)
}
if err = repo.recalculateTeamAccesses(e, 0); err != nil {
return fmt.Errorf("recalculateAccesses: %v", err)
}
if err = t.getMembers(e); err != nil {
return fmt.Errorf("getMembers: %v", err)
}
for _, u := range t.Members {
if err = watchRepo(e, u.Id, repo.ID, true); err != nil {
return fmt.Errorf("watchRepo: %v", err)
}
}
return nil
}
// AddRepository adds new repository to team of organization.
func (t *Team) AddRepository(repo *Repository) (err error) {
if repo.OwnerID != t.OrgID {
return errors.New("Repository does not belong to organization")
} else if t.HasRepository(repo.ID) {
return nil
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = t.addRepository(sess, repo); err != nil {
return err
}
return sess.Commit()
}
func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) {
if err = removeTeamRepo(e, t.ID, repo.ID); err != nil {
return err
}
t.NumRepos--
if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
return err
}
// Don't need to recalculate when delete a repository from organization.
if recalculate {
if err = repo.recalculateTeamAccesses(e, t.ID); err != nil {
return err
}
}
if err = t.getMembers(e); err != nil {
return fmt.Errorf("get team members: %v", err)
}
for _, u := range t.Members {
has, err := hasAccess(e, u, repo, ACCESS_MODE_READ)
if err != nil {
return err
} else if has {
continue
}
if err = watchRepo(e, u.Id, repo.ID, false); err != nil {
return err
}
}
return nil
}
// RemoveRepository removes repository from team of organization.
func (t *Team) RemoveRepository(repoID int64) error {
if !t.HasRepository(repoID) {
return nil
}
repo, err := GetRepositoryByID(repoID)
if err != nil {
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = t.removeRepository(sess, repo, true); err != nil {
return err
}
return sess.Commit()
}
// NewTeam creates a record of new team.
// It's caller's responsibility to assign organization ID.
func NewTeam(t *Team) error {
if len(t.Name) == 0 {
return errors.New("empty team name")
}
has, err := x.Id(t.OrgID).Get(new(User))
if err != nil {
return err
} else if !has {
return ErrOrgNotExist
}
t.LowerName = strings.ToLower(t.Name)
has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(new(Team))
if err != nil {
return err
} else if has {
return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Insert(t); err != nil {
sess.Rollback()
return err
}
// Update organization number of teams.
if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
sess.Rollback()
return err
}
return sess.Commit()
}
func getTeam(e Engine, orgId int64, name string) (*Team, error) {
t := &Team{
OrgID: orgId,
LowerName: strings.ToLower(name),
}
has, err := e.Get(t)
if err != nil {
return nil, err
} else if !has {
return nil, ErrTeamNotExist
}
return t, nil
}
// GetTeam returns team by given team name and organization.
func GetTeam(orgId int64, name string) (*Team, error) {
return getTeam(x, orgId, name)
}
func getTeamById(e Engine, teamId int64) (*Team, error) {
t := new(Team)
has, err := e.Id(teamId).Get(t)
if err != nil {
return nil, err
} else if !has {
return nil, ErrTeamNotExist
}
return t, nil
}
// GetTeamById returns team by given ID.
func GetTeamById(teamId int64) (*Team, error) {
return getTeamById(x, teamId)
}
// UpdateTeam updates information of team.
func UpdateTeam(t *Team, authChanged bool) (err error) {
if len(t.Name) == 0 {
return errors.New("empty team name")
}
if len(t.Description) > 255 {
t.Description = t.Description[:255]
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
t.LowerName = strings.ToLower(t.Name)
has, err := x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).And("id!=?", t.ID).Get(new(Team))
if err != nil {
return err
} else if has {
return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
}
if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil {
return fmt.Errorf("update: %v", err)
}
// Update access for team members if needed.
if authChanged {
if err = t.getRepositories(sess); err != nil {
return fmt.Errorf("getRepositories:%v", err)
}
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(sess, 0); err != nil {
return fmt.Errorf("recalculateTeamAccesses: %v", err)
}
}
}
return sess.Commit()
}
// DeleteTeam deletes given team.
// It's caller's responsibility to assign organization ID.
func DeleteTeam(t *Team) error {
if err := t.GetRepositories(); err != nil {
return err
}
// Get organization.
org, err := GetUserByID(t.OrgID)
if err != nil {
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
// Delete all accesses.
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(sess, t.ID); err != nil {
return err
}
}
// Delete team-user.
if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil {
return err
}
// Delete team.
if _, err = sess.Id(t.ID).Delete(new(Team)); err != nil {
return err
}
// Update organization number of teams.
if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
return err
}
return sess.Commit()
}
// ___________ ____ ___
// \__ ___/___ _____ _____ | | \______ ___________
// | |_/ __ \\__ \ / \| | / ___// __ \_ __ \
// | |\ ___/ / __ \| Y Y \ | /\___ \\ ___/| | \/
// |____| \___ >____ /__|_| /______//____ >\___ >__|
// \/ \/ \/ \/ \/
// TeamUser represents an team-user relation.
type TeamUser struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"INDEX"`
TeamID int64 `xorm:"UNIQUE(s)"`
Uid int64 `xorm:"UNIQUE(s)"`
}
func isTeamMember(e Engine, orgID, teamID, uid int64) bool {
has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser))
return has
}
// IsTeamMember returns true if given user is a member of team.
func IsTeamMember(orgID, teamID, uid int64) bool {
return isTeamMember(x, orgID, teamID, uid)
}
func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
teamUsers := make([]*TeamUser, 0, 10)
if err = e.Where("team_id=?", teamID).Find(&teamUsers); err != nil {
return nil, fmt.Errorf("get team-users: %v", err)
}
members := make([]*User, 0, len(teamUsers))
for i := range teamUsers {
member := new(User)
if _, err = e.Id(teamUsers[i].Uid).Get(member); err != nil {
return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].Uid, err)
}
members = append(members, member)
}
return members, nil
}
// GetTeamMembers returns all members in given team of organization.
func GetTeamMembers(teamID int64) ([]*User, error) {
return getTeamMembers(x, teamID)
}
func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) {
tus := make([]*TeamUser, 0, 5)
if err := e.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil {
return nil, err
}
ts := make([]*Team, len(tus))
for i, tu := range tus {
t := new(Team)
has, err := e.Id(tu.TeamID).Get(t)
if err != nil {
return nil, err
} else if !has {
return nil, ErrTeamNotExist
}
ts[i] = t
}
return ts, nil
}
// GetUserTeams returns all teams that user belongs to in given organization.
func GetUserTeams(orgId, uid int64) ([]*Team, error) {
return getUserTeams(x, orgId, uid)
}
// AddTeamMember adds new member to given team of given organization.
func AddTeamMember(orgId, teamId, uid int64) error {
if IsTeamMember(orgId, teamId, uid) {
return nil
}
if err := AddOrgUser(orgId, uid); err != nil {
return err
}
// Get team and its repositories.
t, err := GetTeamById(teamId)
if err != nil {
return err
}
t.NumMembers++
if err = t.GetRepositories(); err != nil {
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
tu := &TeamUser{
Uid: uid,
OrgID: orgId,
TeamID: teamId,
}
if _, err = sess.Insert(tu); err != nil {
return err
} else if _, err = sess.Id(t.ID).Update(t); err != nil {
return err
}
// Give access to team repositories.
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(sess, 0); err != nil {
return err
}
}
// We make sure it exists before.
ou := new(OrgUser)
if _, err = sess.Where("uid=?", uid).And("org_id=?", orgId).Get(ou); err != nil {
return err
}
ou.NumTeams++
if t.IsOwnerTeam() {
ou.IsOwner = true
}
if _, err = sess.Id(ou.ID).AllCols().Update(ou); err != nil {
return err
}
return sess.Commit()
}
func removeTeamMember(e Engine, orgId, teamId, uid int64) error {
if !isTeamMember(e, orgId, teamId, uid) {
return nil
}
// Get team and its repositories.
t, err := getTeamById(e, teamId)
if err != nil {
return err
}
// Check if the user to delete is the last member in owner team.
if t.IsOwnerTeam() && t.NumMembers == 1 {
return ErrLastOrgOwner{UID: uid}
}
t.NumMembers--
if err = t.getRepositories(e); err != nil {
return err
}
// Get organization.
org, err := getUserByID(e, orgId)
if err != nil {
return err
}
tu := &TeamUser{
Uid: uid,
OrgID: orgId,
TeamID: teamId,
}
if _, err := e.Delete(tu); err != nil {
return err
} else if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
return err
}
// Delete access to team repositories.
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(e, 0); err != nil {
return err
}
}
// This must exist.
ou := new(OrgUser)
_, err = e.Where("uid=?", uid).And("org_id=?", org.Id).Get(ou)
if err != nil {
return err
}
ou.NumTeams--
if t.IsOwnerTeam() {
ou.IsOwner = false
}
if _, err = e.Id(ou.ID).AllCols().Update(ou); err != nil {
return err
}
return nil
}
// RemoveTeamMember removes member from given team of given organization.
func RemoveTeamMember(orgId, teamId, uid int64) error {
sess := x.NewSession()
defer sessionRelease(sess)
if err := sess.Begin(); err != nil {
return err
}
if err := removeTeamMember(sess, orgId, teamId, uid); err != nil {
return err
}
return sess.Commit()
}
// ___________ __________
// \__ ___/___ _____ _____\______ \ ____ ______ ____
// | |_/ __ \\__ \ / \| _// __ \\____ \ / _ \
// | |\ ___/ / __ \| Y Y \ | \ ___/| |_> > <_> )
// |____| \___ >____ /__|_| /____|_ /\___ > __/ \____/
// \/ \/ \/ \/ \/|__|
// TeamRepo represents an team-repository relation.
type TeamRepo struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"INDEX"`
TeamID int64 `xorm:"UNIQUE(s)"`
RepoID int64 `xorm:"UNIQUE(s)"`
}
func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("repo_id=?", repoID).Get(new(TeamRepo))
return has
}
// HasTeamRepo returns true if given repository belongs to team.
func HasTeamRepo(orgID, teamID, repoID int64) bool {
return hasTeamRepo(x, orgID, teamID, repoID)
}
func addTeamRepo(e Engine, orgID, teamID, repoID int64) error {
_, err := e.InsertOne(&TeamRepo{
OrgID: orgID,
TeamID: teamID,
RepoID: repoID,
})
return err
}
// AddTeamRepo adds new repository relation to team.
func AddTeamRepo(orgID, teamID, repoID int64) error {
return addTeamRepo(x, orgID, teamID, repoID)
}
func removeTeamRepo(e Engine, teamID, repoID int64) error {
_, err := e.Delete(&TeamRepo{
TeamID: teamID,
RepoID: repoID,
})
return err
}
// RemoveTeamRepo deletes repository relation to team.
func RemoveTeamRepo(teamID, repoID int64) error {
return removeTeamRepo(x, teamID, repoID)
}
func removeOrgRepo(e Engine, orgID, repoID int64) error { func removeOrgRepo(e Engine, orgID, repoID int64) error {
_, err := e.Delete(&TeamRepo{ _, err := e.Delete(&TeamRepo{
OrgID: orgID, OrgID: orgID,

618
models/org_team.go

@ -0,0 +1,618 @@
// Copyright 2016 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"errors"
"fmt"
"strings"
)
const OWNER_TEAM = "Owners"
// Team represents a organization team.
type Team struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"INDEX"`
LowerName string
Name string
Description string
Authorize AccessMode
Repos []*Repository `xorm:"-"`
Members []*User `xorm:"-"`
NumRepos int
NumMembers int
}
// IsOwnerTeam returns true if team is owner team.
func (t *Team) IsOwnerTeam() bool {
return t.Name == OWNER_TEAM
}
// IsTeamMember returns true if given user is a member of team.
func (t *Team) IsMember(uid int64) bool {
return IsTeamMember(t.OrgID, t.ID, uid)
}
func (t *Team) getRepositories(e Engine) (err error) {
teamRepos := make([]*TeamRepo, 0, t.NumRepos)
if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil {
return fmt.Errorf("get team-repos: %v", err)
}
t.Repos = make([]*Repository, 0, len(teamRepos))
for i := range teamRepos {
repo, err := getRepositoryByID(e, teamRepos[i].RepoID)
if err != nil {
return fmt.Errorf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err)
}
t.Repos = append(t.Repos, repo)
}
return nil
}
// GetRepositories returns all repositories in team of organization.
func (t *Team) GetRepositories() error {
return t.getRepositories(x)
}
func (t *Team) getMembers(e Engine) (err error) {
t.Members, err = getTeamMembers(e, t.ID)
return err
}
// GetMembers returns all members in team of organization.
func (t *Team) GetMembers() (err error) {
return t.getMembers(x)
}
// AddMember adds new membership of the team to the organization,
// the user will have membership to the organization automatically when needed.
func (t *Team) AddMember(uid int64) error {
return AddTeamMember(t.OrgID, t.ID, uid)
}
// RemoveMember removes member from team of organization.
func (t *Team) RemoveMember(uid int64) error {
return RemoveTeamMember(t.OrgID, t.ID, uid)
}
func (t *Team) hasRepository(e Engine, repoID int64) bool {
return hasTeamRepo(e, t.OrgID, t.ID, repoID)
}
// HasRepository returns true if given repository belong to team.
func (t *Team) HasRepository(repoID int64) bool {
return t.hasRepository(x, repoID)
}
func (t *Team) addRepository(e Engine, repo *Repository) (err error) {
if err = addTeamRepo(e, t.OrgID, t.ID, repo.ID); err != nil {
return err
}
t.NumRepos++
if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
return fmt.Errorf("update team: %v", err)
}
if err = repo.recalculateTeamAccesses(e, 0); err != nil {
return fmt.Errorf("recalculateAccesses: %v", err)
}
if err = t.getMembers(e); err != nil {
return fmt.Errorf("getMembers: %v", err)
}
for _, u := range t.Members {
if err = watchRepo(e, u.Id, repo.ID, true); err != nil {
return fmt.Errorf("watchRepo: %v", err)
}
}
return nil
}
// AddRepository adds new repository to team of organization.
func (t *Team) AddRepository(repo *Repository) (err error) {
if repo.OwnerID != t.OrgID {
return errors.New("Repository does not belong to organization")
} else if t.HasRepository(repo.ID) {
return nil
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = t.addRepository(sess, repo); err != nil {
return err
}
return sess.Commit()
}
func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) {
if err = removeTeamRepo(e, t.ID, repo.ID); err != nil {
return err
}
t.NumRepos--
if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
return err
}
// Don't need to recalculate when delete a repository from organization.
if recalculate {
if err = repo.recalculateTeamAccesses(e, t.ID); err != nil {
return err
}
}
if err = t.getMembers(e); err != nil {
return fmt.Errorf("get team members: %v", err)
}
for _, u := range t.Members {
has, err := hasAccess(e, u, repo, ACCESS_MODE_READ)
if err != nil {
return err
} else if has {
continue
}
if err = watchRepo(e, u.Id, repo.ID, false); err != nil {
return err
}
}
return nil
}
// RemoveRepository removes repository from team of organization.
func (t *Team) RemoveRepository(repoID int64) error {
if !t.HasRepository(repoID) {
return nil
}
repo, err := GetRepositoryByID(repoID)
if err != nil {
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = t.removeRepository(sess, repo, true); err != nil {
return err
}
return sess.Commit()
}
// NewTeam creates a record of new team.
// It's caller's responsibility to assign organization ID.
func NewTeam(t *Team) error {
if len(t.Name) == 0 {
return errors.New("empty team name")
}
has, err := x.Id(t.OrgID).Get(new(User))
if err != nil {
return err
} else if !has {
return ErrOrgNotExist
}
t.LowerName = strings.ToLower(t.Name)
has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(new(Team))
if err != nil {
return err
} else if has {
return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Insert(t); err != nil {
sess.Rollback()
return err
}
// Update organization number of teams.
if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
sess.Rollback()
return err
}
return sess.Commit()
}
func getTeam(e Engine, orgId int64, name string) (*Team, error) {
t := &Team{
OrgID: orgId,
LowerName: strings.ToLower(name),
}
has, err := e.Get(t)
if err != nil {
return nil, err
} else if !has {
return nil, ErrTeamNotExist
}
return t, nil
}
// GetTeam returns team by given team name and organization.
func GetTeam(orgId int64, name string) (*Team, error) {
return getTeam(x, orgId, name)
}
func getTeamByID(e Engine, teamId int64) (*Team, error) {
t := new(Team)
has, err := e.Id(teamId).Get(t)
if err != nil {
return nil, err
} else if !has {
return nil, ErrTeamNotExist
}
return t, nil
}
// GetTeamByID returns team by given ID.
func GetTeamByID(teamId int64) (*Team, error) {
return getTeamByID(x, teamId)
}
// UpdateTeam updates information of team.
func UpdateTeam(t *Team, authChanged bool) (err error) {
if len(t.Name) == 0 {
return errors.New("empty team name")
}
if len(t.Description) > 255 {
t.Description = t.Description[:255]
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
t.LowerName = strings.ToLower(t.Name)
has, err := x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).And("id!=?", t.ID).Get(new(Team))
if err != nil {
return err
} else if has {
return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
}
if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil {
return fmt.Errorf("update: %v", err)
}
// Update access for team members if needed.
if authChanged {
if err = t.getRepositories(sess); err != nil {
return fmt.Errorf("getRepositories:%v", err)
}
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(sess, 0); err != nil {
return fmt.Errorf("recalculateTeamAccesses: %v", err)
}
}
}
return sess.Commit()
}
// DeleteTeam deletes given team.
// It's caller's responsibility to assign organization ID.
func DeleteTeam(t *Team) error {
if err := t.GetRepositories(); err != nil {
return err
}
// Get organization.
org, err := GetUserByID(t.OrgID)
if err != nil {
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
// Delete all accesses.
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(sess, t.ID); err != nil {
return err
}
}
// Delete team-user.
if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil {
return err
}
// Delete team.
if _, err = sess.Id(t.ID).Delete(new(Team)); err != nil {
return err
}
// Update organization number of teams.
if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
return err
}
return sess.Commit()
}
// ___________ ____ ___
// \__ ___/___ _____ _____ | | \______ ___________
// | |_/ __ \\__ \ / \| | / ___// __ \_ __ \
// | |\ ___/ / __ \| Y Y \ | /\___ \\ ___/| | \/
// |____| \___ >____ /__|_| /______//____ >\___ >__|
// \/ \/ \/ \/ \/
// TeamUser represents an team-user relation.
type TeamUser struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"INDEX"`
TeamID int64 `xorm:"UNIQUE(s)"`
Uid int64 `xorm:"UNIQUE(s)"`
}
func isTeamMember(e Engine, orgID, teamID, uid int64) bool {
has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser))
return has
}
// IsTeamMember returns true if given user is a member of team.
func IsTeamMember(orgID, teamID, uid int64) bool {
return isTeamMember(x, orgID, teamID, uid)
}
func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
teamUsers := make([]*TeamUser, 0, 10)
if err = e.Where("team_id=?", teamID).Find(&teamUsers); err != nil {
return nil, fmt.Errorf("get team-users: %v", err)
}
members := make([]*User, 0, len(teamUsers))
for i := range teamUsers {
member := new(User)
if _, err = e.Id(teamUsers[i].Uid).Get(member); err != nil {
return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].Uid, err)
}
members = append(members, member)
}
return members, nil
}
// GetTeamMembers returns all members in given team of organization.
func GetTeamMembers(teamID int64) ([]*User, error) {
return getTeamMembers(x, teamID)
}
func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) {
tus := make([]*TeamUser, 0, 5)
if err := e.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil {
return nil, err
}
ts := make([]*Team, len(tus))
for i, tu := range tus {
t := new(Team)
has, err := e.Id(tu.TeamID).Get(t)
if err != nil {
return nil, err
} else if !has {
return nil, ErrTeamNotExist
}
ts[i] = t
}
return ts, nil
}
// GetUserTeams returns all teams that user belongs to in given organization.
func GetUserTeams(orgId, uid int64) ([]*Team, error) {
return getUserTeams(x, orgId, uid)
}
// AddTeamMember adds new membership of given team to given organization,
// the user will have membership to given organization automatically when needed.
func AddTeamMember(orgID, teamID, uid int64) error {
if IsTeamMember(orgID, teamID, uid) {
return nil
}
if err := AddOrgUser(orgID, uid); err != nil {
return err
}
// Get team and its repositories.
t, err := GetTeamByID(teamID)
if err != nil {
return err
}
t.NumMembers++
if err = t.GetRepositories(); err != nil {
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
tu := &TeamUser{
Uid: uid,
OrgID: orgID,
TeamID: teamID,
}
if _, err = sess.Insert(tu); err != nil {
return err
} else if _, err = sess.Id(t.ID).Update(t); err != nil {
return err
}
// Give access to team repositories.
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(sess, 0); err != nil {
return err
}
}
// We make sure it exists before.
ou := new(OrgUser)
if _, err = sess.Where("uid = ?", uid).And("org_id = ?", orgID).Get(ou); err != nil {
return err
}
ou.NumTeams++
if t.IsOwnerTeam() {
ou.IsOwner = true
}
if _, err = sess.Id(ou.ID).AllCols().Update(ou); err != nil {
return err
}
return sess.Commit()
}
func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
if !isTeamMember(e, orgID, teamID, uid) {
return nil
}
// Get team and its repositories.
t, err := getTeamByID(e, teamID)
if err != nil {
return err
}
// Check if the user to delete is the last member in owner team.
if t.IsOwnerTeam() && t.NumMembers == 1 {
return ErrLastOrgOwner{UID: uid}
}
t.NumMembers--
if err = t.getRepositories(e); err != nil {
return err
}
// Get organization.
org, err := getUserByID(e, orgID)
if err != nil {
return err
}
tu := &TeamUser{
Uid: uid,
OrgID: orgID,
TeamID: teamID,
}
if _, err := e.Delete(tu); err != nil {
return err
} else if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
return err
}
// Delete access to team repositories.
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(e, 0); err != nil {
return err
}
}
// This must exist.
ou := new(OrgUser)
_, err = e.Where("uid = ?", uid).And("org_id = ?", org.Id).Get(ou)
if err != nil {
return err
}
ou.NumTeams--
if t.IsOwnerTeam() {
ou.IsOwner = false
}
if _, err = e.Id(ou.ID).AllCols().Update(ou); err != nil {
return err
}
return nil
}
// RemoveTeamMember removes member from given team of given organization.
func RemoveTeamMember(orgID, teamID, uid int64) error {
sess := x.NewSession()
defer sessionRelease(sess)
if err := sess.Begin(); err != nil {
return err
}
if err := removeTeamMember(sess, orgID, teamID, uid); err != nil {
return err
}
return sess.Commit()
}
// ___________ __________
// \__ ___/___ _____ _____\______ \ ____ ______ ____
// | |_/ __ \\__ \ / \| _// __ \\____ \ / _ \
// | |\ ___/ / __ \| Y Y \ | \ ___/| |_> > <_> )
// |____| \___ >____ /__|_| /____|_ /\___ > __/ \____/
// \/ \/ \/ \/ \/|__|
// TeamRepo represents an team-repository relation.
type TeamRepo struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"INDEX"`
TeamID int64 `xorm:"UNIQUE(s)"`
RepoID int64 `xorm:"UNIQUE(s)"`
}
func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("repo_id=?", repoID).Get(new(TeamRepo))
return has
}
// HasTeamRepo returns true if given repository belongs to team.
func HasTeamRepo(orgID, teamID, repoID int64) bool {
return hasTeamRepo(x, orgID, teamID, repoID)
}
func addTeamRepo(e Engine, orgID, teamID, repoID int64) error {
_, err := e.InsertOne(&TeamRepo{
OrgID: orgID,
TeamID: teamID,
RepoID: repoID,
})
return err
}
// AddTeamRepo adds new repository relation to team.
func AddTeamRepo(orgID, teamID, repoID int64) error {
return addTeamRepo(x, orgID, teamID, repoID)
}
func removeTeamRepo(e Engine, teamID, repoID int64) error {
_, err := e.Delete(&TeamRepo{
TeamID: teamID,
RepoID: repoID,
})
return err
}
// RemoveTeamRepo deletes repository relation to team.
func RemoveTeamRepo(teamID, repoID int64) error {
return removeTeamRepo(x, teamID, repoID)
}

52
modules/bindata/bindata.go

File diff suppressed because one or more lines are too long

2
modules/context/api.go

@ -18,6 +18,7 @@ import (
type APIContext struct { type APIContext struct {
*Context *Context
Org *APIOrganization
} }
// Error responses error message to client with given message. // Error responses error message to client with given message.
@ -40,6 +41,7 @@ func (ctx *APIContext) Error(status int, title string, obj interface{}) {
}) })
} }
// SetLinkHeader sets pagination link header by given totol number and page size.
func (ctx *APIContext) SetLinkHeader(total, pageSize int) { func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
page := paginater.New(total, pageSize, ctx.QueryInt("page"), 0) page := paginater.New(total, pageSize, ctx.QueryInt("page"), 0)
links := make([]string, 0, 4) links := make([]string, 0, 4)

14
modules/context/api_org.go

@ -0,0 +1,14 @@
// Copyright 2016 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package context
import (
"github.com/gogits/gogs/models"
)
type APIOrganization struct {
Organization *models.User
Team *models.Team
}

5
modules/template/template.go

@ -18,6 +18,7 @@ import (
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
@ -97,7 +98,7 @@ func NewFuncMap() []template.FuncMap {
"ActionContent2Commits": ActionContent2Commits, "ActionContent2Commits": ActionContent2Commits,
"ToUtf8": ToUtf8, "ToUtf8": ToUtf8,
"EscapePound": func(str string) string { "EscapePound": func(str string) string {
return strings.Replace(strings.Replace(str, "%", "%25", -1), "#", "%23", -1) return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20").Replace(str)
}, },
"RenderCommitMessage": RenderCommitMessage, "RenderCommitMessage": RenderCommitMessage,
"ThemeColorMetaTag": func() string { "ThemeColorMetaTag": func() string {
@ -255,7 +256,7 @@ func ActionIcon(opType int) string {
func ActionContent2Commits(act Actioner) *models.PushCommits { func ActionContent2Commits(act Actioner) *models.PushCommits {
push := models.NewPushCommits() push := models.NewPushCommits()
if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil { if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil {
return nil log.Error(4, "json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err)
} }
return push return push
} }

8
public/js/gogs.js

@ -1043,10 +1043,14 @@ $(window).load(function () {
var $num_list = $('.code-view .lines-num'); var $num_list = $('.code-view .lines-num');
// Building blocks. // Building blocks.
var $toappendblock = [];
var $toappendnum_list = [];
for (var i = 0; i < lines.length; i++) { for (var i = 0; i < lines.length; i++) {
$block.append('<li class="L' + (i + 1) + '" rel="L' + (i + 1) + '">' + lines[i] + '</li>'); $toappendblock.push('<li class="L' + (i + 1) + '" rel="L' + (i + 1) + '">' + lines[i] + '</li>');
$num_list.append('<span id="L' + (i + 1) + '">' + (i + 1) + '</span>'); $toappendnum_list.push('<span id="L' + (i + 1) + '">' + (i + 1) + '</span>');
} }
$block.append($toappendblock.join(''));
$num_list.append($toappendnum_list.join(''));
$(document).on('click', '.lines-num span', function (e) { $(document).on('click', '.lines-num span', function (e) {
var $select = $(this); var $select = $(this);

49
routers/api/v1/admin/org_repo.go

@ -0,0 +1,49 @@
// Copyright 2016 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package admin
import (
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/context"
)
func GetRepositoryByParams(ctx *context.APIContext) *models.Repository {
repo, err := models.GetRepositoryByName(ctx.Org.Team.OrgID, ctx.Params(":reponame"))
if err != nil {
if models.IsErrRepoNotExist(err) {
ctx.Status(404)
} else {
ctx.Error(500, "GetRepositoryByName", err)
}
return nil
}
return repo
}
func AddTeamRepository(ctx *context.APIContext) {
repo := GetRepositoryByParams(ctx)
if ctx.Written() {
return
}
if err := ctx.Org.Team.AddRepository(repo); err != nil {
ctx.Error(500, "AddRepository", err)
return
}
ctx.Status(204)
}
func RemoveTeamRepository(ctx *context.APIContext) {
repo := GetRepositoryByParams(ctx)
if ctx.Written() {
return
}
if err := ctx.Org.Team.RemoveRepository(repo.ID); err != nil {
ctx.Error(500, "RemoveRepository", err)
return
}
ctx.Status(204)
}

34
routers/api/v1/admin/org_team.go

@ -14,13 +14,8 @@ import (
) )
func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) {
org := user.GetUserByParamsName(ctx, ":orgname")
if ctx.Written() {
return
}
team := &models.Team{ team := &models.Team{
OrgID: org.Id, OrgID: ctx.Org.Organization.Id,
Name: form.Name, Name: form.Name,
Description: form.Description, Description: form.Description,
Authorize: models.ParseAccessMode(form.Permission), Authorize: models.ParseAccessMode(form.Permission),
@ -36,3 +31,30 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) {
ctx.JSON(201, convert.ToTeam(team)) ctx.JSON(201, convert.ToTeam(team))
} }
func AddTeamMember(ctx *context.APIContext) {
u := user.GetUserByParams(ctx)
if ctx.Written() {
return
}
if err := ctx.Org.Team.AddMember(u.Id); err != nil {
ctx.Error(500, "AddMember", err)
return
}
ctx.Status(204)
}
func RemoveTeamMember(ctx *context.APIContext) {
u := user.GetUserByParams(ctx)
if ctx.Written() {
return
}
if err := ctx.Org.Team.RemoveMember(u.Id); err != nil {
ctx.Error(500, "RemoveMember", err)
return
}
ctx.Status(204)
}

53
routers/api/v1/api.go

@ -110,6 +110,47 @@ func ReqAdmin() macaron.Handler {
} }
} }
func OrgAssignment(args ...bool) macaron.Handler {
var (
assignOrg bool
assignTeam bool
)
if len(args) > 0 {
assignOrg = args[0]
}
if len(args) > 1 {
assignTeam = args[1]
}
return func(ctx *context.APIContext) {
ctx.Org = new(context.APIOrganization)
var err error
if assignOrg {
ctx.Org.Organization, err = models.GetUserByName(ctx.Params(":orgname"))
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Status(404)
} else {
ctx.Error(500, "GetUserByName", err)
}
return
}
}
if assignTeam {
ctx.Org.Team, err = models.GetTeamByID(ctx.ParamsInt64(":teamid"))
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Status(404)
} else {
ctx.Error(500, "GetTeamById", err)
}
return
}
}
}
}
// RegisterRoutes registers all v1 APIs routes to web application. // RegisterRoutes registers all v1 APIs routes to web application.
// FIXME: custom form error response // FIXME: custom form error response
func RegisterRoutes(m *macaron.Macaron) { func RegisterRoutes(m *macaron.Macaron) {
@ -208,7 +249,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/orgs/:orgname", func() { m.Group("/orgs/:orgname", func() {
m.Combo("").Get(org.Get).Patch(bind(api.EditOrgOption{}), org.Edit) m.Combo("").Get(org.Get).Patch(bind(api.EditOrgOption{}), org.Edit)
m.Combo("/teams").Get(org.ListTeams) m.Combo("/teams").Get(org.ListTeams)
}) }, OrgAssignment(true))
m.Any("/*", func(ctx *context.Context) { m.Any("/*", func(ctx *context.Context) {
ctx.Error(404) ctx.Error(404)
@ -228,7 +269,15 @@ func RegisterRoutes(m *macaron.Macaron) {
}) })
m.Group("/orgs/:orgname", func() { m.Group("/orgs/:orgname", func() {
m.Combo("/teams").Post(bind(api.CreateTeamOption{}), admin.CreateTeam) m.Group("/teams", func() {
m.Post("", OrgAssignment(true), bind(api.CreateTeamOption{}), admin.CreateTeam)
})
})
m.Group("/teams", func() {
m.Group("/:teamid", func() {
m.Combo("/members/:username").Put(admin.AddTeamMember).Delete(admin.RemoveTeamMember)
m.Combo("/repos/:reponame").Put(admin.AddTeamRepository).Delete(admin.RemoveTeamRepository)
}, OrgAssignment(false, true))
}) })
}, ReqAdmin()) }, ReqAdmin())
}, context.APIContexter()) }, context.APIContexter())

12
routers/api/v1/org/org.go

@ -42,20 +42,12 @@ func ListUserOrgs(ctx *context.APIContext) {
// https://github.com/gogits/go-gogs-client/wiki/Organizations#get-an-organization // https://github.com/gogits/go-gogs-client/wiki/Organizations#get-an-organization
func Get(ctx *context.APIContext) { func Get(ctx *context.APIContext) {
org := user.GetUserByParamsName(ctx, ":orgname") ctx.JSON(200, convert.ToOrganization(ctx.Org.Organization))
if ctx.Written() {
return
}
ctx.JSON(200, convert.ToOrganization(org))
} }
// https://github.com/gogits/go-gogs-client/wiki/Organizations#edit-an-organization // https://github.com/gogits/go-gogs-client/wiki/Organizations#edit-an-organization
func Edit(ctx *context.APIContext, form api.EditOrgOption) { func Edit(ctx *context.APIContext, form api.EditOrgOption) {
org := user.GetUserByParamsName(ctx, ":orgname") org := ctx.Org.Organization
if ctx.Written() {
return
}
if !org.IsOwnedBy(ctx.User.Id) { if !org.IsOwnedBy(ctx.User.Id) {
ctx.Status(403) ctx.Status(403)
return return

7
routers/api/v1/org/team.go

@ -9,15 +9,10 @@ import (
"github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/modules/context"
"github.com/gogits/gogs/routers/api/v1/convert" "github.com/gogits/gogs/routers/api/v1/convert"
"github.com/gogits/gogs/routers/api/v1/user"
) )
func ListTeams(ctx *context.APIContext) { func ListTeams(ctx *context.APIContext) {
org := user.GetUserByParamsName(ctx, ":orgname") org := ctx.Org.Organization
if ctx.Written() {
return
}
if err := org.GetTeams(); err != nil { if err := org.GetTeams(); err != nil {
ctx.Error(500, "GetTeams", err) ctx.Error(500, "GetTeams", err)
return return

2
routers/repo/download.go

@ -25,7 +25,7 @@ func ServeData(ctx *context.Context, name string, reader io.Reader) error {
if !isTextFile { if !isTextFile {
_, isImageFile := base.IsImageFile(buf) _, isImageFile := base.IsImageFile(buf)
if !isImageFile { if !isImageFile {
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+path.Base(ctx.Repo.TreeName)) ctx.Resp.Header().Set("Content-Disposition", "attachment; filename=\""+path.Base(ctx.Repo.TreeName)+"\"")
ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
} }
} else { } else {

2
routers/user/home.go

@ -268,7 +268,7 @@ func Issues(ctx *context.Context) {
SortType: sortType, SortType: sortType,
}) })
if err != nil { if err != nil {
ctx.Handle(500, "Issues: %v", err) ctx.Handle(500, "Issues", err)
return return
} }

2
templates/.VERSION

@ -1 +1 @@
0.9.15.0323 0.9.20.0404

6
templates/repo/issue/list.tmpl

@ -132,17 +132,17 @@
{{if gt .TotalPages 1}} {{if gt .TotalPages 1}}
<div class="center page buttons"> <div class="center page buttons">
<div class="ui borderless pagination menu"> <div class="ui borderless pagination menu">
<a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&page={{.Previous}}"{{end}}> <a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&page={{.Previous}}"{{end}}>
<i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}} <i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
</a> </a>
{{range .Pages}} {{range .Pages}}
{{if eq .Num -1}} {{if eq .Num -1}}
<a class="disabled item">...</a> <a class="disabled item">...</a>
{{else}} {{else}}
<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&page={{.Num}}"{{end}}>{{.Num}}</a> <a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&page={{.Num}}"{{end}}>{{.Num}}</a>
{{end}} {{end}}
{{end}} {{end}}
<a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&page={{.Next}}"{{end}}> <a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&page={{.Next}}"{{end}}>
{{$.i18n.Tr "repo.issues.next"}}&nbsp;<i class="icon right arrow"></i> {{$.i18n.Tr "repo.issues.next"}}&nbsp;<i class="icon right arrow"></i>
</a> </a>
</div> </div>

Loading…
Cancel
Save