public static function processCommit(TBGProject $project, $commit_msg, $old_rev, $new_rev, $date = null, $changed, $author, $branch = null) { $output = ''; TBGContext::setCurrentProject($project); if ($project->isArchived()) { return; } try { TBGContext::getI18n(); } catch (Exception $e) { TBGContext::reinitializeI18n(null); } // Is VCS Integration enabled? if (TBGSettings::get('vcs_mode_' . $project->getID(), 'vcs_integration') == TBGVCSIntegration::MODE_DISABLED) { $output .= '[VCS ' . $project->getKey() . '] This project does not use VCS Integration' . "\n"; return $output; } // Parse the commit message, and obtain the issues and transitions for issues. $parsed_commit = TBGIssue::getIssuesFromTextByRegex($commit_msg); $issues = $parsed_commit["issues"]; $transitions = $parsed_commit["transitions"]; // If no issues exist, we may not be able to continue // if (count($issues) == 0) // { // $output .= '[VCS '.$project->getKey().'] This project only accepts commits which affect issues' . "\n"; // return $output; // } // Build list of affected files $file_lines = preg_split('/[\\n\\r]+/', $changed); $files = array(); foreach ($file_lines as $aline) { $action = mb_substr($aline, 0, 1); if ($action == "A" || $action == "U" || $action == "D" || $action == "M") { $theline = trim(mb_substr($aline, 1)); $files[] = array($action, $theline); } } // Find author of commit, fallback is guest /* * Some VCSes use a different format of storing the committer's name. Systems like bzr, git and hg use the format * Joe Bloggs <*****@*****.**>, instead of a classic username. Therefore a user will be found via 4 queries: * a) First we extract the email if there is one, and find a user with that email * b) If one is not found - or if no email was specified, then instead test against the real name (using the name part if there was an email) * c) the username or full name is checked against the friendly name field * d) and if we still havent found one, then we check against the username * e) and if we STILL havent found one, we use the guest user */ if (preg_match("/(?<=<)(.*)(?=>)/", $author, $matches)) { $email = $matches[0]; // a) $user = TBGUsersTable::getTable()->getByEmail($email); if (!$user instanceof TBGUser) { // Not found by email preg_match("/(?<=^)(.*)(?= <)/", $author, $matches); $author = $matches[0]; } } // b) if (!$user instanceof TBGUser) { $user = TBGUsersTable::getTable()->getByRealname($author); } // c) if (!$user instanceof TBGUser) { $user = TBGUsersTable::getTable()->getByBuddyname($author); } // d) if (!$user instanceof TBGUser) { $user = TBGUsersTable::getTable()->getByUsername($author); } // e) if (!$user instanceof TBGUser) { $user = TBGSettings::getDefaultUser(); } TBGContext::setUser($user); TBGSettings::forceSettingsReload(); TBGContext::cacheAllPermissions(); $output .= '[VCS ' . $project->getKey() . '] Commit to be logged by user ' . $user->getName() . "\n"; if ($date == null) { $date = NOW; } // Create the commit data $commit = new TBGVCSIntegrationCommit(); $commit->setAuthor($user); $commit->setDate($date); $commit->setLog($commit_msg); $commit->setPreviousRevision($old_rev); $commit->setRevision($new_rev); $commit->setProject($project); if ($branch !== null) { $data = 'branch:' . $branch; $commit->setMiscData($data); } $commit->save(); $output .= '[VCS ' . $project->getKey() . '] Commit logged with revision ' . $commit->getRevision() . "\n"; // Iterate over affected issues and update them. foreach ($issues as $issue) { $inst = new TBGVCSIntegrationIssueLink(); $inst->setIssue($issue); $inst->setCommit($commit); $inst->save(); // Process all commit-message transitions for an issue. foreach ($transitions[$issue->getFormattedIssueNo()] as $transition) { if (TBGSettings::get('vcs_workflow_' . $project->getID(), 'vcs_integration') == TBGVCSIntegration::WORKFLOW_ENABLED) { TBGContext::setUser($user); TBGSettings::forceSettingsReload(); TBGContext::cacheAllPermissions(); if ($issue->isWorkflowTransitionsAvailable()) { // Go through the list of possible transitions for an issue. Only // process transitions that are applicable to issue's workflow. foreach ($issue->getAvailableWorkflowTransitions() as $possible_transition) { if (mb_strtolower($possible_transition->getName()) == mb_strtolower($transition[0])) { $output .= '[VCS ' . $project->getKey() . '] Running transition ' . $transition[0] . ' on issue ' . $issue->getFormattedIssueNo() . "\n"; // String representation of parameters. Used for log message. $parameters_string = ""; // Iterate over the list of this transition's parameters, and // set them. foreach ($transition[1] as $parameter => $value) { $parameters_string .= "{$parameter}={$value} "; switch ($parameter) { case 'resolution': if (($resolution = TBGResolution::getResolutionByKeyish($value)) instanceof TBGResolution) { TBGContext::getRequest()->setParameter('resolution_id', $resolution->getID()); } break; case 'status': if (($status = TBGStatus::getStatusByKeyish($value)) instanceof TBGStatus) { TBGContext::getRequest()->setParameter('status_id', $status->getID()); } break; } } // Run the transition. $possible_transition->transitionIssueToOutgoingStepWithoutRequest($issue); // Log an informative message about the transition. $output .= '[VCS ' . $project->getKey() . '] Ran transition ' . $possible_transition->getName() . ' with parameters \'' . $parameters_string . '\' on issue ' . $issue->getFormattedIssueNo() . "\n"; } } } } } $issue->addSystemComment(TBGContext::getI18n()->__('This issue has been updated with the latest changes from the code repository.<source>%commit_msg</source>', array('%commit_msg' => $commit_msg)), $user->getID()); $output .= '[VCS ' . $project->getKey() . '] Updated issue ' . $issue->getFormattedIssueNo() . "\n"; } // Create file links foreach ($files as $afile) { // index 0 is action, index 1 is file $inst = new TBGVCSIntegrationFile(); $inst->setAction($afile[0]); $inst->setFile($afile[1]); $inst->setCommit($commit); $inst->save(); $output .= '[VCS ' . $project->getKey() . '] Added with action ' . $afile[0] . ' file ' . $afile[1] . "\n"; } TBGEvent::createNew('vcs_integration', 'new_commit')->trigger(array('commit' => $commit)); return $output; }
public static function loadFixtures(TBGScope $scope, TBGWorkflow $workflow, $steps) { $rejected_resolutions = array(); $rejected_resolutions[] = TBGResolution::getResolutionByKeyish('notanissue')->getID(); $rejected_resolutions[] = TBGResolution::getResolutionByKeyish('wontfix')->getID(); $rejected_resolutions[] = TBGResolution::getResolutionByKeyish('cantfix')->getID(); $rejected_resolutions[] = TBGResolution::getResolutionByKeyish('cantreproduce')->getID(); $rejected_resolutions[] = TBGResolution::getResolutionByKeyish('duplicate')->getID(); $resolved_resolutions = array(); $resolved_resolutions[] = TBGResolution::getResolutionByKeyish('resolved')->getID(); $resolved_resolutions[] = TBGResolution::getResolutionByKeyish('wontfix')->getID(); $resolved_resolutions[] = TBGResolution::getResolutionByKeyish('postponed')->getID(); $resolved_resolutions[] = TBGResolution::getResolutionByKeyish('duplicate')->getID(); $closed_statuses = array(); $closed_statuses[] = TBGStatus::getStatusByKeyish('closed')->getID(); $closed_statuses[] = TBGStatus::getStatusByKeyish('postponed')->getID(); $closed_statuses[] = TBGStatus::getStatusByKeyish('done')->getID(); $closed_statuses[] = TBGStatus::getStatusByKeyish('fixed')->getID(); $transitions = array(); $transitions['investigateissue'] = array('name' => 'Investigate issue', 'description' => 'Assign the issue to yourself and start investigating it', 'outgoing_step' => 'investigating', 'template' => null, 'pre_validations' => array(TBGWorkflowTransitionValidationRule::RULE_MAX_ASSIGNED_ISSUES => 5), 'actions' => array(TBGWorkflowTransitionAction::ACTION_ASSIGN_ISSUE_SELF => 0)); $transitions['requestmoreinformation'] = array('name' => 'Request more information', 'description' => 'Move issue back to new state for more details', 'outgoing_step' => 'new', 'template' => 'main/updateissueproperties', 'actions' => array(TBGWorkflowTransitionAction::ACTION_CLEAR_ASSIGNEE => 0)); $transitions['confirmissue'] = array('name' => 'Confirm issue', 'description' => 'Confirm that the issue is valid', 'outgoing_step' => 'confirmed', 'template' => null, 'actions' => array(TBGWorkflowTransitionAction::ACTION_SET_PERCENT => 10, TBGWorkflowTransitionAction::ACTION_SET_PRIORITY)); $transitions['rejectissue'] = array('name' => 'Reject issue', 'description' => 'Reject the issue as invalid', 'outgoing_step' => 'rejected', 'template' => 'main/updateissueproperties', 'post_validations' => array(TBGWorkflowTransitionValidationRule::RULE_RESOLUTION_VALID => join(',', $rejected_resolutions)), 'actions' => array(TBGWorkflowTransitionAction::ACTION_SET_RESOLUTION => 0, TBGWorkflowTransitionAction::ACTION_SET_PERCENT => 100, TBGWorkflowTransitionAction::ACTION_USER_STOP_WORKING => 0)); $transitions['acceptissue'] = array('name' => 'Accept issue', 'description' => 'Accept the issue and assign it to yourself', 'outgoing_step' => 'inprogress', 'template' => null, 'pre_validations' => array(TBGWorkflowTransitionValidationRule::RULE_MAX_ASSIGNED_ISSUES => 5), 'actions' => array(TBGWorkflowTransitionAction::ACTION_ASSIGN_ISSUE_SELF => 0, TBGWorkflowTransitionAction::ACTION_USER_START_WORKING => 0)); $transitions['reopenissue'] = array('name' => 'Reopen issue', 'description' => 'Reopen the issue', 'outgoing_step' => 'new', 'template' => null, 'actions' => array(TBGWorkflowTransitionAction::ACTION_CLEAR_RESOLUTION => 0, TBGWorkflowTransitionAction::ACTION_CLEAR_PERCENT => 0)); $transitions['assignissue'] = array('name' => 'Assign issue', 'description' => 'Accept the issue and assign it to someone', 'outgoing_step' => 'inprogress', 'template' => 'main/updateissueproperties', 'actions' => array(TBGWorkflowTransitionAction::ACTION_ASSIGN_ISSUE => 0, TBGWorkflowTransitionAction::ACTION_USER_START_WORKING => 0)); $transitions['markreadyfortesting'] = array('name' => 'Mark ready for testing', 'description' => 'Mark the issue as ready to be tested', 'outgoing_step' => 'readyfortesting', 'template' => null, 'actions' => array(TBGWorkflowTransitionAction::ACTION_CLEAR_ASSIGNEE => 0, TBGWorkflowTransitionAction::ACTION_USER_STOP_WORKING => 0)); $transitions['resolveissue'] = array('name' => 'Resolve issue', 'description' => 'Resolve the issue', 'outgoing_step' => 'closed', 'template' => 'main/updateissueproperties', 'post_validations' => array(TBGWorkflowTransitionValidationRule::RULE_STATUS_VALID => join(',', $closed_statuses), TBGWorkflowTransitionValidationRule::RULE_RESOLUTION_VALID => join(',', $resolved_resolutions)), 'actions' => array(TBGWorkflowTransitionAction::ACTION_SET_STATUS => 0, TBGWorkflowTransitionAction::ACTION_SET_PERCENT => 100, TBGWorkflowTransitionAction::ACTION_SET_RESOLUTION => 0, TBGWorkflowTransitionAction::ACTION_USER_STOP_WORKING => 0)); $transitions['testissuesolution'] = array('name' => 'Test issue solution', 'description' => 'Check whether the solution is valid', 'outgoing_step' => 'testing', 'template' => null, 'actions' => array(TBGWorkflowTransitionAction::ACTION_ASSIGN_ISSUE_SELF => 0, TBGWorkflowTransitionAction::ACTION_USER_START_WORKING => 0)); $transitions['acceptissuesolution'] = array('name' => 'Accept issue solution', 'description' => 'Mark the issue as resolved', 'outgoing_step' => 'closed', 'template' => 'main/updateissueproperties', 'actions' => array(TBGWorkflowTransitionAction::ACTION_SET_RESOLUTION => 0, TBGWorkflowTransitionAction::ACTION_CLEAR_ASSIGNEE => 0, TBGWorkflowTransitionAction::ACTION_USER_STOP_WORKING => 0)); $transitions['rejectissuesolution'] = array('name' => 'Reject issue solution', 'description' => 'Reject the proposed solution and mark the issue as in progress', 'outgoing_step' => 'inprogress', 'template' => null, 'actions' => array(TBGWorkflowTransitionAction::ACTION_SET_RESOLUTION => 0, TBGWorkflowTransitionAction::ACTION_CLEAR_ASSIGNEE => 0, TBGWorkflowTransitionAction::ACTION_USER_STOP_WORKING => 0)); foreach ($transitions as $key => $transition) { $transition_object = new TBGWorkflowTransition(); $transition_object->setName($transition['name']); $transition_object->setDescription($transition['description']); $transition_object->setOutgoingStep($steps[$transition['outgoing_step']]['step']); $transition_object->setTemplate($transition['template']); $transition_object->setWorkflow($workflow); $transition_object->save(); $transitions[$key] = $transition_object; if (array_key_exists('pre_validations', $transition) && is_array($transition['pre_validations'])) { foreach ($transition['pre_validations'] as $type => $validation) { $rule = new TBGWorkflowTransitionValidationRule(); $rule->setTransition($transition_object); $rule->setPre(); $rule->setRule($type); $rule->setRuleValue($validation); $rule->setWorkflow($workflow); $rule->save(); } } if (array_key_exists('post_validations', $transition) && is_array($transition['post_validations'])) { foreach ($transition['post_validations'] as $type => $validation) { $rule = new TBGWorkflowTransitionValidationRule(); $rule->setTransition($transition_object); $rule->setPost(); $rule->setRule($type); $rule->setRuleValue($validation); $rule->setWorkflow($workflow); $rule->save(); } } if (array_key_exists('actions', $transition) && is_array($transition['actions'])) { foreach ($transition['actions'] as $type => $action) { $action_object = new TBGWorkflowTransitionAction(); $action_object->setActionType($type); $action_object->setTransition($transition_object); $action_object->setWorkflow($workflow); if (!is_null($action)) { $action_object->setTargetValue($action); } $action_object->save(); } } } return $transitions; }
public function processIncomingEmailCommand($content, TBGIssue $issue, TBGUser $user) { if (!$issue->isWorkflowTransitionsAvailable()) { return false; } $lines = preg_split("/(\r?\n)/", $content); $first_line = array_shift($lines); $commands = explode(" ", trim($first_line)); $command = array_shift($commands); foreach ($issue->getAvailableWorkflowTransitions() as $transition) { if (strpos(str_replace(array(' ', '/'), array('', ''), mb_strtolower($transition->getName())), str_replace(array(' ', '/'), array('', ''), mb_strtolower($command))) !== false) { foreach ($commands as $single_command) { if (mb_strpos($single_command, '=')) { list($key, $val) = explode('=', $single_command); switch ($key) { case 'resolution': if (($resolution = TBGResolution::getResolutionByKeyish($val)) instanceof TBGResolution) { TBGContext::getRequest()->setParameter('resolution_id', $resolution->getID()); } break; case 'status': if (($status = TBGStatus::getStatusByKeyish($val)) instanceof TBGStatus) { TBGContext::getRequest()->setParameter('status_id', $status->getID()); } break; } } } TBGContext::getRequest()->setParameter('comment_body', join("\n", $lines)); return $transition->transitionIssueToOutgoingStepWithoutRequest($issue); } } }