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)
##### Current version: 0.9.15
##### Current version: 0.9.20
| Web | UI | Preview |
|:-------------:|:-------:|:-------:|

3
conf/license/ISC license

@ -1,6 +1,5 @@
ISC License:
Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
Copyright (c) 1995-2003 by Internet Software Consortium
Copyright (c) Year(s), Company or Person's Name
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!
confirm_delete_account=Löschvorgang bestätigen
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]
owner=Besitzer
@ -342,7 +342,7 @@ fork_from=Fork von
fork_visiblity_helper=Die Sichtbarkeit von geforkten Repositories ist nicht veränderbar.
repo_desc=Beschreibung
repo_lang=Sprache
repo_lang_helper=.gitignore Dateien auswählen
repo_lang_helper=Wählen Sie eine .gitignore-Datei aus
license=Lizenz
license_helper=Wählen Sie eine Lizenz aus
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_commits=Commits
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.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.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.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.
@ -938,7 +938,7 @@ auths.pam_service_name=PAM Dienstname
auths.enable_auto_register=Automatische Registrierung aktivieren
auths.tips=Tipps
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.update_success=Die Authentifizierungseinstellungen wurden erfolgreich aktualisiert.
auths.update=Authentifizierungseinstellungen aktualisieren
@ -946,7 +946,7 @@ auths.delete=Diese Authentifizierung löschen
auths.delete_auth_title=Authentifizierung löschen
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.deletion_success=Authentifizierung wurde erfolgreich entfernt!
auths.deletion_success=Authentifizierung wurde erfolgreich gelöscht!
config.server_config=Serverkonfiguration
config.app_name=Anwendungsname

2
conf/locale/locale_es-ES.ini

@ -384,7 +384,7 @@ unstar=Eliminar destacado
star=Destacar
fork=Fork
no_desc=Sin Descripción
no_desc=Sin descripción
quick_guide=Guía Rápida
clone_this_repo=Clonar este repositorio
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_helper=Veuillez utiliser le moteur INNODB avec le jeu de caractères utf8_general_ci pour MySQL.
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.
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 "-".

24
conf/locale/locale_it-IT.ini

