$repository) { $repo_name_mapping[] = t('!id for @repo-name', array( '!id' => $repo_id, '@repo-name' => $repository['name'], )); } $vcs_mapping = array(); foreach (versioncontrol_get_backends() as $vcs => $backend) { $vcs_mapping[$vcs] = check_plain($backend['name']); } $tokens['versioncontrol repository'] = array( 'id' => t('Repository identifier (known ids: !repo-mapping)', array( '!repo-mapping' => implode(', ', $repo_name_mapping), )), 'name' => t('Repository name, as listed in the above mapping'), 'vcs' => t('VCS backend identifier (known backend ids: !backends)', array( '!backends' => implode(', ', array_keys($vcs_mapping)), )), 'vcs-name' => t('VCS backend name (known backend names: !backends)', array( '!backends' => implode(', ', $vcs_mapping), )), 'root' => t('Repository root'), ); } if ($type == 'versioncontrol_operation' || $type == 'all') { $tokens['versioncontrol operation'] = array( 'id' => t('Operation identifier (vc_op_id)'), 'date' => t('Commit date'), 'revision' => t('Global revision identifier'), 'vcs-user' => t('VCS specific account name'), 'vcs-user-raw' => t('VCS specific account name. WARNING - raw unfiltered message, not safe for HTML (fine for emails, though).'), 'message' => t('Commit/branch/tag message (might be empty for branches and tags)'), 'message-raw' => t('Commit/branch/tag message like above. WARNING - raw unfiltered message, not safe for HTML (fine for emails, though).'), ); } return $tokens; } /** * Implementation of hook_token_values(). */ function versioncontrol_token_values($type, $object = NULL) { switch ($type) { case 'versioncontrol_repository': $repository = $object; $backend = versioncontrol_get_backend($repository['vcs']); $values = array( 'id' => $repository['repo_id'], 'name' => check_plain($repository['name']), 'vcs' => check_plain($repository['vcs']), 'vcs-name' => check_plain($backend['name']), 'root' => check_plain($backend['root']), ); break; case 'versioncontrol_operation': $operation = $object; $values = array( 'id' => $operation['vc_op_id'], 'revision' => $operation['revision'], 'vcs-user' => check_plain($operation['username']), 'vcs-user-raw' => $operation['username'], 'message' => check_plain($operation['message']), 'message-raw' => $operation['message'], 'date' => format_date($operation['date'], 'short', '', variable_get('date_default_timezone', 0) ), ); break; } return $values; } // Secondly, Rules integration. /** * Implementation of hook_rules_data_type_info(). */ function versioncontrol_rules_data_type_info() { return array( 'versioncontrol_repository' => array( 'label' => t('Repository'), 'class' => 'versioncontrol_data_type_repository', // The versioncontrol_data_type_repository class also supports // 'savable' == TRUE, but there's no use case for it currently. 'savable' => FALSE, 'identifiable' => TRUE, 'module' => t('Version Control API'), ), 'versioncontrol_operation' => array( 'label' => t('Version control operation'), 'class' => 'versioncontrol_data_type_operation', 'savable' => FALSE, 'identifiable' => TRUE, 'module' => t('Version Control API'), ), 'versioncontrol_item_list' => array( 'label' => t('List of repository items (files/directories)'), 'class' => 'rules_data_type', 'savable' => FALSE, 'identifiable' => FALSE, 'module' => t('Version Control API'), ), ); } /** * Defines the versioncontrol_repository data type for Rules. */ class versioncontrol_data_type_repository extends rules_data_type { function save() { $repository = &$this->get(); $repository_urls = _versioncontrol_get_repository_urls($repository); versioncontrol_update_repository($repository); return TRUE; } function load($repo_id) { return versioncontrol_get_repository($repo_id); } function get_identifier() { $repository = &$this->get(); return $repository['repo_id']; } } /** * Defines the versioncontrol_operation data type for Rules. */ class versioncontrol_data_type_operation extends rules_data_type { function load($vc_op_id) { $operations = versioncontrol_get_operations(array('vc_op_ids' => array($vc_op_id))); return empty($operations) ? FALSE : reset($operation); // first (only) element } function get_identifier() { $operation = &$this->get(); return $operation['vc_op_id']; } } /** * Implementation of hook_rules_event_info(). */ function versioncontrol_rules_event_info() { return array( 'versioncontrol_operation_insert' => array( 'label' => t('Version control operation has been recorded'), 'help' => t('A "version control operation" is either a commit or the creation/deletion of a branch or tag. Note that this event is normally called from either cron or an external script, so you shouldn\'t do things like page redirections, message popups or similar on-site actions.'), 'module' => t('Version Control API'), 'arguments' => array( 'operation' => array( 'type' => 'versioncontrol_operation', 'label' => t('Operation'), ), 'author' => array( 'type' => 'user', 'label' => t('Author'), 'handler' => 'versioncontrol_events_argument_get_author', ), 'repository' => array( 'type' => 'versioncontrol_repository', 'label' => t('Repository'), 'handler' => 'versioncontrol_events_argument_get_repository', ), 'message' => array( 'type' => 'string', 'label' => t('Log message'), 'handler' => 'versioncontrol_events_argument_get_message', ), 'date' => array( 'type' => 'date', 'label' => t('Date'), 'handler' => 'versioncontrol_events_argument_get_date', ), 'items' => array( 'type' => 'versioncontrol_item_list', 'label' => t('Items (files/directories)'), ), ), ), ); } function versioncontrol_events_argument_get_author($operation) { return user_load($operation['uid']); } function versioncontrol_events_argument_get_repository($operation) { return $operation['repository']; } function versioncontrol_events_argument_get_message($operation) { return $operation['message']; } function versioncontrol_events_argument_get_date($operation) { // Looking at the source code of rules.rules.inc, this seems to be the // native date representation of the rules_data_type_date class. return gmdate('Y-m-d H:i:s', $operation['date']); } /** * Implementation of hook_rules_condition_info(). */ function versioncontrol_rules_condition_info() { return array( 'versioncontrol_condition_repository' => array( 'label' => t('Compare repository'), 'arguments' => array( 'repository' => array( 'entity' => 'versioncontrol_repository', 'label' => t('Repository to compare'), ), ), 'help' => t('Evaluates to TRUE if a repository is one of several selected repositories.'), 'module' => t('Version Control API'), ), 'versioncontrol_condition_operation_labels' => array( 'label' => t('Compare branch/tag name'), 'arguments' => array( 'items' => array( 'entity' => 'versioncontrol_operation', 'label' => t('Operation (commit, or creation/deletion of a branch or tag)'), ), ), 'help' => t('Evaluates to TRUE if the version control operation affected a branch or tag of the given name.'), 'module' => t('Version Control API'), ), 'versioncontrol_condition_item_paths' => array( 'label' => t('Compare item paths'), 'arguments' => array( 'items' => array( 'entity' => 'versioncontrol_item_list', 'label' => t('Set of items to be compared'), ), ), 'help' => t('Evaluates to TRUE if the path of at least one of the items matches a given set of regular expressions.'), 'module' => t('Version Control API'), ), ); } // Condition implementations. /** * "Compare repository" condition: * Evaluates to TRUE if a repository is one of several selected repositories. */ function versioncontrol_condition_repository($repository, $settings) { return in_array($repository['repo_id'], $settings['repo_ids']); } function versioncontrol_condition_repository_form($settings, &$form) { $repository_options = array(); foreach (versioncontrol_get_repositories() as $repo_id => $repository) { $repository_options[$repo_id] = check_plain($repository['name']); } $form['settings']['repo_ids'] = array( '#type' => 'checkboxes', '#title' => t('Matched repositories'), '#description' => t('If the repository is in the selected set of repositories, this condition will return TRUE.'), '#options' => $repository_options, '#default_value' => empty($settings['repo_ids']) ? array() : $settings['repo_ids'], '#required' => TRUE, ); } function versioncontrol_condition_repository_submit(&$settings, $form, $form_state) { $settings['repo_ids'] = array_filter(array_keys(array_filter($settings['repo_ids']))); } /** * "Compare item location" condition: * Evaluates to TRUE if the path of at least one of the items matches a given * set of regular expressions. */ function versioncontrol_condition_item_paths($items, $settings) { $all_items_required = ($settings['operation'] == 'AND'); foreach ($items as $path => $item) { if ($all_items_required) { $item_matches = FALSE; } // Here comes the actual comparison. foreach ($settings['path_regexps'] as $path_regexp) { if (versioncontrol_preg_item_match($path_regexp, $item)) { if ($all_items_required) { // 'AND' mode: this item is ok, next one $item_matches = TRUE; break; } else { // 'OR' mode: a single matching item is enough, return TRUE return TRUE; } } } // This item didn't match any of the regexps // -> not all items match one of the regexps // -> condition has not been met. if ($all_items_required && !$item_matches) { return FALSE; } } // If we made it here in 'AND' mode, all items match. if ($all_items_required) { return TRUE; } // In 'OR' mode, any matching item would have returned TRUE, // which means no single item has matched if we made it here. return FALSE; } function versioncontrol_condition_item_paths_form($settings, &$form) { $form['settings']['item_paths']['path_regexps'] = array( '#type' => 'textfield', '#title' => t('List of regular expressions'), '#description' => t('If the paths of the items (relative to the repository root, starting with "/") match one of these PHP regular expressions, this condition will return TRUE. Separate the regular expressions from each other with spaces. Example: "@^/trunk/@ @^/contributions/profiles.*(?<!\.profile|\.txt)$@ @^.*\.(gz|tgz|tar|zip)$@"'), '#default_value' => empty($settings['path_regexps']) ? '@^/@' : implode(' ', $settings['path_regexps']), '#required' => TRUE, '#size' => 60, ); $form['settings']['item_paths']['operation'] = array( '#type' => 'select', '#title' => t('How many items must match one of the regular expressions'), '#options' => array('OR' => t('It\'s enough if any item matches'), 'AND' => t('All items must match')), '#description' => t('Specify whether any item or all items must match one of the regular expressions for the condition to return TRUE.'), '#default_value' => $settings['operation'], ); } function versioncontrol_condition_item_paths_submit(&$settings, $form, $form_state) { $settings['path_regexps'] = array_filter(explode(' ', $settings['path_regexps'])); } /** * "Compare item location" condition: * Evaluates to TRUE if the path of at least one of the items matches a given * set of regular expressions. */ function versioncontrol_condition_operation_labels($operation, $settings) { // Might not cover all use cases if there are multiple labels in one // operation, especially with "deleted" label actions. // Probably need to distinguish between operation types and label actions. foreach ($operation['labels'] as $label) { foreach ($settings['label_regexps'] as $label_regexp) { if (preg_match($path_regexp, $label['name'])) { return TRUE; } } } return FALSE; } function versioncontrol_condition_operation_labels_form($settings, &$form) { $form['settings']['labels']['label_regexps'] = array( '#type' => 'textfield', '#title' => t('List of regular expressions'), '#description' => t('If a branch/tag name matches one of these PHP regular expressions, this condition will return TRUE. Separate the regular expressions from each other with spaces. Example (for a branch name, in this case): "@^HEAD$@ @^DRUPAL-5(--[2-9])?$@ @^DRUPAL-6--[1-9]$@"'), '#default_value' => empty($settings['label_regexps']) ? '' : implode(' ', $settings['label_regexps']), '#required' => TRUE, '#size' => 60, ); } function versioncontrol_condition_operation_labels_submit(&$settings, $form, $form_state) { $settings['label_regexps'] = array_filter(explode(' ', $settings['label_regexps'])); }