From 1e9bac87df7e28f7abf4aacdb2efc3b298277bcd Mon Sep 17 00:00:00 2001 From: Marco Villegas Date: Tue, 11 Mar 2014 22:39:33 -0500 Subject: [PATCH 1/2] Move general issue/operation related code to an abstract class. --- ...ersioncontrolEventProcessorGitOperationBase.inc | 82 ++++++++++++++++++++ .../VersioncontrolEventProcessorGitIssueMapper.inc | 78 +------------------ .../versioncontrol_project_issue_git.info | 1 + 3 files changed, 86 insertions(+), 75 deletions(-) create mode 100644 versioncontrol_project_issue/versioncontrol_project_issue_git/includes/VersioncontrolEventProcessorGitOperationBase.inc diff --git a/versioncontrol_project_issue/versioncontrol_project_issue_git/includes/VersioncontrolEventProcessorGitOperationBase.inc b/versioncontrol_project_issue/versioncontrol_project_issue_git/includes/VersioncontrolEventProcessorGitOperationBase.inc new file mode 100644 index 0000000..a5c1d3e --- /dev/null +++ b/versioncontrol_project_issue/versioncontrol_project_issue_git/includes/VersioncontrolEventProcessorGitOperationBase.inc @@ -0,0 +1,82 @@ +repository = $repository; + } + + public function setConfiguration($default_data) { + $this->messagePattern = !empty($default_data['message_pattern']) ? $default_data['message_pattern'] : ''; + } + + public function buildForm($default_data) { + $form = array(); + $form['event-processor-issue-mapper-pattern'] = array( + '#type' => 'textfield', + '#title' => t('Extract pattern'), + '#description' => t('A pattern to be used to extract the issue ids from the commit message. i.e. "/#(\d+)\b/"'), + '#default_value' => !empty($default_data['message_pattern']) ? $default_data['message_pattern'] : '', + ); + return $form; + } + + public function getFormData($form_state_values_data) { + return array( + 'message_pattern' => $form_state_values_data['event-processor-issue-mapper-pattern'], + ); + } + + public function submitForm(&$form, &$form_state) { + // Nothing special. + } + + /** + * Retrieve the issues from operation message. + */ + public function getIssuesFromMessage($message) { + if (preg_match_all($this->messagePattern, $message, $matches)) { + return $matches[1]; + } + return array(); + } + + /** + * Returns nids which can be associated. + */ + public function checkValidIssues($nids) { + $valid_nids = array(); + // Make sure we are dealing with project issues. + $possible_issues = node_load_multiple($nids); + foreach ($possible_issues as $possible_issue) { + if (!project_issue_node_is_issue($possible_issue)) { + continue; + } + // Associate only if repository project is the same than issue + // project. + if (empty($possible_issue->field_project[LANGUAGE_NONE][0]['target_id'])) { + continue; + } + if ($possible_issue->field_project[LANGUAGE_NONE][0]['target_id'] != $this->repository->project_nid) { + continue; + } + $valid_nids[] = $possible_issue->nid; + } + return $valid_nids; + } +} diff --git a/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/VersioncontrolEventProcessorGitIssueMapper.inc b/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/VersioncontrolEventProcessorGitIssueMapper.inc index ae873da..9e53c38 100644 --- a/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/VersioncontrolEventProcessorGitIssueMapper.inc +++ b/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/VersioncontrolEventProcessorGitIssueMapper.inc @@ -4,19 +4,10 @@ * Maps issues with operations on git repositories based on commit messages. */ -class VersioncontrolEventProcessorGitIssueMapper implements VersioncontrolSynchronizationEventProcessorInterface, VersioncontrolPluginConfigurationInterface { - /** - * Associated repository. - * - * @var VersioncontrolRepository - */ - protected $repository; - - /** - * Pattern to find issue numbers in commit messages. - */ - protected $messagePattern; +// @todo Is this possible to be added to the ctools plugins metadada? +require_once drupal_get_path('module', 'versioncontrol_project_issue_git') . '/includes/VersioncontrolEventProcessorGitOperationBase.inc'; +class VersioncontrolEventProcessorGitIssueMapper extends VersioncontrolEventProcessorGitOperationBase { public function process(VersioncontrolEvent $event) { if (!$event instanceof VersioncontrolGitEvent) { // Not a git event. @@ -82,67 +73,4 @@ class VersioncontrolEventProcessorGitIssueMapper implements VersioncontrolSynchr } $insert_query->execute(); } - - public function setRepository(VersioncontrolRepository $repository) { - $this->repository = $repository; - } - - public function setConfiguration($default_data) { - $this->messagePattern = !empty($default_data['message_pattern']) ? $default_data['message_pattern'] : ''; - } - - public function buildForm($default_data) { - $form = array(); - $form['event-processor-issue-mapper-pattern'] = array( - '#type' => 'textfield', - '#title' => t('Extract pattern'), - '#description' => t('A pattern to be used to extract the issue ids from the commit message. i.e. "/#(\d+)\b/"'), - '#default_value' => !empty($default_data['message_pattern']) ? $default_data['message_pattern'] : '', - ); - return $form; - } - - public function getFormData($form_state_values_data) { - return array( - 'message_pattern' => $form_state_values_data['event-processor-issue-mapper-pattern'], - ); - } - - public function submitForm(&$form, &$form_state) { - // Nothing special. - } - - /** - * Retrieve the issues from operation message. - */ - public function getIssuesFromMessage($message) { - if (preg_match_all($this->messagePattern, $message, $matches)) { - return $matches[1]; - } - return array(); - } - - /** - * Returns nids which can be associated. - */ - public function checkValidIssues($nids) { - $valid_nids = array(); - // Make sure we are dealing with project issues. - $possible_issues = node_load_multiple($nids); - foreach ($possible_issues as $possible_issue) { - if (!project_issue_node_is_issue($possible_issue)) { - continue; - } - // Associate only if repository project is the same than issue - // project. - if (empty($possible_issue->field_project[LANGUAGE_NONE][0]['target_id'])) { - continue; - } - if ($possible_issue->field_project[LANGUAGE_NONE][0]['target_id'] != $this->repository->project_nid) { - continue; - } - $valid_nids[] = $possible_issue->nid; - } - return $valid_nids; - } } diff --git a/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.info b/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.info index 4c1ef66..8be0414 100644 --- a/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.info +++ b/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.info @@ -5,3 +5,4 @@ dependencies[] = versioncontrol_project_issue dependencies[] = versioncontrol_git dependencies[] = project_issue core = 7.x +files[] = includes/VersioncontrolEventProcessorGitOperationBase.inc -- 1.7.10.4 From eb8148d81caddb31e1036cd91bf6fc4d1c5c300f Mon Sep 17 00:00:00 2001 From: Marco Villegas Date: Wed, 12 Mar 2014 00:54:25 -0500 Subject: [PATCH 2/2] Issue #493074: Added an event processor git specific plugin to back-link to the commit as a comment on the related issue. --- ...rsioncontrolEventProcessorGitCommitsComment.inc | 88 ++++++++++++++++++++ .../event_processor/git_commits_as_comment.inc | 15 ++++ .../versioncontrol_project_issue_git.module | 71 ++++++++++++++++ 3 files changed, 174 insertions(+) create mode 100644 versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/VersioncontrolEventProcessorGitCommitsComment.inc create mode 100644 versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/git_commits_as_comment.inc diff --git a/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/VersioncontrolEventProcessorGitCommitsComment.inc b/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/VersioncontrolEventProcessorGitCommitsComment.inc new file mode 100644 index 0000000..c3405cd --- /dev/null +++ b/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/VersioncontrolEventProcessorGitCommitsComment.inc @@ -0,0 +1,88 @@ + $event->getRepository()->repo_id))->fetchField(); + if (!$project_nid) { + // No project associated. + return; + } + // We are depending on getCommitInterval(), so check we are using the right + // synchronization plugin. + $synchronizer = $event->getRepository()->getSynchronizer(); + if (!method_exists($synchronizer, 'getCommitInterval')) { + watchdog('versioncontrol_project_issue', 'git_issue_mapper: Cannot use a repository synchronizer of type %type', array('%type' => get_class($synchronizer)), WATCHDOG_WARNING); + return; + } + $replies_per_nid = array(); + foreach ($event as $ref) { + if ($ref->reftype != VERSIONCONTROL_GIT_REFTYPE_BRANCH) { + // Only process branch refs for now. + return; + } + if ($ref->ff != 1) { + // @fixme support non-ff. + continue; + } + if ($ref->eventDeletedMe()) { + // Nothing to do. + // We could try to figure out orphaned commits from label removal. Let's + // trust on hook_versioncontrol_entity_commit_delete() for now. + continue; + } + // Usual ff ref update. + $commit_hashes = $synchronizer->getCommitInterval($ref->old_sha1, $ref->new_sha1); + // Try to deal with big pushes loading operations in chunks of 500 items. + foreach (array_chunk($commit_hashes, 500) as $commit_hashes_chunk) { + $related_operations = $event->getRepository()->loadCommits(array(), array('revision' => $commit_hashes_chunk)); + foreach ($related_operations as $operation) { + $nids_on_message = $this->getIssuesFromMessage($operation->message); + if (count($nids_on_message) < 1) { + // Nothing to do. + continue; + } + $valid_issue_nids = $this->checkValidIssues($nids_on_message); + // Group by node, since we coudl receive multiple commits in the same + // event. + foreach (node_load_multiple($valid_issue_nids) as $node) { + $comment = theme('versioncontrol_project_issue_git_operation_comment_item', array('operation' => $operation)); + $replies_per_nid[$node->nid]['operation_comments'][] = $comment; + } + + foreach ($valid_issue_nids as $valid_issue_nid) { + $operation_nid_maps[] = array( + 'nid' => $valid_issue_nid, + 'vc_op_id' => $operation->vc_op_id + ); + } + } + } + } + // Actually make the comments. + $followup_account = user_load(variable_get('project_issue_followup_user', 0)); + foreach ($replies_per_nid as $nid => $reply) { + $node = node_load($nid); + $node->nodechanges_uid = $followup_account->uid; + $node->nodechanges_comment_body = array( + 'value' => theme('item_list', array('items' => $reply['operation_comments'])), + 'format' => filter_default_format($followup_account), + ); + // @todo Send mail or not? + $node->nodechanges_comment_attributes = array( + 'project_issue_no_email' => TRUE, + ); + node_save($node); + } + } +} diff --git a/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/git_commits_as_comment.inc b/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/git_commits_as_comment.inc new file mode 100644 index 0000000..3da40c3 --- /dev/null +++ b/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/git_commits_as_comment.inc @@ -0,0 +1,15 @@ + 'git', + 'title' => t('Git commits as comment'), + 'handler' => array( + 'class' => 'VersioncontrolEventProcessorGitCommitsComment', + 'file' => 'VersioncontrolEventProcessorGitCommitsComment.inc', + 'path' => drupal_get_path('module', 'versioncontrol_project_issue_git') . '/plugins/event_processor', + ), +); diff --git a/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.module b/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.module index 54e3cbc..23626f5 100644 --- a/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.module +++ b/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.module @@ -90,3 +90,74 @@ function versioncontrol_project_issue_git_versioncontrol_repository_post_resync( } drupal_static_reset('versioncontrol_project_issue_git_resync_recover_' . $repository->repo_id); } + +/** + * Implements hook_theme(). + */ +function versioncontrol_project_issue_git_theme($variables) { + return array( + 'versioncontrol_project_issue_git_operation_comment_item' => array( + 'variables' => array( + 'operation' => NULL, + ), + ), + ); +} + +/** + * Theme for an operation rendered inside a comment. + */ +function theme_versioncontrol_project_issue_git_operation_comment_item($variables) { + $operation = $variables['operation']; + $url_handler = $operation->getRepository()->getUrlHandler(); + $label_names = array(); + foreach ($operation->labels as $label) { + $label_names[] = $label->name; + } + $trim_alter = array( + 'max_length' => 73, + 'word_boundary' => TRUE, + 'ellipsis' => TRUE, + 'html' => FALSE, + ); + $revision = $operation->getBackend()->formatRevisionIdentifier($operation->revision, 'short'); + if (module_exists('commitlog')) { + $revision = l($revision, sprintf('commitlog/commit/%d/%s', $operation->getRepository()->repo_id, $operation->revision)); + } + $placeholders = array( + '!revision' => $revision, + '@labels' => implode(', ', $label_names), + // Re-use views function. + '@message' => views_trim_text($trim_alter, $operation->message), + ); + + if ($operation->committer_uid != 0) { + $committer_account = user_load($operation->committer_uid); + } + if ($operation->author_uid != 0) { + $author_account = user_load($operation->author_uid); + } + if (isset($committer_account)) { + $placeholders['!committer'] = theme('username', array('account' => $committer_account)); + if (isset($author_account)) { + $placeholders['!author'] = theme('username', array('account' => $author_account)); + if ($committer_account->uid == $author_account->uid) { + return t('Commit !revision on @labels by !committer:
@message
', $placeholders); + } + else { + return t('Commit !revision on @labels authored by !author, committed by !committer:
@message
', $placeholders); + } + } + else { + return t('Commit !revision on @labels by !committer:
@message
', $placeholders); + } + } + elseif (isset($author_account)) { + $placeholders['!author'] = theme('username', array('account' => $author_account)); + return t('Commit !revision on @labels authored by !author:
@message
', $placeholders); + } + else { + // No uid associated yet. + return t('Commit !revision on @labels:
@message
', $placeholders); + } +} -- 1.7.10.4