@ -467,8 +467,8 @@ issues.close_comment_issue=Commenta e chiudi
issues.reopen_issue=Riapri
issues.reopen_comment_issue=Commenta e riapri
issues.create_comment=Commento
issues.closed_at=`closed <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.closed_at=`chiuso <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.poster=Autore
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.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.title_desc=wants to merge %[1]d commits from <code>%[2]s</code> into <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.title_desc=vorrebbe unire %[1]d commit da <code>%[2]s</code> a <code>%[3]s</code>
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_commits=Commit
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.issues_desc=Abilita l'issue tracker builtin leggero
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.pulls_desc=Abilita le pull requests per accettare contributi pubblici
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_notices_1=- Questa operazione non potrà essere annullata e convertirà questo mirror in un repository regolare.
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_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.
@ -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.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_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.delete=Elimina questo repository
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.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_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_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.
@ -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>`
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>
compare_commits=View comparison for these %d commits
compare_commits=Visualizza comparazione tra questi %d commit
[tool]
ago=fa
@ -1091,8 +1091,8 @@ raw_seconds=secondi
raw_minutes=minuti
[dropzone]
default_message=Drop files here or click to upload.
invalid_input_type=You can't upload files of this type.
file_too_big=File size ({{filesize}} MB) exceeds maximum size ({{maxFilesize}} MB).
default_message=Trascina i file qui o clicca per caricare.
invalid_input_type=Non è possibile caricare file di questo tipo.
file_too_big=La dimensione del file ({{filesize}} MB) supera la dimensione massima ({{maxFilesize}} MB).
remove_file=Rimuovi file

6
conf/locale/locale_ja-JP.ini

@ -133,8 +133,8 @@ issues.in_your_repos=あなたのリポジトリ
[explore]
repos=リポジトリ
users=Users
search=Search
users=ユーザ
search=検索
[auth]
create_new_account=新規アカウントを作成
@ -371,7 +371,7 @@ migrate.permission_denied=ローカル リポジトリをインポートする
migrate.invalid_local_path=ローカルパスが無効です。存在しないかディレクトリではありません。
migrate.failed=移行に失敗しました: %v
mirror_from=mirror of
mirror_from=同期ミラー
forked_from=フォーク元
fork_from_self=すでにあなたの所有しているリポジトリはフォークできません
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.commit_ref_at='verwees naar dit probleem vanuit een commit <a id="%[1]s" href="#%[1]s"> %[2]s'</a>
issues.poster=Poster
issues.collaborator=Collaborator
issues.collaborator=Medewerker
issues.owner=Eigenaar
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>
@ -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.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_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.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.open_tab=%d geopend
@ -553,7 +553,7 @@ wiki.last_commit_info=%s heeft deze pagina aangepast %s
wiki.edit_page_button=Bewerken
wiki.new_page_button=Nieuwe 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.pages=Pagina’s
wiki.last_updated=Laatst bijgewerkt: %s
@ -566,40 +566,40 @@ settings.githooks=Git-hooks
settings.basic_settings=Basis instellingen
settings.site=Officiële site
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.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.external_wiki_url=Externe wiki-URL
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.use_external_issue_tracker=Externe issuetracker gebruiken
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.pulls_desc=Enable pull requests to accept public contributions
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=Schakel 'pull request' in om publieke bijdragen te mogelijk te maken
settings.danger_zone=Gevaren zone
settings.new_owner_has_same_repo=De nieuwe eigenaar heeft al een repositorie met deze naam
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_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_succeed=Deze repository is geconverteerd naar een normale repository.
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_notices_1=- You will lose access if new owner is a individual user.
settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners.
settings.transfer_form_title=Please enter following information to confirm your operation:
settings.wiki_delete=Erase Wiki Data
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_deletion_success=Repository wiki data have been erased successfully.
settings.transfer_notices_1=- U verliest toegang als de nieuwe gebruiker een individuele gebruiker is.
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=Voer de volgende informatie in om de bewerking te bevestigen:
settings.wiki_delete=Wiki gegevens verwijderen
settings.wiki_delete_desc=Als U wiki informatie wist gaat deze onherroepelijk verloren. Bent U zeker?
settings.wiki_delete_notices_1=- Deze operatie wist de wiki voor %s en schakelt de wiki uit
settings.wiki_deletion_success=De repository met wiki data is succesvol gewist.
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_notices_1=- This operation <strong>CANNOT</strong> be undone.
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_fork_1=- If this repository is public, all forks will become independent after deletion.
settings.delete_notices_fork_2=- If this repository is private, all forks will be removed at the same time.
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_1=- Deze bewerking kan <strong>NIET</strong> ongedaan gemaakt worden.
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=- Als deze repository publiek is worden alle vorken onafhankelijk na verwijdering.
settings.delete_notices_fork_2=- Als deze repository privé is worden alle vorken ook verwijderd.
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.update_settings_success=Repositorie instellingen zijn succesvol bijgewerkt.
settings.transfer_owner=Nieuwe eigenaar
@ -609,20 +609,20 @@ settings.confirm_delete=Bevestig verwijdering
settings.add_collaborator=Nieuwe medewerker toevoegen
settings.add_collaborator_success=medewerker is toegevoegd.
settings.delete_collaborator=Verwijderen
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=Verwijder Medewerker
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.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.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.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.test_delivery=Test-bezorging
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_desc=Stuur een nep push bericht om de webhook te testen
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.response=Antwoord
settings.webhook.headers=Headers
@ -662,7 +662,7 @@ settings.slack_domain=Slack domein
settings.slack_channel=Slack kanaal
settings.deploy_keys=Installeer sleutels
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.title=Titel
settings.deploy_key_content=Inhoud
@ -678,8 +678,8 @@ diff.parent=bovenliggende
diff.commit=commit
diff.data_not_available=Diff gegevens niet beschikbaar.
diff.show_diff_stats=Toon Diff Stats
diff.show_split_view=Split View
diff.show_unified_view=Unified View
diff.show_split_view=Zij-aan-zij weergave
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.bin=BIN
diff.view_file=Bestand weergeven
@ -692,7 +692,7 @@ release.stable=Stabiel
release.edit=bewerken
release.ahead=<strong>%d</strong> aanpassingen aan %s sinds deze versie
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.tag_name=Tagnaam
release.target=Doel
@ -710,7 +710,7 @@ release.save_draft=Concept opslaan
release.edit_release=Release bewerken
release.delete_release=Deze 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.tag_name_already_exist=Versie met deze naam bestaat al.
release.downloads=Downloads
@ -764,7 +764,7 @@ members.owner=Eigenaar
members.member=Lid
members.remove=Verwijderen
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
teams.join=Lid worden
@ -874,7 +874,7 @@ users.edit=Bewerken
users.auth_source=Authenticatiebron
users.local=Lokaal
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.edit_account=Bewerk account
users.max_repo_creation=Maximum Repository Creation Limit
@ -914,11 +914,11 @@ auths.domain=Domein
auths.host=Host
auths.port=Poort
auths.bind_dn=Binden DN
auths.bind_password=Bind Password
auths.bind_password_helper=Warning: This password is stored in plain text. Do not use a high privileged account.
auths.bind_password=Bind wachtwoord
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_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_name=Voornaam attribuut
auths.attribute_surname=Achternaam attribuut
@ -937,9 +937,9 @@ auths.skip_tls_verify=TLS-verificatie overslaan
auths.pam_service_name=PAM servicenaam
auths.enable_auto_register=Activeer automatische registratie
auths.tips=Tips
auths.edit=Edit Authentication Setting
auths.edit=Verificatie-instelling bewerken
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=Authenticatie-instellingen bijwerken
auths.delete=Deze authenticatiewijze verwijderen
@ -1040,13 +1040,13 @@ monitor.start=Starttijd
monitor.execute_time=Uitvoertijd
notices.system_notice_list=Systeem aankondigingen
notices.view_detail_header=View Notice Detail
notices.view_detail_header=Bekijk bericht details
notices.actions=Acties
notices.select_all=Alles selecteren
notices.deselect_all=Alles deselecteren
notices.inverse_selection=Selectie omkeren
notices.delete_selected=Selectie verwijderen
notices.delete_all=Delete All Notices
notices.delete_all=Verwijder alle berichten
notices.type=Type
notices.type_1=Opslagplaats
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.parent=Родитель
diff.commit=Сommit
diff.data_not_available=Данные Diff не доступны.
diff.data_not_available=Данные Diff недоступны.
diff.show_diff_stats=Показать статистику Diff
diff.show_split_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_settings=用戶設定
activities=Activities
activities=活動
pull_requests=合併請求
issues=問題
@ -58,7 +58,7 @@ db_name=資料庫名稱
db_helper=如果您使用 MySQL,請使用 INNODB 引擎以及 utf8_general_ci 字符集。
ssl_mode=SSL 模式
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_invalid_tidb_name=TiDB 數據庫名稱不允許包含字符 "." 或 "-" 。
no_admin_and_disable_registration=您不能夠在未創建管理員用戶的情況下禁止註冊。
@ -79,8 +79,8 @@ http_port=HTTP 端口號
http_port_helper=應用監聽的端口號
app_url=應用程式網址
app_url_helper=該設置影響 HTTP/HTTPS 複製地址和一些郵箱中的連結。
log_root_path=Log Path
log_root_path_helper=Directory to write log files to.
log_root_path=日誌路徑
log_root_path_helper=寫入日誌檔目錄
optional_title=可選設置
email_title=電子郵件服務設定
@ -117,7 +117,7 @@ run_user_not_match=執行系統用戶非當前用戶:%s -> %s
save_config_failed=應用配置保存失敗:%v
invalid_admin_setting=管理員帳戶設置不正確:%v
install_success=您好!我們很高興您選擇使用 Gogs,祝您使用愉快,代碼從此無 BUG!
invalid_log_root_path=Log root path is invalid: %v
invalid_log_root_path=日誌根目錄無效: %v
[home]
uname_holder=用戶名或郵箱
@ -133,8 +133,8 @@ issues.in_your_repos=屬於該用戶倉庫的
[explore]
repos=探索倉庫
users=Users
search=Search
users=用戶
search=搜索
[auth]
create_new_account=創建帳戶
@ -259,7 +259,7 @@ cancel=取消操作
enable_custom_avatar=啟動自定義頭像
choose_new_avatar=選擇新的頭像
update_avatar=更新頭像設置
delete_current_avatar=Delete Current Avatar
delete_current_avatar=刪除當前頭像
uploaded_avatar_not_a_image=上傳的文件不是一張圖片!
update_avatar_success=您的頭像設置更新成功!
@ -382,7 +382,7 @@ unwatch=取消關注
watch=關註
unstar=取消讚好
star=讚好
fork=派生
fork=複刻
no_desc=暫無描述
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.commit_ref_at=`在代碼提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中引用了該問題`
issues.poster=發佈者
issues.collaborator=Collaborator
issues.collaborator=協同者
issues.owner=所有者
issues.sign_up_for_free=免費註冊
issues.sign_in_require_desc=及加入到對話當中。如果您已經註冊,可以直接 <a href="%s">登錄及評論</a>
@ -488,7 +488,7 @@ issues.label_modify=修改標籤
issues.label_deletion=刪除標籤
issues.label_deletion_desc=刪除該標籤將會移除所有問題中相關的訊息。是否繼續?
issues.label_deletion_success=標籤刪除成功!
issues.num_participants=%d Participants
issues.num_participants=%d 參與者
pulls.new=創建合併請求
pulls.compare_changes=對比文件變化
@ -552,8 +552,8 @@ wiki.save_page=保存頁面
wiki.last_commit_info=%s 於 %s 修改了此頁面
wiki.edit_page_button=修改
wiki.new_page_button=新的頁面
wiki.delete_page_button=Delete Page
wiki.delete_page_notice_1=This will delete the page <code>"%s"</code>. Please be certain.
wiki.delete_page_button=刪除頁面
wiki.delete_page_notice_1=這將刪除頁面 <code>"%s"</code>。請三思而後行。
wiki.page_already_exists=相同名稱的 Wiki 頁面已經存在。
wiki.pages=所有頁面
wiki.last_updated=最後更新於 %s
@ -579,8 +579,8 @@ settings.tracker_url_format_desc=您可以使用 <code>{user} {repo} {index}</co
settings.pulls_desc=啟用合併請求以接受社區貢獻
settings.danger_zone=危險操作區
settings.new_owner_has_same_repo=新的倉庫擁有者已經存在同名倉庫!
settings.convert=Convert To Regular Repository
settings.convert_desc=You can convert this mirror to a regular repository. This cannot be reversed.
settings.convert=轉換為正規倉庫
settings.convert_desc=您可以將此鏡像轉成正規倉庫。此動做不可逆。
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_succeed=Repository has been converted to regular type successfully.
@ -589,9 +589,9 @@ settings.transfer_desc=您可以將倉庫轉移至您擁有管理員權限的帳
settings.transfer_notices_1=- 如果您將倉庫轉移給個人用戶,您將會丟失操作權限。
settings.transfer_notices_2=- 如果您將倉庫轉移給您是所有者的組織,您的操作權限將被保留。
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_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.delete=刪除本倉庫
settings.delete_desc=刪除倉庫操作不可逆轉,請三思而後行。
@ -608,7 +608,7 @@ settings.transfer_succeed=倉庫所有權轉移成功!
settings.confirm_delete=確認刪除倉庫
settings.add_collaborator=增加新的協作者
settings.add_collaborator_success=成功添加新的協作者!
settings.delete_collaborator=Delete
settings.delete_collaborator=刪除
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.remove_collaborator_success=被操作的協作者已經被收回權限!
@ -963,17 +963,17 @@ config.log_file_root_path=日志文件根目錄
config.script_type=腳本類型
config.reverse_auth_user=反向代理認證
config.ssh_config=SSH Configuration
config.ssh_enabled=Enabled
config.ssh_start_builtin_server=Start Builtin Server
config.ssh_config=SSH 配置
config.ssh_enabled=已啟用
config.ssh_start_builtin_server=啟動內建伺服器
config.ssh_domain=Domain
config.ssh_port=Port
config.ssh_listen_port=Listen Port
config.ssh_root_path=Root Path
config.ssh_key_test_path=Key Test Path
config.ssh_keygen_path=Keygen ('ssh-keygen') Path
config.ssh_minimum_key_size_check=Minimum Key Size Check
config.ssh_minimum_key_sizes=Minimum Key Sizes
config.ssh_port=
config.ssh_listen_port=監聽埠
config.ssh_root_path=根路徑
config.ssh_key_test_path=金鑰測試路徑
config.ssh_keygen_path=金鑰產生 (' ssh-keygen ') 路徑
config.ssh_minimum_key_size_check=金鑰最小大小檢查
config.ssh_minimum_key_sizes=金鑰最小大小
config.db_config=數據庫配置
config.db_type=數據庫類型
@ -1004,9 +1004,9 @@ config.mailer_disable_helo=禁用 HELO 操作
config.mailer_name=發送者名稱
config.mailer_host=郵件主機地址
config.mailer_user=發送者帳號
config.send_test_mail=Send Test Email
config.test_mail_failed=Fail to send test email to '%s': %v
config.test_mail_sent=Test email has been sent to '%s'.
config.send_test_mail=發送測試郵件
config.test_mail_failed=無法向 '%s' 發送測試郵件: %v
config.test_mail_sent=測試電子郵件已發送到 '%s'。
config.oauth_config=社交帳號配置
config.oauth_enabled=啟用服務
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
docker run --name=gogs-data --entrypoint /bin/true gogs/gogs
# Use `docker run` for the first time.
docker run --name=gogs --volumes-from gogs-data -p 10022:22 -p 10080:3000 gogs/gogs
```
#### 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).
### Crond
Please set environment variable `RUN_CROND` to be `true` or `1` in order to start `crond` inside the container.
### Container options
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

