$operation['uid'])); } if (!$user) { $backend = versioncontrol_get_backend($operation['repository']); $error_message = t( "** ERROR: no Drupal user matches !vcs user '!user'. ** Please contact a !vcs administrator for help.", array('!vcs' => $backend['name'], '!user' => $operation['username']) ); return array($error_message); // disallow the commit with an explanation } // Mind that also commits normally have labels, except for stuff like // Subversion when the user commits outside of the trunk/branches/tags // directories. Let's say we want to prevent such commits. if (empty($operation['labels'])) { $error_message = t("** ERROR: It is not allowed to commit without a branch or tag!"); return array($error_message); } // If an empty array is returned then that means we're indifferent: // allow the operation if nobody else has objections. $error_messages = array(); // Restrict disallowed branches and tags. $valid_labels = array( VERSIONCONTROL_OPERATION_BRANCH => array('@^HEAD$@', '@^DRUPAL-5(--[2-9])?$@', '@^DRUPAL-6--[1-9]$@'), VERSIONCONTROL_OPERATION_TAG => array('@^DRUPAL-[56]--(\d+)-(\d+)(-[A-Z0-9]+)?$@'), ); foreach ($operation['labels'] as $label) { if ($label['type'] == VERSIONCONTROL_OPERATION_TAG && $label['action'] == VERSIONCONTROL_ACTION_DELETED) { continue; // no restrictions, even invalid tags should be allowed to be deleted } // Make sure that the assigned branch or tag name is allowed. $valid = FALSE; foreach ($valid_labels[$label['type']] as $valid_label_regexp) { if (preg_match($valid_label_regexp, $label['name'])) { $valid = TRUE; break; } } if (!$valid) { // No regexps match this label, so deny it. $error_messages[] = t('** ERROR: the !name !labeltype is not allowed in this repository.', array( '!name' => $label['name'], '!labeltype' => ($label['type'] == VERSIONCONTROL_OPERATION_BRANCH) ? t('branch') : t('tag'), )); } } return $error_messages; } /** * Extract repository data from the repository edit/add form's submitted * values, and add it to the @p $repository array. Later, that array will be * passed to hook_versioncontrol_repository() as part of the repository * insert/update procedure. * * @param $repository * The repository array which is being passed by reference so that it can be * written to. * @param $form * The form array of the submitted repository edit/add form, with * $form['#id'] == 'versioncontrol-repository-form' (amongst others). * @param $form_state * The form state of the submitted repository edit/add form. * If you altered this form and added an additional form element then * $form_state['values'] will also contain the value of this form element. * * @ingroup Repositories * @ingroup Form handling * @ingroup Target audience: All modules with repository specific settings */ function hook_versioncontrol_repository_submit(&$repository, $form, $form_state) { // The user can specify multiple repository ponies, separated by whitespace. // So, split the string up into an array of ponies. $ponies = trim($form_state['values']['mymodule_ponies']); $ponies = empty($ponies) ? array() : explode(' ', $ponies); $repository['mymodule']['ponies'] = $ponies; } /** * Act on database changes when VCS repositories are inserted, * updated or deleted. * * @param $op * Either 'insert' when the repository has just been created, or 'update' * when repository name, root, URL backend or module specific data change, * or 'delete' if it will be deleted after this function has been called. * * @param $repository * The repository array containing the repository. It's a single * repository array like the one returned by versioncontrol_get_repository(), * so it consists of the following elements: * * - 'repo_id': The unique repository id. * - 'name': The user-visible name of the repository. * - 'vcs': The unique string identifier of the version control system * that powers this repository. * - 'root': The root directory of the repository. In most cases, * this will be a local directory (e.g. '/var/repos/drupal'), * but it may also be some specialized string for remote repository * access. How this string may look like depends on the backend. * - 'authorization_method': The string identifier of the repository's * authorization method, that is, how users may register accounts * in this repository. Modules can provide their own methods * by implementing hook_versioncontrol_authorization_methods(). * - 'url_backend': The prefix (excluding the trailing underscore) * for URL backend retrieval functions. * - '[xxx]_specific': An array of VCS specific additional repository * information. How this array looks like is defined by the * corresponding backend module (versioncontrol_[xxx]). * - '???': Any other additions that modules added by implementing * hook_versioncontrol_repository_submit(). * * @ingroup Repositories * @ingroup Database change notification * @ingroup Form handling * @ingroup Target audience: All modules with repository specific settings */ function hook_versioncontrol_repository($op, $repository) { $ponies = $repository['mymodule']['ponies']; switch ($op) { case 'update': db_query("DELETE FROM {mymodule_ponies} WHERE repo_id = %d", $repository['repo_id']); // fall through case 'insert': foreach ($ponies as $pony) { db_query("INSERT INTO {mymodule_ponies} (repo_id, pony) VALUES (%d, %s)", $repository['repo_id'], $pony); } break; case 'delete': db_query("DELETE FROM {mymodule_ponies} WHERE repo_id = %d", $repository['repo_id']); break; } } /** * Register new authorization methods that can be selected for a repository. * A module may restrict access and alter forms depending on the selected * authorization method which is a property of every repository array * ($repository['authorization_method']). * * A list of all authorization methods can be retrieved * by calling versioncontrol_get_authorization_methods(). * * @return * A structured array containing information about authorization methods * provided by this module, wrapped in a structured array. Array keys are * the unique string identifiers of each authorization method, and * array values are the user-visible method descriptions (wrapped in t()). * * @ingroup Accounts * @ingroup Authorization * @ingroup Target audience: Authorization control modules */ function hook_versioncontrol_authorization_methods() { return array( 'mymodule_code_ninja' => t('Code ninja skills required'), ); } /** * Alter the list of repositories that are available for user registration * and editing. * * @param $repository_names * The list of repository names as it is shown in the select box * at 'versioncontrol/register'. Array keys are the repository ids, * and array elements are the captions in the select box. * There's two things that can be done with this array: * - Change (amend) the caption, in order to provide more information * for the user. (E.g. note that an application is necessary.) * - Unset any number of array elements. If you do so, the user will not * be able to register a new account for this repository. * @param $repositories * A list of repositories (with the repository ids as array keys) that * includes at least all of the repositories that correspond to the * repository ids of the @p $repository_names array. * * @ingroup Accounts * @ingroup Authorization * @ingroup Repositories * @ingroup Form handling * @ingroup Target audience: Authorization control modules */ function hook_versioncontrol_alter_repository_selection(&$repository_names, $repositories) { global $user; foreach ($repository_names as $repo_id => $caption) { if ($repositories[$repo_id]['authorization_method'] == 'mymodule_code_ninja') { if (!in_array('code ninja', $user->roles)) { unset($repository_names[$repo_id]); } } } } /** * Let the Version Control API know whether the given VCS account * is authorized or not. * * @return * TRUE if the account is authorized, or FALSE if it's not. * * @ingroup Accounts * @ingroup Authorization * @ingroup Target audience: Authorization control modules */ function hook_versioncontrol_is_account_authorized($uid, $repository) { if ($repository['authorization_method'] != 'mymodule_dojo_status') { return TRUE; } $result = db_query("SELECT status FROM {mymodule_dojo_status} WHERE uid = %d AND repo_id = %d", $uid, $repository['repo_id']); while ($account = db_fetch_object($result)) { return ($account->status == MYMODULE_SENSEI); } return FALSE; } /** * Unset filtered accounts before they are even attempted to be displayed * on the account list ("admin/project/versioncontrol-accounts"). * You'll most probably use this in conjunction with an additional filter * form element that is added to the account filter form * ($form['#id'] == 'versioncontrol-account-filter-form') with form_alter(). * * @param $accounts * The accounts that would normally be displayed, in the same format as the * return value of versioncontrol_get_accounts(). Entries in this list * may be unset by this filter function. * * @ingroup Accounts * @ingroup Form handling * @ingroup Target audience: Authorization control modules */ function hook_versioncontrol_filter_accounts(&$accounts) { if (empty($accounts)) { return; } // Use a default value if the session variable hasn't yet been set. if (!isset($_SESSION['mymodule_filter_username'])) { $_SESSION['mymodule_filter_username'] = 'chx'; } $mymodule_filter_username = $_SESSION['mymodule_filter_username']; if ($mymodule_filter_username == '') { return; // Don't change the list if no filtering should happen. } foreach ($accounts as $uid => $usernames_by_repository) { foreach ($usernames_by_repository as $repo_id => $username) { if ($username != $mymodule_filter_username) { unset($accounts[$uid][$repo_id]); if (empty($accounts[$uid])) { unset($accounts[$uid]); } } } } } /** * Extract account data from the account form's submitted * values, and add it to the @p $additional_data array. Later, that array * will be passed to hook_versioncontrol_account() as part of the account * insert/update procedure. * * @param $additional_data * The additional account data array which is being passed by reference so * that it can be written to. * @param $form * The form array of the submitted account edit/register form, with * $form['#id'] == 'versioncontrol-account-form' (amongst others). * @param $form_state * The form state of the submitted account edit/register form. * If you altered this form and added an additional form element then * $form_state['values'] will also contain the value of this form element. * * @ingroup Accounts * @ingroup Form handling * @ingroup Target audience: Commit access modules * @ingroup Target audience: Authorization control modules * @ingroup Target audience: All modules with account specific settings */ function hook_versioncontrol_account_submit(&$additional_data, $form, $form_state) { if (empty($form_state['values']['mymodule_karma'])) { return; } $additional_data['mymodule']['karma'] = $form_state['values']['mymodule_karma']; } /** * Act on database changes when VCS accounts are inserted, updated or deleted. * * @param $op * Either 'insert' when the account has just been created, 'update' * when it has been updated, or 'delete' if it will be deleted after * this function has been called. * @param $uid * The Drupal user id corresponding to the VCS account. * @param $username * The VCS specific username (a string) of the account. * @param $repository * The repository where the user has its VCS account. * @param $additional_data * An array of additional author information. Modules can fill this array * by implementing hook_versioncontrol_account_submit(). * * @ingroup Accounts * @ingroup Form handling * @ingroup Target audience: Commit access modules * @ingroup Target audience: Authorization control modules * @ingroup Target audience: All modules with account specific settings */ function hook_versioncontrol_account($op, $uid, $username, $repository, $additional_data = array()) { switch ($op) { case 'insert': case 'update': // Recap: if form_alter() wasn't applied, our array element is not set. $mymodule_data = $additional_data['mymodule']; if (!isset($mymodule_data)) { // In most modules, form_alter() will always be applied to the // account editing/creating form. If $mymodule_data is empty // nevertheless then it means that the account has been created // programmatically rather than with a form submit. // In that case, we better assign a default value: if ($op == 'insert') { $mymodule_data = array('karma' => 50); } // Don't change the status for programmatical updates, though. if ($op == 'update') { break; } } db_query("DELETE FROM {mymodule_karma} WHERE uid = %d", $uid); db_query("INSERT INTO {mymodule_karma} (uid, karma) VALUES (%d, %d)", $uid, $mymodule_data['karma']); break; case 'delete': db_query("DELETE FROM {mymodule_karma} WHERE uid = %d", $uid); break; } } /** * Add additional columns into the list of VCS accounts. * By changing the @p $header and @p $rows_by_uid arguments, * the account list can be customized accordingly. * * @param $accounts * The list of accounts that is being displayed in the account table. This is * a structured array like the one returned by versioncontrol_get_accounts(). * @param $repositories * An array of repositories where the given users have a VCS account. * Array keys are the repository ids, and array values are the * repository arrays like returned from versioncontrol_get_repository(). * @param $header * A list of columns that will be passed to theme('table'). * @param $rows_by_uid * An array of existing table rows, with Drupal user ids as array keys. * Each row already includes the generic column values, and for each row * there is an account with the same uid given in the @p $accounts parameter. * * @ingroup Accounts * @ingroup Form handling * @ingroup Target audience: Authorization control modules * @ingroup Target audience: All modules with account specific settings */ function hook_versioncontrol_alter_account_list($accounts, $repositories, &$header, &$rows_by_uid) { $header[] = t('Karma'); foreach ($rows_by_uid as $uid => $row) { $rows_by_uid[$uid][] = theme('user_karma', $uid); } }