diff --git a/README.md b/README.md index 3fee349..d2e6441 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ - # Mantis Source Integration -Copyright (C) 2012 John Reese +Copyright (c) 2008 - 2012 John Reese - http://noswap.com +Copyright (c) 2012 - 2014 MantisBT Team - mantisbt-dev@lists.sourceforge.net + +Released under the [MIT license](http://opensource.org/licenses/MIT) + ## Description @@ -16,9 +19,11 @@ and [Git](http://git-scm.com/) repositories using the following extension plugins: +* **SourceBitBucket**: Git repositories hosted on [BitBucket](http://bitbucket.org/). * **SourceCgit**: Git repositories accessible via a [cgit](http://hjemli.net/git/cgit/) web frontend installation. * **SourceGithub**: Git repositories hosted on [GitHub](http://github.com/). +* **SourceGitlab**: Git repositories hosted on [GitLab](https://about.gitlab.com/). * **SourceGitweb**: Git repositories accessible via a [GitWeb](https://git.wiki.kernel.org/index.php/Gitweb) web frontend installation. @@ -70,23 +75,41 @@ 6. Click on the "Source Control Integration" plugin to configure it. + NOTE: an API Key must be set up to import changesets via shell. + To generate a random key, run + + openssl rand -hex 12 + 7. Go to "Repositories" and enter your repository name, select the repository type, and click "Create Repository" to begin adding your first repository. -8. Once configured, click the "Return to Repository" link and click either +8. Configure the repository, following the specific documentation for the + relevant plugin extension: + + * [SourceGithub](docs/CONFIGURING.SourceGithub.md) + +9. Once configured, click the "Return to Repository" link and click either the "Import Everything" or "Import Newest Data" button to perform initial import of repository changesets and verify configuration. -9. Once satisfied that your repository is configured & working correctly, - you can automate the import of new changesets by configuring a cron - job on the web server where your Mantis installation resides, as follows: +10. Once satisfied that your repository is configured & working correctly, + you can automate the import of new changesets by configuring a cron + job on the web server where your Mantis installation resides, as follows: - curl "http://host.domain.tld/mantisbt/plugin.php?page=Source/import&id=all" + a. import via curl (could run into timeouts on large repositories, + or block your webserver) - This will import new changesets for all configured repositories. + curl "http://host.domain.tld/mantisbt/plugin.php?page=Source/import&id=all" -10. Add additional repositories as needed. + b. import via shell + + php-cgi -f plugin.php page=Source/import id=all api_key= + + + This will import new changesets for all configured repositories. + +11. Add additional repositories as needed. ## Support diff --git a/Source/Source.php b/Source/Source.php index c0e9a87..defacc0 100755 --- a/Source/Source.php +++ b/Source/Source.php @@ -49,10 +49,10 @@ 'enable_message' => OFF, 'enable_product_matrix' => OFF, - 'buglink_regex_1' => '/(?:bugs?|issues?|reports?)+\s+(?:#(?:\d+)[,\.\s]*)+/i', + 'buglink_regex_1' => '/(?:bugs?|issues?|reports?)+\s*:?\s+(?:#(?:\d+)[,\.\s]*)+/i', 'buglink_regex_2' => '/#?(\d+)/', - 'bugfix_regex_1' => '/(?:fixe?d?s?|resolved?s?)+\s+(?:#(?:\d+)[,\.\s]*)+/i', + 'bugfix_regex_1' => '/(?:fixe?d?s?|resolved?s?)+\s*:?\s+(?:#(?:\d+)[,\.\s]*)+/i', 'bugfix_regex_2' => '/#?(\d+)/', 'bugfix_status' => -1, 'bugfix_resolution' => FIXED, diff --git a/Source/lang/strings_german.txt b/Source/lang/strings_german.txt index badb7d4..f880719 100644 --- a/Source/lang/strings_german.txt +++ b/Source/lang/strings_german.txt @@ -74,6 +74,8 @@ $s_plugin_Source_attach_to_issue = 'Zu Eintrag hinzufügen:'; $s_plugin_Source_configuration = 'Konfiguration'; +$s_plugin_Source_api_key = 'API Key'; +$s_plugin_Source_api_key_info = 'Dies ist ein geheimer Schlüssel der von Diensten genutzt wird, um Commit-Daten an Mantis zu senden.
Um einen API-Key zu generieren, führen Sie openssl rand -hex 12 auf einer Unix Konsole aus.'; $s_plugin_Source_allow_remote_checkin = 'Übertragung vom Fremdrechner erlauben'; $s_plugin_Source_remote_checkin_urls = 'Erlaubte Adressen'; $s_plugin_Source_allow_remote_import = 'Importe vom Fremdrechner erlauben'; @@ -87,16 +89,19 @@ $s_plugin_Source_bugfix_regex_1 = 'RegEx für die automatische Zuordnung und Erledigung eines Eintrags #1
Dieser reguläre Ausdruck erkennt die Anweisung in der Übertragungsmeldung, z.B. "Fixed #123"'; $s_plugin_Source_bugfix_regex_2 = 'RegEx für die automatische Zuordnung und Erledigung eines Eintrags #2
Dieser reguläre Ausdruck wird auf das Ergebnis von #1 angewandt und ermittelt daraus die Eintrags-ID'; $s_plugin_Source_bugfix_status = 'Automatische Erledigung: Status'; +$s_plugin_Source_bugfix_status_pvm = 'Automatische Erledigung Produktstatus'; $s_plugin_Source_bugfix_status_off = '[Deaktiviert]'; $s_plugin_Source_bugfix_status_default = '[Standardstatus für \'Erledigt\']'; $s_plugin_Source_bugfix_resolution = 'Automatische Erledigung: Lösung'; $s_plugin_Source_bugfix_handler = 'Automatische Erledigung: Zum Autor der Übertragung zuweisen'; $s_plugin_Source_bugfix_message = 'Automatische Erledigung: Text der automatischen Eintragsnotiz'; $s_plugin_Source_bugfix_message_info = 'Platzhalter: $1 für Zweig, $2 für Revision, $3 für Datum/Uhrzeit, $4 für die Übertragungsmeldung, $5 für den Namen des Projektarchivs oder $6 für die ID des Änderungssatzes.'; +$s_plugin_Source_bugfix_message_view_status = 'Automatische Erledigung: Sichtbarkeit der Eintragsnotiz'; +$s_plugin_Source_bugfix_message_view_status_info = 'Hinweis: Wenn der Autor/Comitter des Änderungssatzes nicht das Privileg private_bugnote_threshold besitzt, wird die Eintragsnotiz immer öffentlich sein.'; $s_plugin_Source_reset = 'Zurücksetzen'; $s_plugin_Source_menu_links = 'Hauptmenüeinträge'; $s_plugin_Source_show_repo_link = 'Projektarchive'; -$s_plugin_Source_show_search_link = 'Suchen'; +$s_plugin_Source_show_search_link = 'Projektarchive durchsuchen'; $s_plugin_Source_show_repo_stats = 'Statistiken zum Projektarchiv'; $s_plugin_Source_enabled_features = 'Aktivierte Funktionen'; $s_plugin_Source_enable_linking = 'Änderungssätze automatisch verknüpfen ([sv]:<reponame>:<revision>:)'; @@ -104,6 +109,7 @@ $s_plugin_Source_enable_resolving = 'Eintrag automatisch auf "Erledigt" setzen'; $s_plugin_Source_enable_message = 'Nach dem automatischen Erledigen eine Eintragsnotiz hinzufügen'; $s_plugin_Source_enable_porting = 'Portierungsstatus'; +$s_plugin_Source_enable_product_matrix = 'Integration von Product Matrix'; $s_plugin_Source_branch_mapping = 'Automatische Versionszuordnung'; $s_plugin_Source_mapping_update = 'Aktualisiere Zuordnung'; @@ -134,4 +140,7 @@ $s_plugin_Source_invalid_repo = 'Ungültige Bezeichnung für das Projektarchiv'; $s_plugin_Source_invalid_changeset = 'Die Informationen zu dem Änderungssatz konnten nicht ermittelt werden'; +$s_plugin_Source_import_latest_failed = 'Partieller Import des Projektarchivs fehlgeschlagen.'; $s_plugin_Source_import_full_failed = 'Vollständiger Import des Projektarchivs fehlgeschlagen.'; + +$s_plugin_Source_changeset_column_title = 'C'; diff --git a/Source/lang/strings_russian.txt b/Source/lang/strings_russian.txt index 4948356..003d935 100644 --- a/Source/lang/strings_russian.txt +++ b/Source/lang/strings_russian.txt @@ -7,8 +7,8 @@ $s_plugin_Source_title = 'Интеграция контроля версий'; $s_plugin_Source_description = 'Интеграция контроля версий реализует абстрактную модель API для поддержки любых систем контроля версий.'; -$s_plugin_Source_repository = 'репозитарий'; -$s_plugin_Source_repositories = 'репозитарии'; +$s_plugin_Source_repository = 'репозиторий'; +$s_plugin_Source_repositories = 'репозитории'; $s_plugin_Source_changeset = 'изменения'; $s_plugin_Source_changesets = 'изменения'; $s_plugin_Source_file = 'файл'; @@ -57,15 +57,15 @@ $s_plugin_Source_pending = 'ожидание'; $s_plugin_Source_na = 'Н/Д'; -$s_plugin_Source_create_repository = 'создать репозитарий'; -$s_plugin_Source_manage_repository = 'настроить репозитарий'; -$s_plugin_Source_update_repository = 'обновить репозитарий'; -$s_plugin_Source_delete_repository = 'удалить репозитарий'; +$s_plugin_Source_create_repository = 'создать репозиторий'; +$s_plugin_Source_manage_repository = 'настроить репозиторий'; +$s_plugin_Source_update_repository = 'обновить репозиторий'; +$s_plugin_Source_delete_repository = 'удалить репозиторий'; $s_plugin_Source_update_configuration = 'обновить конфигурацию'; $s_plugin_Source_search_changesets = 'поиск изменений'; $s_plugin_Source_back = 'назад'; -$s_plugin_Source_back_repo = 'назад в репозитарий'; +$s_plugin_Source_back_repo = 'назад в репозиторий'; $s_plugin_Source_back_changeset = 'назад в список изменений'; $s_plugin_Source_import_full = 'импортировать все'; $s_plugin_Source_import_latest = 'импортировать последние изменения'; @@ -100,11 +100,11 @@ $s_plugin_Source_bugfix_message_view_status_info = 'Примечание: если изменения автора/коммитера не имеет доступа private_bugnote_threshold, комментарий будет иметь публичный доступ при этих настройках.'; $s_plugin_Source_reset = 'сбросить в настройки по-умолчанию'; $s_plugin_Source_menu_links = 'Ссылки в главном меню'; -$s_plugin_Source_show_repo_link = 'репозитарии'; +$s_plugin_Source_show_repo_link = 'репозитории'; $s_plugin_Source_show_search_link = 'поиск'; -$s_plugin_Source_show_repo_stats = 'статистика репозитария'; +$s_plugin_Source_show_repo_stats = 'статистика репозитория'; $s_plugin_Source_enabled_features = 'Доступные возможности'; -$s_plugin_Source_enable_linking = 'связывание изменений ([sv]:<имярепозитария>:<ревизия>:)'; +$s_plugin_Source_enable_linking = 'связывание изменений ([sv]:<имярепозитория>:<ревизия>:)'; $s_plugin_Source_enable_mapping = 'маппинг веток'; $s_plugin_Source_enable_resolving = 'решеные исправленные задачи'; $s_plugin_Source_enable_message = 'оповещение при исправлении ошибки'; @@ -128,16 +128,16 @@ $s_plugin_Source_changeset_attached = 'добавлены изменения'; $s_plugin_Source_changeset_removed = 'изменения удалены'; -$s_plugin_Source_ensure_delete = 'Вы действительно хотите удалить репозитарий "%s" и всю его историю?'; -$s_plugin_Source_ensure_import_full = 'Предпринимается попытка создать новую копию Вашего репозитария, с удалением всей старой информации репозитария, и может понадобиться доступ к системе. Хотите продолжить?'; -$s_plugin_Source_ensure_import_latest = 'Предпринимается попытка импортировать изменения в Ваш репозитарий, и может понадобиться доступ к системе. Хотите продолжить?'; +$s_plugin_Source_ensure_delete = 'Вы действительно хотите удалить репозиторий "%s" и всю его историю?'; +$s_plugin_Source_ensure_import_full = 'Предпринимается попытка создать новую копию Вашего репозитория, с удалением всей старой информации репозитория, и может понадобиться доступ к системе. Хотите продолжить?'; +$s_plugin_Source_ensure_import_latest = 'Предпринимается попытка импортировать изменения в Ваш репозиторий, и может понадобиться доступ к системе. Хотите продолжить?'; $s_plugin_Source_import_results = 'результаты импорта'; $s_plugin_Source_import_stats = 'импортировано %s изменений, %s файлов, и %s описаний ошибок.'; $s_plugin_Source_import_repo_error = 'В процедуре импорта произошла ошибка.'; $s_plugin_Source_invalid_checkin_url = 'ошибка в удаленном адресе для проверки'; $s_plugin_Source_invalid_import_url = 'ошибка в удаленном адресе для импорта'; -$s_plugin_Source_invalid_repo = 'неправильное имя репозитария'; +$s_plugin_Source_invalid_repo = 'неправильное имя репозитория'; $s_plugin_Source_invalid_changeset = 'информация о изменениях будет загружена'; $s_plugin_Source_import_latest_failed = 'ошибка импорта последних данных.'; diff --git a/SourceBitBucket/SourceBitBucket.php b/SourceBitBucket/SourceBitBucket.php index 9bd2e70..8d41ddc 100755 --- a/SourceBitBucket/SourceBitBucket.php +++ b/SourceBitBucket/SourceBitBucket.php @@ -18,7 +18,7 @@ $this->name = plugin_lang_get( 'title' ); $this->description = plugin_lang_get( 'description' ); - $this->version = '0.18'; + $this->version = '0.19'; $this->requires = array( 'MantisCore' => '1.2.16', 'Source' => '0.18', @@ -186,7 +186,7 @@ $t_commits[] = $t_commit['id']; } - $t_refData = split( '/', $p_data['ref'] ); + $t_refData = explode( '/', $p_data['ref'] ); $t_branch = $t_refData[2]; return $this->import_commits( $p_repo, $t_commits, $t_branch ); @@ -207,7 +207,16 @@ $t_uri = $this->api_url10( "repositories/$t_username/$t_reponame/branches" ); $t_json = $this->api_json_url( $p_repo, $t_uri ); $t_branches = array(); - foreach ( $t_json as $t_branch ) $t_branches[] = $t_branch->branch; + foreach ( $t_json as $t_branchname => $t_branch ) { + if(isset($t_branchname)) { + if (strpos($t_branchname, '/') !== FALSE) { + $t_branches[] = $t_branch->raw_node; + } else { + $t_branches[] = $t_branchname; + } + } + } + $t_branches = array_unique($t_branches); } $t_changesets = array(); @@ -216,7 +225,7 @@ foreach ( $t_branches as $t_branch ) { $t_query = "SELECT parent FROM $t_changeset_table WHERE repo_id=" . db_param() . ' AND branch=' . db_param() . - 'ORDER BY timestamp ASC'; + ' ORDER BY timestamp ASC'; $t_result = db_query_bound( $t_query, array($p_repo->id, $t_branch), 1 ); $t_commits = array($t_branch); diff --git a/SourceBitBucket/lang/strings_english.txt b/SourceBitBucket/lang/strings_english.txt index 79e0ec8..3c4f423 100644 --- a/SourceBitBucket/lang/strings_english.txt +++ b/SourceBitBucket/lang/strings_english.txt @@ -7,9 +7,9 @@ $s_plugin_SourceBitBucket_bit_basic_login = 'BitBucket login (for basic auth)'; $s_plugin_SourceBitBucket_bit_basic_pwd = 'BitBucket password (for basic auth)'; -$s_plugin_SourceBitBucket_bit_username = 'BitBucket Username'; +$s_plugin_SourceBitBucket_bit_username = 'BitBucket Username/Teamname'; $s_plugin_SourceBitBucket_bit_reponame = 'BitBucket Repository Name
(no spaces; must match the name as received from the webservice\'s payload)'; -$s_plugin_SourceBitBucket_master_branch = 'Primary Branches
(comma-separated list)'; +$s_plugin_SourceBitBucket_master_branch = 'Primary Branches
(comma-separated list, * imports all branches)'; $s_plugin_SourceBitBucket_repo_authorized = '

MantisBT is now authorized to access this BitBucket repository.

'; $s_plugin_SourceBitBucket_repo_authorization_failed = '

Sorry, MantisBT could not be authorized to access this BitBucket repository.

'; diff --git a/SourceBitBucket/lang/strings_german.txt b/SourceBitBucket/lang/strings_german.txt new file mode 100644 index 0000000..3186df9 --- /dev/null +++ b/SourceBitBucket/lang/strings_german.txt @@ -0,0 +1,18 @@ +(keine Leerzeichen; muss mit dem Namen aus dem Webservice Payload übereinstimmen)'; +$s_plugin_SourceBitBucket_master_branch = 'Zu importierende Branches
(getrennt mit Komma, * importiert alle Zweige)'; + +$s_plugin_SourceBitBucket_repo_authorized = '

MantisBT ist nun autorisiert, um auf das BitBucket Repository zuzugreifen.

'; +$s_plugin_SourceBitBucket_repo_authorization_failed = '

Sorry, MantisBT konnte nicht autorisiert werden, um auf das BitBucket Repository zuzugreifen.

'; + +$s_plugin_SourceBitBucket_oauth_authorization = 'BitBucket OAuth Authorization'; +$s_plugin_SourceBitBucket_back_repo = 'Zurück zum Repository'; \ No newline at end of file diff --git a/SourceBitBucket/lang/strings_russian.txt b/SourceBitBucket/lang/strings_russian.txt index d50e865..30391d0 100644 --- a/SourceBitBucket/lang/strings_russian.txt +++ b/SourceBitBucket/lang/strings_russian.txt @@ -8,10 +8,10 @@ $s_plugin_SourceBitBucket_bit_basic_login = 'BitBucket login (for basic auth)'; $s_plugin_SourceBitBucket_bit_basic_pwd = 'BitBucket password (for basic auth)'; $s_plugin_SourceBitBucket_bit_username = 'Имя пользователя BitBucket'; -$s_plugin_SourceBitBucket_bit_reponame = 'Имя репозитария BitBucket
(только имя в нижней раскладке)'; +$s_plugin_SourceBitBucket_bit_reponame = 'Имя репозитория BitBucket
(только имя в нижней раскладке)'; $s_plugin_SourceBitBucket_master_branch = 'Главные ветки
(список разделенный запятыми)'; -$s_plugin_SourceBitBucket_repo_authorized = '

MantisBT авторизован в репозитарии BitBucket.

'; -$s_plugin_SourceBitBucket_repo_authorization_failed = '

Извините, MantisBT не может авторизоваться к этому репозитарию BitBucket.

'; +$s_plugin_SourceBitBucket_repo_authorized = '

MantisBT авторизован в репозитории BitBucket.

'; +$s_plugin_SourceBitBucket_repo_authorization_failed = '

Извините, MantisBT не может авторизоваться к этому репозиторию BitBucket.

'; -$s_plugin_SourceBitBucket_back_repo = 'назад в репозитарии'; +$s_plugin_SourceBitBucket_back_repo = 'назад в репозитории'; diff --git a/SourceCgit/SourceCgit.php b/SourceCgit/SourceCgit.php index b2994c4..62a7eed 100644 --- a/SourceCgit/SourceCgit.php +++ b/SourceCgit/SourceCgit.php @@ -129,8 +129,8 @@ # Once it reaches here it is decoded. Hence we split by a space # were as the curl command uses a '+' character instead. # i.e. DATA=`echo $INPUT | sed -e 's/ /+/g'` - list ( , $t_commit_id, $t_branch) = split(' ', $p_data); - list ( , , $t_branch) = split('/', $t_branch); + list( , $t_commit_id, $t_branch ) = explode( ' ', $p_data ); + list( , , $t_branch ) = explode( '/', $t_branch ); $master_branches = map( 'trim', explode( ',', $p_repo->info['master_branch'])); if (!in_array($t_branch,$master_branches) ) { diff --git a/SourceGithub/SourceGithub.php b/SourceGithub/SourceGithub.php index fd3e9c2..c7728df 100755 --- a/SourceGithub/SourceGithub.php +++ b/SourceGithub/SourceGithub.php @@ -10,6 +10,9 @@ require_once( config_get( 'core_path' ) . 'json_api.php' ); class SourceGithubPlugin extends MantisSourcePlugin { + + const ERROR_INVALID_PRIMARY_BRANCH = 'invalid_branch'; + public function register() { $this->name = plugin_lang_get( 'title' ); $this->description = plugin_lang_get( 'description' ); @@ -25,6 +28,16 @@ $this->url = 'http://noswap.com'; } + public function errors() { + $t_errors_list = array( + self::ERROR_INVALID_PRIMARY_BRANCH, + ); + foreach( $t_errors_list as $t_error ) { + $t_errors[$t_error] = plugin_lang_get( 'error_' . $t_error ); + } + return $t_errors; + } + public $type = 'github'; public function show_type() { @@ -181,9 +194,8 @@ $f_hub_app_secret = gpc_get_string( 'hub_app_secret' ); $f_master_branch = gpc_get_string( 'master_branch' ); - if ( !preg_match( '/\*|^[a-zA-Z0-9_\., -]*$/', $f_master_branch ) ) { - echo 'Invalid parameter: \'Primary Branch\''; - trigger_error( ERROR_GENERIC, ERROR ); + if ( !preg_match( '/^(\*|[a-zA-Z0-9_\., -]*)$/', $f_master_branch ) ) { + plugin_error( self::ERROR_INVALID_PRIMARY_BRANCH ); } $p_repo->info['hub_username'] = $f_hub_username; @@ -273,7 +285,7 @@ $t_commits[] = $t_commit['id']; } - $t_refData = split('/',$p_data['ref']); + $t_refData = explode( '/',$p_data['ref'] ); $t_branch = $t_refData[2]; return $this->import_commits( $p_repo, $t_commits, $t_branch ); diff --git a/SourceGithub/lang/strings_english.txt b/SourceGithub/lang/strings_english.txt index 3437143..c37aa28 100644 --- a/SourceGithub/lang/strings_english.txt +++ b/SourceGithub/lang/strings_english.txt @@ -24,3 +24,5 @@ $s_plugin_SourceGithub_oauth_authorization = 'GitHub OAuth Authorization'; $s_plugin_SourceGithub_back_repo = 'Back to Repository'; + +$s_plugin_SourceGithub_error_invalid_branch = 'Invalid character in Primary Branch'; diff --git a/SourceGithub/lang/strings_german.txt b/SourceGithub/lang/strings_german.txt index c63261d..24267a4 100644 --- a/SourceGithub/lang/strings_german.txt +++ b/SourceGithub/lang/strings_german.txt @@ -11,3 +11,5 @@ $s_plugin_SourceGithub_hub_username = 'GitHub Benutzername'; $s_plugin_SourceGithub_hub_reponame = 'GitHub Projektarchiv
(Klein Leerzeichen)'; $s_plugin_SourceGithub_master_branch = 'Hauptzweige
(kommaseparierte Liste)'; + +$s_plugin_SourceGithub_error_invalid_branch = 'Ungültige Zeichen in der Hauptzweige'; diff --git a/SourceGithub/lang/strings_russian.txt b/SourceGithub/lang/strings_russian.txt index 80ef55f..56b7b65 100644 --- a/SourceGithub/lang/strings_russian.txt +++ b/SourceGithub/lang/strings_russian.txt @@ -9,17 +9,17 @@ $s_plugin_SourceGithub_description = 'Добавляет интеграцию GitHub в Source Integration framework.'; $s_plugin_SourceGithub_hub_username = 'Имя пользователя GitHub'; -$s_plugin_SourceGithub_hub_reponame = 'Имя репозитария GitHub
(только имя в нижней раскладке)'; -$s_plugin_SourceGithub_hub_app_client_id = 'ID приложения GitHub
Для частных репозитариев. Создать новый в GitHub Application если надо
Application OAuth callback должен быть вида: http://адрес к вашему MantisBT/plugin.php?page=SourceGithub/SourceGithub.php'; -$s_plugin_SourceGithub_hub_app_secret = 'GitHub Application Secret
Для частных репозитариев'; -$s_plugin_SourceGithub_hub_app_access_token = 'GitHub Application Access Token
Для частных репозитариев'; +$s_plugin_SourceGithub_hub_reponame = 'Имя репозитория GitHub
(только имя в нижней раскладке)'; +$s_plugin_SourceGithub_hub_app_client_id = 'ID приложения GitHub
Для частных репозиториев. Создать новый в GitHub Application если надо
Application OAuth callback должен быть вида: http://адрес к вашему MantisBT/plugin.php?page=SourceGithub/SourceGithub.php'; +$s_plugin_SourceGithub_hub_app_secret = 'GitHub Application Secret
Для частных репозиториев'; +$s_plugin_SourceGithub_hub_app_access_token = 'GitHub Application Access Token
Для частных репозиториев'; $s_plugin_SourceGithub_master_branch = 'Главные ветки
(список разделенный запятыми)'; $s_plugin_SourceGithub_hub_app_client_id_secret_missing = 'Н/Д
Вы должны ввести ID приложение GitHub & Secret и обновить прежде чем сможете авторизоваться'; $s_plugin_SourceGithub_hub_app_authorize = 'Авторизоваться'; $s_plugin_SourceGithub_hub_app_authorized = 'Авторизовано'; -$s_plugin_SourceGithub_repo_authorized = '

MantisBT авторизован в репозитарии GitHub.

'; -$s_plugin_SourceGithub_repo_authorization_failed = '

Извините, MantisBT не может авторизоваться к этому репозитарию GitHub.

'; +$s_plugin_SourceGithub_repo_authorized = '

MantisBT авторизован в репозитории GitHub.

'; +$s_plugin_SourceGithub_repo_authorization_failed = '

Извините, MantisBT не может авторизоваться к этому репозиторию GitHub.

'; $s_plugin_SourceGithub_oauth_authorization = 'OAuth авторизация GitHub'; -$s_plugin_SourceGithub_back_repo = 'назад в репозитарии'; +$s_plugin_SourceGithub_back_repo = 'назад в репозитории'; diff --git a/SourceGitlab/README.md b/SourceGitlab/README.md new file mode 100644 index 0000000..8176b2f --- /dev/null +++ b/SourceGitlab/README.md @@ -0,0 +1,40 @@ +## One time Mantis source integration setup + + - Copy the source integration plugins into mantis "plugins/" subdirectory + - Install required plugins with Mantis plugin admin page + - Get the "API Key" (like 'abcdeb8129a4451a35f47881') from the Source Integration plugin "manage_config_page" + http://mantis.server.intra/plugin.php?page=Source/manage_config_page + +## Gitlab setup + + - Login with an Owner (or Adminstrator) of a Project, go to "Projet settings", "Web hooks", + add a Push hook with Mantis url and Mantis Source Integration plugin "API Key": + http://mantis.server.intra/plugin.php?page=Source/checkin&api_key=abcdeb8129a4451a35f47881 + - Create a user with (at least) read access to the repository. For a public repository, any user would work. + Login with this user, go to "Profile settings", "Account", and copy the "Private token". + +## Mantis repository setup + + - In Mantis administration, create a repository with a unique name and of type "Gitlab". + - URL field is not used by the plugin. + - Gitlab config fields are: + - hub_root: root url of the Gitlab webserver, required to access Web API + - hub_repoid: id of the Gitlab projet, starting from 1 for the first created project (auto-filed if reponame is valid and readable for the user) + - hub_reponame: full name of the project in the form "group-namespace/project-name" + - hub_app_secret: the "Private token" of a user with at least a read access to the project. + - master_branch: use '*' or a list of branches to track + +``` +array(5) { + ["hub_root"]=> + string(27) "http://gitlab.server.intra" + ["hub_repoid"]=> + string(1) "5" + ["hub_reponame"]=> + string(30) "dispora/dispora" + ["hub_app_secret"]=> + string(20) "abcde-private-token" + ["master_branch"]=> + string(1) "*" +} +``` diff --git a/SourceGitlab/SourceGitlab.php b/SourceGitlab/SourceGitlab.php new file mode 100644 index 0000000..5a2da60 --- /dev/null +++ b/SourceGitlab/SourceGitlab.php @@ -0,0 +1,398 @@ +name = plugin_lang_get( 'title' ); + $this->description = plugin_lang_get( 'description' ); + + $this->version = '1.0.2'; + $this->requires = array( + 'MantisCore' => '1.2.0', + 'Source' => '0.16', + ); + + $this->author = 'Johannes Goehr'; + $this->contact = 'johannes.goehr@mobilexag.de'; + $this->url = 'http://www.mobilexag.de'; + } + + public function errors() { + $t_errors_list = array( + self::ERROR_INVALID_PRIMARY_BRANCH, + ); + foreach( $t_errors_list as $t_error ) { + $t_errors[$t_error] = plugin_lang_get( 'error_' . $t_error ); + } + return $t_errors; + } + + public $type = 'gitlab'; + + public function show_type() { + return plugin_lang_get( 'gitlab' ); + } + + public function show_changeset( $p_repo, $p_changeset ) { + $t_ref = substr( $p_changeset->revision, 0, 8 ); + $t_branch = $p_changeset->branch; + + return "$t_branch $t_ref"; + } + + public function show_file( $p_repo, $p_changeset, $p_file ) { + return "$p_file->action - $p_file->filename"; + } + + public function url_repo( $p_repo, $p_changeset=null ) { + $t_root = $p_repo->info['hub_root']; + $t_reponame = $p_repo->info['hub_reponame']; + $t_ref = ""; + + if ( !is_null( $p_changeset ) ) { + $t_ref = "/$p_changeset->revision"; + } + if ( !is_null( $t_ref ) ) { + return "$t_root/$t_reponame/"; + } + return "$t_root/$t_reponame/tree/$t_ref"; + } + + public function url_changeset( $p_repo, $p_changeset ) { + $t_root = $p_repo->info['hub_root']; + $t_reponame = $p_repo->info['hub_reponame']; + $t_ref = $p_changeset->revision; + + return "$t_root/$t_reponame/commit/$t_ref"; + } + + public function url_file( $p_repo, $p_changeset, $p_file ) { + $t_root = $p_repo->info['hub_root']; + $t_reponame = $p_repo->info['hub_reponame']; + $t_ref = $p_changeset->revision; + $t_filename = $p_file->filename; + + return "$t_root/$t_reponame/blob/$t_ref/$t_filename"; + } + + public function url_diff( $p_repo, $p_changeset, $p_file ) { + $t_root = $p_repo->info['hub_root']; + $t_reponame = $p_repo->info['hub_reponame']; + $t_ref = $p_changeset->revision; + $t_filename = $p_file->filename; + + return "t_root/$t_reponame/commit/$t_ref?view=parallel"; + } + +public function update_repo_form( $p_repo ) { + $t_hub_root = null; + $t_hub_repoid = null; + $t_hub_app_secret = null; + + if ( isset( $p_repo->info['hub_root'] ) ) { + $t_hub_root = $p_repo->info['hub_root']; + } + if ( isset( $p_repo->info['hub_repoid'] ) ) { + $t_hub_repoid = $p_repo->info['hub_repoid']; + } + if ( isset( $p_repo->info['hub_reponame'] ) ) { + $t_hub_reponame = $p_repo->info['hub_reponame']; + } + if ( isset( $p_repo->info['hub_app_secret'] ) ) { + $t_hub_app_secret = $p_repo->info['hub_app_secret']; + } + if ( isset( $p_repo->info['master_branch'] ) ) { + $t_master_branch = $p_repo->info['master_branch']; + } else { + $t_master_branch = 'master'; + } +?> +> + + + +> + + + +> + + + +> + + + +> + + + +api_uri( $p_repo, "projects" ); + $t_member = null; + $t_json = json_url( $t_uri, $t_member ); + + $f_hub_repoid = 'RepoName is invalid'; + if( !is_null( $t_json ) ) { + foreach( $t_json as $project ) { + if ( property_exists( $project, 'path_with_namespace' ) + && $project->path_with_namespace == $f_hub_reponame + && property_exists( $project, 'id' ) + ) { + $f_hub_repoid = (string)$project ->id; + } + } + } + } + $f_hub_app_secret = gpc_get_string( 'hub_app_secret' ); + $f_master_branch = gpc_get_string( 'master_branch' ); + + if ( !preg_match( '/^(\*|[a-zA-Z0-9_\., -]*)$/', $f_master_branch ) ) { + plugin_error( self::ERROR_INVALID_PRIMARY_BRANCH ); + } + + $p_repo->info['hub_root'] = $f_hub_root; + $p_repo->info['hub_repoid'] = $f_hub_repoid; + $p_repo->info['hub_reponame'] = $f_hub_reponame; + $p_repo->info['hub_app_secret'] = $f_hub_app_secret; + $p_repo->info['master_branch'] = $f_master_branch; + + return $p_repo; + } + + private function api_uri( $p_repo, $p_path ) { + $t_root = $p_repo->info['hub_root']; + $t_uri = $t_root . '/api/v3/' . $p_path; + + if( isset( $p_repo->info['hub_app_secret'] ) ) { + $t_access_token = $p_repo->info['hub_app_secret']; + if ( !is_blank( $t_access_token ) ) { + $t_uri .= '?private_token=' . $t_access_token; + } + } + + return $t_uri; + } + + public function precommit() { + $f_payload = file_get_contents( "php://input" ); + if ( is_null( $f_payload ) ) { + return; + } + + $t_data = json_decode( $f_payload, true ); + + $t_repoid = $t_data['project_id']; + $t_repo_table = plugin_table( 'repository', 'Source' ); + + $t_query = "SELECT * FROM $t_repo_table WHERE info LIKE " . db_param(); + $t_result = db_query_bound( $t_query, array( '%' . $t_repoid . '%' ) ); + + if ( db_num_rows( $t_result ) < 1 ) { + return; + } + while ( $t_row = db_fetch_array( $t_result ) ) { + $t_repo = new SourceRepo( $t_row['type'], $t_row['name'], $t_row['url'], $t_row['info'] ); + $t_repo->id = $t_row['id']; + if ( $t_repo->info['hub_repoid'] == $t_repoid ) { + return array( 'repo' => $t_repo, 'data' => $t_data ); + } + } + return; + } + + public function commit( $p_repo, $p_data ) { + $t_commits = array(); + foreach( $p_data['commits'] as $t_commit ) { + $t_commits[] = $t_commit['id']; + } + + # extract branch name 'refs/heads/issue/branch-description' => ['refs', 'heads', 'issue/branch-description'] + $t_refData = explode( '/', $p_data['ref'], 3 ); + $t_branch = $t_refData[2]; + + return $this->import_commits( $p_repo, $t_commits, $t_branch ); + } + + public function import_full( $p_repo ) { + echo '
';
+
+		$t_branch = $p_repo->info['master_branch'];
+		if ( is_blank( $t_branch ) ) {
+			$t_branch = 'master';
+		}
+
+		# if we're not allowed everything, populate an array of what we are allowed
+		if( $t_branch != '*' ) {
+			$t_branches_allowed = array_map( 'trim', explode( ',', $t_branch ) );
+		}
+
+		# Always pull back full list of repos
+		$t_repoid = $p_repo->info['hub_repoid'];
+		$t_uri = $this->api_uri( $p_repo, "projects/$t_repoid/repository/branches" );
+
+		$t_member = null;
+		$t_json = json_url( $t_uri, $t_member );
+		$t_branches = array();
+		foreach( $t_json as $t_branch ) {
+			if( empty( $t_branches_allowed ) || in_array( $t_branch->name, $t_branches_allowed ) ) {
+				$t_branches[] = $t_branch;
+			}
+		}
+
+		$t_changesets = array();
+
+		$t_changeset_table = plugin_table( 'changeset', 'Source' );
+
+		foreach( $t_branches as $t_branch ) {
+			$t_query = "SELECT parent FROM $t_changeset_table
+				WHERE repo_id=" . db_param() . ' AND branch=' . db_param() .
+				' ORDER BY timestamp ASC';
+			$t_result = db_query_bound( $t_query, array( $p_repo->id, $t_branch->name ), 1 );
+
+			$t_commits = array( $t_branch->commit->id );
+			if ( db_num_rows( $t_result ) > 0 ) {
+				$t_parent = db_result( $t_result );
+				echo "Oldest '$t_branch->name' branch parent: '$t_parent'\n";
+
+				if ( !empty( $t_parent ) ) {
+					$t_commits[] = $t_parent;
+					echo "Parents not empty";
+				}
+				echo "Parents  empty";
+			}
+
+			$t_changesets = array_merge( $t_changesets, $this->import_commits( $p_repo, $t_commits, $t_branch->name ) );
+		}
+
+		echo '
'; + + return $t_changesets; + } + + public function import_latest( $p_repo ) { + return $this->import_full( $p_repo ); + } + + public function import_commits( $p_repo, $p_commit_ids, $p_branch='' ) { + static $s_parents = array(); + static $s_counter = 0; + $t_repoid = $p_repo->info['hub_repoid']; + + if ( is_array( $p_commit_ids ) ) { + $s_parents = array_merge( $s_parents, $p_commit_ids ); + } else { + $s_parents[] = $p_commit_ids; + } + + $t_changesets = array(); + + while( count( $s_parents ) > 0 && $s_counter < 200 ) { + $t_commit_id = array_shift( $s_parents ); + echo "Retrieving $t_commit_id ...
"; + $t_uri = $this->api_uri( $p_repo, "projects/$t_repoid/repository/commits/$t_commit_id" ); + $t_member = null; + $t_json = json_url( $t_uri, $t_member ); + if ( false === $t_json || is_null( $t_json ) ) { + # Some error occured retrieving the commit + echo "failed.\n"; + continue; + } else if ( !property_exists( $t_json, 'id' ) ) { + echo "failed ($t_json->message).\n"; + continue; + } + + list( $t_changeset, $t_commit_parents ) = $this->json_commit_changeset( $p_repo, $t_json, $p_branch ); + if ( $t_changeset ) { + $t_changesets[] = $t_changeset; + } + + $s_parents = array_merge( $s_parents, $t_commit_parents ); + } + + $s_counter = 0; + return $t_changesets; + } + + private function json_commit_changeset( $p_repo, $p_json, $p_branch='' ) { + echo "processing $p_json->id ... "; + if ( !SourceChangeset::exists( $p_repo->id, $p_json->id ) ) { + $t_parents = array(); + foreach( $p_json->parent_ids as $t_parent ) { + $t_parents[] = $t_parent; + } + + $t_changeset = new SourceChangeset( + $p_repo->id, + $p_json->id, + $p_branch, + date( 'Y-m-d H:i:s', strtotime( $p_json->authored_date ) ), + $p_json->author_name, + $p_json->message + ); + + if ( count( $p_json->parents ) > 0 ) { + $t_parent = $p_json->parents[0]; + $t_changeset->parent = $t_parent->id; + } + + $t_changeset->author_email = $p_json->author_email; + $t_changeset->save(); + + echo "saved.\n"; + return array( $t_changeset, $t_parents ); + } else { + echo "already exists.\n"; + return array( null, array() ); + } + } + + + + public static function url_post( $p_url, $p_post_data ) { + $t_post_data = http_build_query( $p_post_data ); + + # Use the PHP cURL extension + if( function_exists( 'curl_init' ) ) { + $t_curl = curl_init( $p_url ); + curl_setopt( $t_curl, CURLOPT_RETURNTRANSFER, true ); + curl_setopt( $t_curl, CURLOPT_POST, true ); + curl_setopt( $t_curl, CURLOPT_POSTFIELDS, $t_post_data ); + + $t_data = curl_exec( $t_curl ); + curl_close( $t_curl ); + + return $t_data; + } else { + # Last resort system call + $t_url = escapeshellarg( $p_url ); + $t_post_data = escapeshellarg( $t_post_data ); + return shell_exec( 'curl ' . $t_url . ' -d ' . $t_post_data ); + } + } + +} diff --git a/SourceGitlab/lang/strings_english.txt b/SourceGitlab/lang/strings_english.txt new file mode 100644 index 0000000..faa0222 --- /dev/null +++ b/SourceGitlab/lang/strings_english.txt @@ -0,0 +1,20 @@ +The full URL of your GitLab instance'; +$s_plugin_SourceGitlab_hub_repoid = 'GitLab Repository ID
Numerical, filled in automatically if empty'; +$s_plugin_SourceGitlab_hub_reponame = 'GitLab Repository Name
Name & Namespace with no leading or trailing slashes (namespace/project)'; +$s_plugin_SourceGitlab_hub_app_secret = 'GitLab API Key
api key for mantis user (continuous user)'; +$s_plugin_SourceGitlab_master_branch = 'Primary Branches
(comma-separated list or *)'; + +$s_plugin_SourceGitlab_back_repo = 'Back to Repository'; + +$s_plugin_SourceGitlab_error_invalid_branch = 'Invalid character in Primary Branch'; diff --git a/SourceGitlab/lang/strings_german.txt b/SourceGitlab/lang/strings_german.txt new file mode 100644 index 0000000..e4dbb86 --- /dev/null +++ b/SourceGitlab/lang/strings_german.txt @@ -0,0 +1,19 @@ +The full URL of your GitLab instance'; +$s_plugin_SourceGitlab_hub_repoid = 'GitLab Repository ID
wird über Repo Name automatisch ermittelt falls leer'; +$s_plugin_SourceGitlab_hub_reponame = 'GitLab Repository Name
Ohne Namespace Slash am Anfang oder Ende (namespace/project)'; +$s_plugin_SourceGitlab_hub_app_secret = 'GitLab API Key
API Schlüssel für den Mantis Benutzer (continuous Benutzer)'; +$s_plugin_SourceGitlab_master_branch = 'Hauptzweige
(Komma-separierte Liste oder *)'; + +$s_plugin_SourceGitlab_back_repo = 'Zurück zum Repository'; + +$s_plugin_SourceGitlab_error_invalid_branch = 'Ungültige Zeichen in der Hauptzweige'; diff --git a/SourceGitweb/SourceGitweb.php b/SourceGitweb/SourceGitweb.php index 28e580f..ec5a3c4 100644 --- a/SourceGitweb/SourceGitweb.php +++ b/SourceGitweb/SourceGitweb.php @@ -14,7 +14,7 @@ $this->name = plugin_lang_get( 'title' ); $this->description = plugin_lang_get( 'description' ); - $this->version = '0.17'; + $this->version = '0.18'; $this->requires = array( 'MantisCore' => '1.2.0', 'Source' => '0.16', @@ -309,7 +309,7 @@ # Parse for changed file data $t_commit['files'] = array(); - preg_match_all( '#class="list".*?h=(\w*)[^>]*>([^<]*)(?:(?:(.*?)<.a>(?:(?:<.td>(trunk/branches/tags)'; -$s_plugin_SourceSVN_trunk_path = 'Путь к Trunk
(нестандартный репозитарий)'; -$s_plugin_SourceSVN_branch_path = 'Путь к Branch
(нестандартный репозитарий)'; -$s_plugin_SourceSVN_tag_path = 'Путь к Tag
(нестандартный репозитарий)'; -$s_plugin_SourceSVN_ignore_paths = 'Игнорировать остальные пути
(нестандартный репозитарий)'; +$s_plugin_SourceSVN_standard_repo = 'Стандартный репозиторий
(trunk/branches/tags)'; +$s_plugin_SourceSVN_trunk_path = 'Путь к Trunk
(нестандартный репозиторий)'; +$s_plugin_SourceSVN_branch_path = 'Путь к Branch
(нестандартный репозиторий)'; +$s_plugin_SourceSVN_tag_path = 'Путь к Tag
(нестандартный репозиторий)'; +$s_plugin_SourceSVN_ignore_paths = 'Игнорировать остальные пути
(нестандартный репозиторий)'; $s_plugin_SourceSVN_configuration = 'Настройка'; $s_plugin_SourceSVN_update = 'Обновить'; diff --git a/SourceWebSVN/lang/strings_russian.txt b/SourceWebSVN/lang/strings_russian.txt index b800bee..0bb2187 100644 --- a/SourceWebSVN/lang/strings_russian.txt +++ b/SourceWebSVN/lang/strings_russian.txt @@ -11,4 +11,4 @@ $s_plugin_SourceWebSVN_websvn_url = 'Адрес WebSVN
(Включая последний слеш)'; $s_plugin_SourceWebSVN_websvn_multiviews = 'Режим Multiviews в WebSVN
(Отметьте если WebSVN запущен под
 Apache в режиме Multiviews)
'; $s_plugin_SourceWebSVN_websvn_name = 'WebSVN имя
(директория репозитория)'; -$s_plugin_SourceWebSVN_websvn_path = 'WebSVN путь
(от корня репозитария)'; \ No newline at end of file +$s_plugin_SourceWebSVN_websvn_path = 'WebSVN путь
(от корня репозитория)'; \ No newline at end of file diff --git a/docs/CONFIGURING.SourceGithub.md b/docs/CONFIGURING.SourceGithub.md new file mode 100644 index 0000000..cd06872 --- /dev/null +++ b/docs/CONFIGURING.SourceGithub.md @@ -0,0 +1,78 @@ +# SourceGithub Configuration + +## Description + +The **SourceGithub** extension plugin adds support for Git repositories +hosted on [GitHub](http://github.com/), both public & private as well as +personal & organization repositories. + + +## Requirements + +The **SourceGithub** plugin requires Mantis 1.2.16. See the +[README](../README.md#requirements) for further information. + +Ensure both the **Source** and **SourceGithub** plugins are installed. +See the [README](../README.md#installation) for overall instructions. + + +## Configuration + +1. Click the *Repositories* link in the navigation bar. + +2. In the *Create Repository* section: + + - Enter the repository name in the *Name* text field. + - Select *GitHub* from the *Type* pop-up menu. + - Click the *Create Repository* button. + +3. This will take you to the *Update Repository* page where you'll need to fill + in all the details for the repository: + + - The *Name* field should be pre-populated with the name you entered in Step 3a above. + + - Paste in the GitHub repository's URL in the *URL* field + (e.g. `https://github.com/mantisbt-plugins/source-integration`). + + - Enter the GitHub username of the repository's owner in the *GitHub Username* + field (e.g. "mantisbt-plugins"). + + - Enter the GitHub repository's name in the *GitHub Repository Name* field + (e.g. "source-integration"). + + - If it's a public GitHub repository, you can skip the *GitHub Application + Client ID* & *GitHub Application Secret* fields. If it's a private + repository, you'll need to configure a GitHub Developer Application: + + 1. Visit https://github.com/settings/developers and click the *Register + new application* button. + 2. Enter *MantisBT Source Integration* in the *Application name* field. + 3. Enter the URL for your Mantis installation in the *Homepage URL* + field. + 4. Enter the URL for your Mantis Source/oauth page (the URL with + `/plugin.php?page=Source/oauth` appended to it; e.g. "http://mantisbt.org/bugs/plugin.php?page=Source/oauth"). + 5. Click the *Register application* button. + 6. Switch back to your Mantis GitHub Repository configuration. + 7. Enter your GitHub Developer Application's Client ID in the *GitHub + Application Client ID* field. + 8. Enter your GitHub Developer Application's Secret in the *GitHub + Application Access Secret* field. + + - You can specify a branch or branches other than just "master" in the + *Primary Branches* field, if you like. + + - Click the *Update Repository* button. + +4. If this is a private GitHub repository, you'll need to authorize Mantis + to access your repository: + + - Click the *Update Repository* button. + + - Click the *Click to Authorize* button in the *GitHub Application + Access Token* field. If successful, it will say "MantisBT is now + authorized to access this GitHub repository." + +5. Click the *Import Everything* button to test connectivity and perform an + initial import of the repository changesets. + + **Note:** This may take a long time or even fail for large repositories.