2
gogs.go

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

36
models/issue.go

@ -5,7 +5,6 @@
package models
import (
"bytes"
"errors"
"fmt"
"io"
@ -513,7 +512,7 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
if len(opts.RepoIDs) == 0 {
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 {
sess.Where("issue.is_closed=?", opts.IsClosed)
}
@ -548,27 +547,16 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
}
labelIDs := base.StringsToInt64s(strings.Split(opts.Labels, ","))
if len(labelIDs) > 0 {
validJoin := false
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 len(labelIDs) > 1 {
sess.Join("INNER", "issue_label", "issue.id = issue_label.issue_id").In("issue_label.label_id", labelIDs)
}
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 {
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)
@ -684,18 +672,8 @@ func GetIssueUserPairsByRepoIds(rids []int64, isClosed bool, page int) ([]*Issue
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)
sess := x.Limit(20, (page-1)*20).Where("is_closed=?", isClosed)
if len(cond) > 0 {
sess.And(cond)
}
sess := x.Limit(20, (page-1)*20).Where("is_closed=?", isClosed).In("repo_id", rids)
err := sess.Find(&ius)
return ius, err
}

612
models/org.go

@ -426,618 +426,6 @@ func RemoveOrgUser(orgId, uid int64) error {
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 {
_, err := e.Delete(&TeamRepo{
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 {
*Context
Org *APIOrganization
}
// 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) {
page := paginater.New(total, pageSize, ctx.QueryInt("page"), 0)
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/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/setting"
)
@ -97,7 +98,7 @@ func NewFuncMap() []template.FuncMap {
"ActionContent2Commits": ActionContent2Commits,
"ToUtf8": ToUtf8,
"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,
"ThemeColorMetaTag": func() string {
@ -255,7 +256,7 @@ func ActionIcon(opType int) string {
func ActionContent2Commits(act Actioner) *models.PushCommits {
push := models.NewPushCommits()
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
}

8
public/js/gogs.js

@ -1043,10 +1043,14 @@ $(window).load(function () {
var $num_list = $('.code-view .lines-num');
// Building blocks.
var $toappendblock = [];
var $toappendnum_list = [];
for (var i = 0; i < lines.length; i++) {
$block.append('<li class="L' + (i + 1) + '" rel="L' + (i + 1) + '">' + lines[i] + '</li>');
$num_list.append('<span id="L' + (i + 1) + '">' + (i + 1) + '</span>');
$toappendblock.push('<li class="L' + (i + 1) + '" rel="L' + (i + 1) + '">' + lines[i] + '</li>');
$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) {
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) {
org := user.GetUserByParamsName(ctx, ":orgname")
if ctx.Written() {
return
}
team := &models.Team{
OrgID: org.Id,
OrgID: ctx.Org.Organization.Id,
Name: form.Name,
Description: form.Description,
Authorize: models.ParseAccessMode(form.Permission),
@ -36,3 +31,30 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) {
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.
// FIXME: custom form error response
func RegisterRoutes(m *macaron.Macaron) {
@ -208,7 +249,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/orgs/:orgname", func() {
m.Combo("").Get(org.Get).Patch(bind(api.EditOrgOption{}), org.Edit)
m.Combo("/teams").Get(org.ListTeams)
})
}, OrgAssignment(true))
m.Any("/*", func(ctx *context.Context) {
ctx.Error(404)
@ -228,7 +269,15 @@ func RegisterRoutes(m *macaron.Macaron) {
})
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())
}, 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
func Get(ctx *context.APIContext) {
org := user.GetUserByParamsName(ctx, ":orgname")
if ctx.Written() {
return
}
ctx.JSON(200, convert.ToOrganization(org))
ctx.JSON(200, convert.ToOrganization(ctx.Org.Organization))
}
// https://github.com/gogits/go-gogs-client/wiki/Organizations#edit-an-organization
func Edit(ctx *context.APIContext, form api.EditOrgOption) {
org := user.GetUserByParamsName(ctx, ":orgname")
if ctx.Written() {
return
}
org := ctx.Org.Organization
if !org.IsOwnedBy(ctx.User.Id) {
ctx.Status(403)
return

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

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

2
routers/repo/download.go

@ -25,7 +25,7 @@ func ServeData(ctx *context.Context, name string, reader io.Reader) error {
if !isTextFile {
_, isImageFile := base.IsImageFile(buf)
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")
}
} else {

2
routers/user/home.go

@ -268,7 +268,7 @@ func Issues(ctx *context.Context) {
SortType: sortType,
})
if err != nil {
ctx.Handle(500, "Issues: %v", err)
ctx.Handle(500, "Issues", err)
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}}
<div class="center page buttons">
<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"}}
</a>
{{range .Pages}}
{{if eq .Num -1}}
<a class="disabled item">...</a>
{{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}}
<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>
</a>
</div>

Loading…
Cancel
Save