'textfield', '#title' => t('E-mail address'), '#default_value' => variable_get('versioncontrol_email_address', 'versioncontrol@example.com'), '#description' => t('The e-mail address of the VCS administrator.'), '#weight' => -10, ); $form['versioncontrol_register_form'] = array( '#type' => 'fieldset', '#title' => t('Account registration form strings'), '#description' => t('The following messages are shown on the !repository-selection-page that leads to the account registration form.', array('!repository-selection-page' => l(t('repository selection page'), 'versioncontrol/register/demo'))), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 0, ); $form['versioncontrol_register_form']['versioncontrol_registration_message_unauthorized'] = array( '#type' => 'textarea', '#title' => t('Message to unauthorized users'), '#description' => t('Message to show to anonymous users and to users without the \'use version control systems\' permission.'), '#default_value' => variable_get( 'versioncontrol_registration_message_unauthorized', $presets['versioncontrol_registration_message_unauthorized'] ), ); $form['versioncontrol_register_form']['versioncontrol_registration_message_authorized'] = array( '#type' => 'textarea', '#title' => t('Message to registering users'), '#description' => t('Message to show to users who are in the process of selecting a repository in order to create a VCS account. If there\'s only one repository on this site, users will never get to see this message.'), '#default_value' => variable_get( 'versioncontrol_registration_message_authorized', $presets['versioncontrol_registration_message_authorized'] ), ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Save configuration'), '#weight' => 100, ); return $form; } /** * Submit handler for the authorization settings form. * system_settings_form() is left out because it's nasty for modules that * hook into this form and don't want to use automatic variable saving. */ function versioncontrol_admin_settings_submit($form, &$form_state) { $value_names = array( 'versioncontrol_email_address', 'versioncontrol_registration_message_unauthorized', 'versioncontrol_registration_message_authorized', ); foreach ($value_names as $name) { variable_set($name, $form_state['values'][$name]); } } /** * Form callback for "admin/project/versioncontrol-accounts": * A list of accounts with filtering possibilities. * * TODO Completely reimplement in Views. */ function versioncontrol_admin_account_list_form(&$form_state) { // Retrieve a list of all repositories with registered VCS accounts. $result = db_query('SELECT DISTINCT repo_id FROM {versioncontrol_accounts}'); $repo_ids = array(); while ($repo_id = db_result($result)) { $repo_ids[] = $repo_id; } $repositories = versioncontrol_repository_load_multiple($repo_ids); $filter_form = versioncontrol_admin_account_list_filter_form($form_state, $repositories); $filter_form['#id'] = 'versioncontrol-account-filter-form'; drupal_alter('form', $filter_form, $form_state, 'versioncontrol_admin_account_list_filter_form'); $form = array(); $form['filters'] = array_merge($filter_form, array( '#type' => 'fieldset', '#title' => t('Filter'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#prefix' => '
', '#suffix' => '
', )); // Filter the list of accounts. $accounts = versioncontrol_user_accounts_load_multiple(array(), array(), array('include unauthorized' => TRUE)); foreach (module_implements('versioncontrol_filter_accounts') as $module) { $function = $module .'_versioncontrol_filter_accounts'; $function($accounts); } $accounts = versioncontrol_admin_get_paged_accounts($accounts); $users = array(); // Retrieve more info for the accounts that survived filtering. foreach ($accounts as $uid => $usernames_by_repository) { $user = user_load($uid); if (!$user) { unset($accounts[$uid]); continue; } $users[$uid] = $user; } if (empty($users)) { $form['empty'] = array( '#type' => 'markup', '#value' => '

'. t('No accounts could be found with the current filter settings.') .'

', ); return $form; } // Retrieve total, first and last commits of each user. $statistics = VersioncontrolOperationCache::getInstance()->getStatistics(array( 'types' => array(VERSIONCONTROL_OPERATION_COMMIT), 'uids' => array_keys($users), ), array( 'group_by' => array('uid'), )); $total_operations = array(); $first_operation_dates = array(); $last_operation_dates = array(); foreach ($statistics as $user_stats) { $total_operations[$user_stats->uid] = $user_stats->total_operations; $first_operation_dates[$user_stats->uid] = t('!time ago', array( '!time' => format_interval(time() - $user_stats->first_operation_date, 1)) ); $last_operation_dates[$user_stats->uid] = t('!time ago', array( '!time' => format_interval(time() - $user_stats->last_operation_date, 1)) ); if (module_exists('commitlog')) { $total_operations[$user_stats->uid] = l($total_operations[$user_stats->uid], 'user/'. $user_stats->uid .'/track/code'); } } // Construct the user account table. $header = array(t('User'), t('Accounts'), t('Commits'), t('First commit'), t('Last commit')); $rows_by_uid = array(); foreach ($accounts as $uid => $usernames_by_repository) { $user = $users[$uid]; // Present a list of all VCS usernames and the repository where they're in. $repo_usernames = array(); foreach ($usernames_by_repository as $repo_id => $account) { if (!isset($repositories[$repo_id])) { // happens if the backend isn't loaded continue; } $formatted_username = theme('versioncontrol_account_username', $uid, $account->vcs_username, $repositories[$repo_id], array('prefer_drupal_username' => FALSE) ); $repo_name = module_exists('commitlog') ? theme('commitlog_repository', $repositories[$repo_id]) : check_plain($repositories[$repo_id]['name']); $repo_usernames[] = t('!username in !repository (!edit)', array( '!username' => $formatted_username, '!repository' => $repo_name, '!edit' => l(t('edit'), 'user/'. $uid .'/edit/versioncontrol/'. $repo_id .'/'. drupal_urlencode($account->vcs_username) ), )); } $vcs_usernames = empty($repo_usernames) ? t('VCS backend is currently disabled') : theme('item_list', $repo_usernames); $rows_by_uid[$uid] = array( theme('username', $user), $vcs_usernames, isset($total_operations[$uid]) ? $total_operations[$uid] : 0, isset($first_operation_dates[$uid]) ? $first_operation_dates[$uid] : t('n/a'), isset($last_operation_dates[$uid]) ? $last_operation_dates[$uid] : t('n/a'), ); } // Provide a possibility for backends and other modules to modify the list. foreach (module_implements('versioncontrol_alter_account_list') as $module) { $function = $module .'_versioncontrol_alter_account_list'; $function($accounts, $repositories, $header, $rows_by_uid); } // We don't want the uids in the final $rows array. $rows = array_values($rows_by_uid); // The finished user account list with account filter. $form['accounts'] = array( '#type' => 'markup', '#value' => theme('table', $header, $rows), ); if ($pager = theme('pager', NULL, variable_get('versioncontrol_admin_account_pager', 20), 0)) { $form['pager'] = array( '#type' => 'markup', '#value' => $pager, ); } return $form; } /** * Return a subset of the given accounts (10 Drupal users by default, and all * of their VCS usernames). Paging is also used by emulating pager_query(). */ function versioncontrol_admin_get_paged_accounts($accounts, $element = 0) { global $pager_page_array, $pager_total, $pager_total_items; $page = isset($_GET['page']) ? $_GET['page'] : ''; $pager_page_array = explode(',', $page); $page = empty($pager_page_array[$element]) ? 0 : $pager_page_array[$element]; $limit = variable_get('versioncontrol_admin_account_pager', 20); $i = 0; $paged_accounts = array(); foreach ($accounts as $uid => $usernames_by_repository) { if ($i >= ($page * $limit) && $i < (($page+1) * $limit)) { $paged_accounts[$uid] = $usernames_by_repository; } ++$i; } // Emulate pager_query() in order to get a proper theme('pager'). $pager_total_items[$element] = count($accounts); $pager_total[$element] = ceil($pager_total_items[$element] / $limit); $pager_page_array[$element] = max(0, min((int)$pager_page_array[$element], ((int)$pager_total[$element]) - 1)); return $paged_accounts; } /** * A form for filtering accounts which is embedded in the account list page. */ function versioncontrol_admin_account_list_filter_form(&$form_state, $repositories) { $form = array(); $repository_names = array(0 => t('All')); foreach ($repositories as $repo_id => $repository) { $repository_names[$repo_id] = check_plain($repository['name']); } if (!isset($_SESSION['versioncontrol_filter_repo_id'])) { $_SESSION['versioncontrol_filter_repo_id'] = 0; } $form['#id'] = 'versioncontrol-account-filter-form'; $form['repo_id'] = array( '#type' => 'select', '#title' => t('Repository'), '#options' => $repository_names, '#default_value' => $_SESSION['versioncontrol_filter_repo_id'], '#weight' => 10, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Filter'), '#submit' => array('versioncontrol_admin_account_list_filter_form_submit'), '#weight' => 100, ); return $form; } /** * Submit handler for the account filter form. */ function versioncontrol_admin_account_list_filter_form_submit($form, &$form_state) { $_SESSION['versioncontrol_filter_repo_id'] = $form_state['values']['repo_id']; } /** * Implementation of hook_versioncontrol_filter_accounts(): * Unset filtered accounts before they are even attempted to be displayed * on the account list. * * TODO remove, obsoleted by Views * * @param $accounts * The accounts that would normally be displayed, in the same format as the * return value of versioncontrol_user_accounts_load_multiple(). Entries in * this list may be unset by this filter function. */ function versioncontrol_versioncontrol_filter_accounts(&$accounts) { if (empty($accounts)) { return; } if (!isset($_SESSION['versioncontrol_filter_repo_id'])) { $_SESSION['versioncontrol_filter_repo_id'] = 0; } $filter_repo_id = $_SESSION['versioncontrol_filter_repo_id']; if ($filter_repo_id == 0) { return; // Don't change the list if "All" repositories are selected. } foreach ($accounts as $uid => $usernames_by_repository) { foreach ($usernames_by_repository as $repo_id => $username) { if ($repo_id != $filter_repo_id) { unset($accounts[$uid][$repo_id]); if (empty($accounts[$uid])) { unset($accounts[$uid]); } } } } } /** * Form callback for "admin/project/versioncontrol-repositories": * A simple list of repositories, sorted by version control system. */ function versioncontrol_admin_repository_list(&$form_state) { $form = array(); $backends = versioncontrol_get_backends(); foreach ($backends as $vcs => $backend) { $title = t('@vcs repositories', array('@vcs' => $backend->name)); $form[$vcs] = array( '#value' => '

'. $title .'

', ); $form[$vcs . '_repositories'] = array( '#value' => views_embed_view($backend->getViewName('repositories'), 'default', $vcs), ); } return $form; } /** * Form callback for * 'admin/project/versioncontrol-repositories/edit/%versioncontrol_repository' * and "admin/project/versioncontrol-repositories/add-$vcs": * Provide a form to edit or add a repository. * * @param $repository * The repository that is to be edited, or VERSIONCONTROL_FORM_CREATE * if the repository doesn't exist yet and should be added. * @param $vcs * If $repo_id is NULL, this should be the unique identification string * of the backend for which the repository should be added. * Otherwise, this value is ignored. */ function versioncontrol_admin_repository_edit(&$form_state, $repository, $vcs = NULL) { $backends = versioncontrol_get_backends(); $repository_exists = ($repository !== VERSIONCONTROL_FORM_CREATE); if (!$repository_exists && !isset($backends[$vcs])) { drupal_goto('admin/project/versioncontrol-repositories'); // drupal_goto() calls exit() so script execution ends right here } $form = array(); $form['#id'] = 'versioncontrol-repository-form'; if ($repository_exists) { $form['#repository'] = $repository; } $form['#vcs'] = $repository_exists ? $repository['vcs'] : $vcs; $form['#original_name'] = $repository_exists ? $repository['name'] : 0; $form['repository_information'] = array( '#type' => 'fieldset', '#title' => t('Repository information'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#weight' => 0, ); $form['repository_information']['repo_name'] = array( '#type' => 'textfield', '#title' => t('Repository name'), '#description' => t('A label for the repository that will be used in all user visible messages.'), '#default_value' => $repository_exists ? $repository['name'] : '', '#required' => TRUE, '#weight' => 0, '#size' => 40, '#maxlength' => 255, ); $form['repository_information']['root'] = array( '#type' => 'textfield', '#title' => t('Repository root'), '#description' => t('The location of the repository\'s root directory.'), '#default_value' => $repository_exists ? $repository['root'] : '', '#weight' => 5, '#size' => 60, '#maxlength' => 255, ); $text = t('Versioncontrol can map commit activity in this repository to user accounts. These options allow you to select the logic that should be used to perform the mapping between Drupal users and raw VCS data.'); $text .= '

'; $text .= t('Versioncontrol internally allows for both "authors" and "committers" to be tracked on each commit, so you can choose distinct mapping logic for each.'); $form['user_mapping'] = array( '#type' => 'fieldset', '#title' => t('User mapping'), '#description' => $text, '#collapsible' => TRUE, '#collapsed' => FALSE, '#weight' => 5, ); $form['user_mapping']['author_mapper'] = array( '#type' => 'radios', '#title' => t('Author mapping'), '#description' => t('The mapping logic to be used for "author" data. All VCSes have some sort of author field.'), '#default_value' => $repository_exists ? $repository->plugins['author_mapper'] : 0, '#options' => array(0 => '' . t('None (no mapping)') . '') + versioncontrol_user_mapping_methods_get_names(), ); $form['user_mapping']['committer_mapper'] = array( '#type' => 'radios', '#title' => t('Committer mapping'), '#description' => t('The mapping logic to be used for "committer" data. Only some VCSes distinguish between author and committer. For most use cases, it is recommended best to set this to use the same plugin as for author mapping, especially if using "None (No mapping)" for author mapping.'), '#default_value' => $repository_exists ? $repository->plugins['committer_mapper'] : 0, '#options' => array(0 => '' . t('Use Author mapping plugin') . '') + versioncontrol_user_mapping_methods_get_names(), ); if ($repository_exists && isset($repository->data['versioncontrol']['registration_message'])) { $current_registration_message = $repository->data['versioncontrol']['registration_message']; } else { $presets = _versioncontrol_get_string_presets(); $current_registration_message = $presets['versioncontrol_registration_message_repository']; } $form['account_registration'] = array( '#type' => 'fieldset', '#title' => t('Account registration'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 10, ); $form['account_registration']['authorization_method'] = array( '#type' => 'select', '#title' => t('Authorization method'), '#description' => t('Determines the way that users can register an account for this repository.'), '#options' => versioncontrol_get_authorization_methods(), '#default_value' => $repository_exists ? $repository['authorization_method'] : _versioncontrol_get_fallback_authorization_method(), '#weight' => 0, ); $form['account_registration']['repository_messages'] = array( '#type' => 'fieldset', '#title' => t('Account registration form strings'), '#description' => t('The following message is shown on the !account-registration-page for this repository.', array('!account-registration-page' => l(t('account registration page'), 'versioncontrol/register/demo/'. $repository['repo_id']))), '#collapsible' => TRUE, '#collapsed' => FALSE, '#weight' => 2, ); $form['account_registration']['repository_messages']['registration_message'] = array( '#type' => 'textarea', '#title' => t('Message to registering users'), '#description' => t('Message to show to users that are in the process of creating an account in this repository.'), '#default_value' => $current_registration_message, ); $backend_capabilities = $backends[$form['#vcs']]->capabilities; if (in_array(VERSIONCONTROL_CAPABILITY_COMMIT_RESTRICTIONS, $backend_capabilities)) { $form['commit_restrictions'] = array( '#type' => 'fieldset', '#title' => t('Commit restrictions'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 10, ); $form['commit_restrictions']['allow_unauthorized_access'] = array( '#type' => 'checkbox', '#title' => t('Allow repository access to unknown or unauthorized committers'), '#description' => 'If this is enabled, users can commit to this repository even if no association of the VCS account to the Drupal user is known (assuming that no other commit restrictions apply). If it is disabled, only known and authorized users may commit. This setting only works if the appropriate pre-commit/pre-branch/pre-tag hook script is set up for this repository.', '#default_value' => $repository_exists ? $repository->data['versioncontrol']['allow_unauthorized_access'] : FALSE, '#weight' => 0, ); } if ($repository_exists) { $repo_urls = $repository->getUrlHandler()->urls; } $form['repository_urls'] = array( '#type' => 'fieldset', '#title' => t('Repository browser URLs'), '#description' => t('These URLs will be used to add links to item and commit displays such as the commit log.'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 15, ); $form['repository_urls']['commit_view'] = array( '#type' => 'textfield', '#title' => t('Commit view URL'), '#default_value' => isset($repo_urls) ? $repo_urls['commit_view'] : '', '#size' => 40, '#maxlength' => 255, '#description' => t('Enter URL to the commit view web site. Use the %revision variable in the right place to represent the global revision identifier of the commit.'), ); $form['repository_urls']['file_log_view'] = array( '#type' => 'textfield', '#title' => t('File log URL'), '#default_value' => isset($repo_urls) ? $repo_urls['file_log_view'] : '', '#size' => 40, '#maxlength' => 255, '#description' => t('Enter URL to the log view of a file in the repository. Use the %path, %revision and %branch variables in the right place to represent the path, revision and branch of the originating version of the file for the log.'), ); $form['repository_urls']['file_view'] = array( '#type' => 'textfield', '#title' => t('File content URL'), '#default_value' => isset($repo_urls) ? $repo_urls['file_view'] : '', '#size' => 40, '#maxlength' => 255, '#description' => t('Enter URL to the content view of a file in the repository. Use the %path, %revision and %branch variables in the right place to represent the path, revision and branch of the file.'), ); $form['repository_urls']['directory_view'] = array( '#type' => 'textfield', '#title' => t('Directory listing URL'), '#default_value' => isset($repo_urls) ? $repo_urls['directory_view'] : '', '#size' => 40, '#maxlength' => 255, '#description' => t('Enter URL to the file listing of a directory in the repository. Use the %path, %revision and %branch variables in the right place to represent the path, revision and branch of the file.'), ); $form['repository_urls']['diff'] = array( '#type' => 'textfield', '#title' => t('Diff URL'), '#default_value' => isset($repo_urls) ? $repo_urls['diff'] : '', '#size' => 40, '#maxlength' => 255, '#description' => t('Enter URL to the diff web site. Use the %path, %new-revision, %old-revision and %branch variables in the right place to represent the file path, old revision and new revision. If the tool supports diffs between wholly different files, you can also use %old-path for the path of the old version of the file.'), ); $form['repository_urls']['tracker'] = array('#type' => 'textfield', '#title' => t('Issue tracker URL'), '#default_value' => isset($repo_urls) ? $repo_urls['tracker'] : '', '#size' => 40, '#maxlength' => 255, '#description' => t('Enter URL to link to issues in log messages. Use the %d variable in the right place to represent the issue/case/bug id.'), ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Save repository'), '#weight' => 100, ); return $form; } /** * Validate the add/edit repository form before it is submitted. */ function versioncontrol_admin_repository_edit_validate($form, &$form_state) { if (!empty($form['#repository']) && $form_state['values']['repo_name'] == $form['#original_name']) { return; } $conditions = array('name' => $form_state['values']['repo_name']); $same_repositories = versioncontrol_repository_load_multiple(array(), $conditions); if (!empty($same_repositories)) { form_error($form['repository_information']['repo_name'], t('The repository name %reponame is already in use, please assign a different one.', array('%reponame' => $form_state['values']['repo_name'])) ); } } /** * Add or update the repository when the add/edit form is submitted. */ function versioncontrol_admin_repository_edit_submit($form, &$form_state) { $repository_urls = array(); $repository_url_keys = array_keys(VersioncontrolRepositoryUrlHandler::getEmpty()); foreach ($repository_url_keys as $key) { $repository_urls[$key] = $form_state['values'][$key]; } // Take the existing repository and modify it with the given form values, // or construct a new one altogether. if (isset($form['#repository'])) { $repository = $form['#repository']; $repository->name = $form_state['values']['repo_name']; $repository->root = $form_state['values']['root']; $repository->authorization_method = $form_state['values']['authorization_method']; $repository->plugins = array( 'author_mapper' => $form_state['values']['author_mapper'], 'committer_mapper' => $form_state['values']['committer_mapper'], ); } else { $backends = versioncontrol_get_backends(); $repository = array( 'name' => $form_state['values']['repo_name'], 'vcs' => $form['#vcs'], 'root' => $form_state['values']['root'], 'authorization_method' => $form_state['values']['authorization_method'], 'backend' => $backends[$form['#vcs']], 'plugins' => array( 'author_mapper' => $form_state['values']['author_mapper'], 'committer_mapper' => $form_state['values']['committer_mapper'], ), ); $repository = $backends[$form['#vcs']]->buildEntity('repo', $repository); } // We also provide a 'data' array where other modules can put their own // per-repository data that doesn't need to be stored in a separate db column. $repository->data['versioncontrol'] = array( 'registration_message' => $form_state['values']['registration_message'], 'url_handler' => new VersioncontrolRepositoryUrlHandler($repository, $repository_urls), ); if (isset($form_state['values']['allow_unauthorized_access'])) { $repository->data['versioncontrol']['allow_unauthorized_access'] = (bool) $form_state['values']['allow_unauthorized_access']; } // Let other modules alter the repository array. foreach (module_implements('versioncontrol_repository_submit') as $module) { $function = $module .'_versioncontrol_repository_submit'; $function($repository, $form, $form_state); } if (!is_null($repository->repo_id)) { $repository->update(); drupal_set_message(t('The %repository repository has been updated.', array( '%repository' => $repository->name, ))); } else { $repository = $repository->insert(); drupal_set_message(t('The %repository repository has been added.', array( '%repository' => $repository->name ))); } $form_state['redirect'] = 'admin/project/versioncontrol-repositories'; } /** * Form callback for 'admin/project/versioncontrol-repositories/delete/%versioncontrol_repository': * Provide a form to confirm deletion of a repository. */ function versioncontrol_admin_repository_delete_confirm(&$form_state, $repository) { $form = array(); $form['#repo_id'] = $repository->repo_id; $form = confirm_form($form, t('Are you sure you want to delete %name?', array('%name' => $repository['name'])), !empty($_GET['destination']) ? $_GET['destination'] : 'admin/project/versioncontrol-repositories', t('Mind that by deleting the repository, all associated data such as commits and account associations will be deleted as well.'), t('Delete'), t('Cancel') ); return $form; } /** * Delete the repository when the confirmation form is submitted. */ function versioncontrol_admin_repository_delete_confirm_submit($form, &$form_state) { $repository = versioncontrol_repository_load($form['#repo_id']); $repository->delete(); drupal_set_message(t('The %repository repository has been deleted.', array( '%repository' => $repository['name'], ))); $form_state['redirect'] = 'admin/project/versioncontrol-repositories'; } function versioncontrol_admin_repository_clearlock_confirm(&$form_state, $repository) { $form_state['repo_id'] = $repository->repo_id; $form_state['backend'] = $repository->vcs; $question = t('Are you sure you want to clear the lock on this repository?'); $path = 'admin/project/versioncontrol-repositories'; $description = t('Locks protect repositories while log fetches are being run. Breaking this lock could result in multiple fetches running in parallel, which will cause irrecoverable data inconsistencies.'); $yes = t('Clear Lock'); $no = t('Cancel'); return confirm_form(array(), $question, $path, $description, $yes, $no); } function versioncontrol_admin_repository_clearlock_confirm_submit($form, &$form_state) { $backend = versioncontrol_get_backends($form_state['backend']); $repository = $backend->loadEntity('repo', array($form_state['repo_id'])); $repository->locked = FALSE; $repository->update(); $form_state['redirect'] = 'admin/project/versioncontrol-repositories'; } function versioncontrol_admin_repository_fetch_confirm(&$form_state, $repository) { $form_state['repo_id'] = $repository->repo_id; $form_state['backend'] = $repository->vcs; $question = t('Are you sure you want to fetch logs from this repository?'); $path = 'admin/project/versioncontrol-repositories'; $description = t('Manually triggering a log fetch from this repository will attempt to fully synchronize database-stored data with all the latest changes in the repository. It should be non-destructive.'); $yes = t('Fetch logs'); $no = t('Cancel'); return confirm_form(array(), $question, $path, $description, $yes, $no); } function versioncontrol_admin_repository_fetch_confirm_submit($form, &$form_state) { $backend = versioncontrol_get_backends($form_state['backend']); $repository = $backend->loadEntity('repo', array($form_state['repo_id'])); $repository->fetchLogs(); $form_state['redirect'] = 'admin/project/versioncontrol-repositories'; }