protected function _getIssueFromRequest(framework\Request $request) { $issue = null; if ($issue_no = framework\Context::getRequest()->getParameter('issue_no')) { $issue = entities\Issue::getIssueFromLink($issue_no); if ($issue instanceof entities\Issue) { if (!$this->selected_project instanceof entities\Project || $issue->getProjectID() != $this->selected_project->getID()) { $issue = null; } } else { framework\Logging::log("Issue no [{$issue_no}] not a valid issue no", 'main', framework\Logging::LEVEL_WARNING_RISK); } } framework\Logging::log('done (Loading issue)'); if ($issue instanceof entities\Issue && (!$issue->hasAccess() || $issue->isDeleted())) { $issue = null; } return $issue; }
public function hasQuickfoundIssues() { if ($this->_quickfound_issues === null) { $this->_quickfound_issues = array(); if ($this->getSearchterm()) { preg_replace_callback(\thebuggenie\core\helpers\TextParser::getIssueRegex(), array('\\thebuggenie\\core\\entities\\SavedSearch', 'extractIssues'), $this->getSearchterm()); } } if (!count($this->_quickfound_issues)) { $issue = Issue::getIssueFromLink($this->getSearchterm()); if ($issue instanceof Issue) { $this->_quickfound_issues[] = $issue; } } return (bool) count($this->_quickfound_issues); }
public static function parseIssuelink($matches, $markdown_format = false) { $theIssue = \thebuggenie\core\entities\Issue::getIssueFromLink($matches[0]); $output = ''; $classname = ''; if ($theIssue instanceof \thebuggenie\core\entities\Issue && ($theIssue->isClosed() || $theIssue->isDeleted())) { $classname = 'closed'; } if ($theIssue instanceof \thebuggenie\core\entities\Issue) { $theIssueUrl = make_url('viewissue', array('issue_no' => $theIssue->getFormattedIssueNo(false), 'project_key' => $theIssue->getProject()->getKey())); if ($markdown_format) { if ($classname != '') { $classname = ' {.' . $classname . '}'; } $output = "[{$matches[0]}]({$theIssueUrl} \"{$theIssue->getFormattedTitle()}\"){$classname}"; } else { $output = ' ' . link_tag($theIssueUrl, $matches[0], array('class' => $classname, 'title' => $theIssue->getFormattedTitle())); } } else { $output = $matches[0]; } return $output; }
public function runUpdateIssueDetails(framework\Request $request) { $this->forward403if(framework\Context::getCurrentProject()->isArchived()); $this->error = false; try { $i18n = framework\Context::getI18n(); $issue = entities\Issue::getIssueFromLink($request['issue_no']); if ($issue->getProject()->getID() != $this->selected_project->getID()) { throw new \Exception($i18n->__('This issue is not valid for this project')); } if (!$issue instanceof entities\Issue) { throw new \Exception($i18n->__('Cannot find this issue')); } $workflow_transition = null; if ($passed_transition = $request['workflow_transition']) { //echo "looking for transition "; $key = str_replace(' ', '', mb_strtolower($passed_transition)); //echo $key . "\n"; foreach ($issue->getAvailableWorkflowTransitions() as $transition) { //echo str_replace(' ', '', mb_strtolower($transition->getName())) . "?"; if (mb_strpos(str_replace(' ', '', mb_strtolower($transition->getName())), $key) !== false) { $workflow_transition = $transition; //echo "found transition " . $transition->getID(); break; } //echo "no"; } if (!$workflow_transition instanceof entities\WorkflowTransition) { throw new \Exception("This transition ({$key}) is not valid"); } } $fields = $request->getRawParameter('fields', array()); $return_values = array(); if ($workflow_transition instanceof entities\WorkflowTransition) { foreach ($fields as $field_key => $field_value) { $classname = "\\thebuggenie\\core\\entities\\" . ucfirst($field_key); $method = "set" . ucfirst($field_key); $choices = $classname::getAll(); $found = false; foreach ($choices as $choice_key => $choice) { if (mb_strpos(str_replace(' ', '', mb_strtolower($choice->getName())), str_replace(' ', '', mb_strtolower($field_value))) !== false) { $request->setParameter($field_key . '_id', $choice->getId()); break; } } } $request->setParameter('comment_body', $request['message']); $return_values['applied_transition'] = $workflow_transition->getName(); if ($workflow_transition->validateFromRequest($request)) { $retval = $workflow_transition->transitionIssueToOutgoingStepFromRequest($issue, $request); $return_values['transition_ok'] = $retval === false ? false : true; } else { $return_values['transition_ok'] = false; $return_values['message'] = "Please pass all information required for this transition"; } } elseif ($issue->isUpdateable()) { foreach ($fields as $field_key => $field_value) { try { if (in_array($field_key, array_merge(array('title', 'state'), entities\Datatype::getAvailableFields(true)))) { switch ($field_key) { case 'state': $issue->setState($field_value == 'open' ? entities\Issue::STATE_OPEN : entities\Issue::STATE_CLOSED); break; case 'title': if ($field_value != '') { $issue->setTitle($field_value); } else { throw new \Exception($i18n->__('Invalid title')); } break; case 'shortname': case 'description': case 'reproduction_steps': $method = "set" . ucfirst($field_key); $issue->{$method}($field_value); break; case 'status': case 'resolution': case 'reproducability': case 'priority': case 'severity': case 'category': $classname = "\\thebuggenie\\core\\entities\\" . ucfirst($field_key); $method = "set" . ucfirst($field_key); $choices = $classname::getAll(); $found = false; foreach ($choices as $choice_key => $choice) { if (str_replace(' ', '', mb_strtolower($choice->getName())) == str_replace(' ', '', mb_strtolower($field_value))) { $issue->{$method}($choice); $found = true; } } if (!$found) { throw new \Exception('Could not find this value'); } break; case 'percent_complete': $issue->setPercentCompleted($field_value); break; case 'owner': case 'assignee': $set_method = "set" . ucfirst($field_key); $unset_method = "un{$set_method}"; switch (mb_strtolower($field_value)) { case 'me': $issue->{$set_method}(framework\Context::getUser()); break; case 'none': $issue->{$unset_method}(); break; default: try { $user = entities\User::findUser(mb_strtolower($field_value)); if ($user instanceof entities\User) { $issue->{$set_method}($user); } } catch (\Exception $e) { throw new \Exception('No such user found'); } break; } break; case 'estimated_time': case 'spent_time': $set_method = "set" . ucfirst(str_replace('_', '', $field_key)); $issue->{$set_method}($field_value); break; case 'milestone': $found = false; foreach ($this->selected_project->getMilestones() as $milestone) { if (str_replace(' ', '', mb_strtolower($milestone->getName())) == str_replace(' ', '', mb_strtolower($field_value))) { $issue->setMilestone($milestone->getID()); $found = true; } } if (!$found) { throw new \Exception('Could not find this milestone'); } break; default: throw new \Exception($i18n->__('Invalid field')); } } $return_values[$field_key] = array('success' => true); } catch (\Exception $e) { $return_values[$field_key] = array('success' => false, 'error' => $e->getMessage()); } } } if (!$workflow_transition instanceof entities\WorkflowTransition) { $issue->getWorkflow()->moveIssueToMatchingWorkflowStep($issue); } if (!array_key_exists('transition_ok', $return_values) || $return_values['transition_ok']) { $comment = new entities\Comment(); $comment->setContent($request->getParameter('message', null, false)); $comment->setPostedBy(framework\Context::getUser()->getID()); $comment->setTargetID($issue->getID()); $comment->setTargetType(entities\Comment::TYPE_ISSUE); $comment->setModuleName('core'); $comment->setIsPublic(true); $comment->setSystemComment(false); $comment->save(); $issue->setSaveComment($comment); $issue->save(); } $this->return_values = $return_values; } catch (\Exception $e) { //$this->getResponse()->setHttpStatus(400); return $this->renderJSON(array('failed' => true, 'error' => $e->getMessage())); } }
/** * Runs one or more regular expressions against a supplied text, extracts * issue numbers from it, and then obtains corresponding issues. The * function will also obtain information about transitions (if this was * specified in the text). This data can be used for transitioning the * issues through a workflow. * * Once the function finishes processing, it will return an array of format: * * array('issues' => tbg_issues, 'transitions' => transitions). * * tbgissues is an array consisting of \thebuggenie\core\entities\Issue instances. * * transitions is an array containing transition arrays. The transition * arrays are accessed with issue numbers as keys (e.g. 'PREFIX-1', * 'PREFIX-5' or '2', '3' etc). Each transition array has the following * format: * * array(0 => command, 1 => parameters) * * command is a string representing the transision command (for example * 'Resolve issue') from the workflow definition. parameters is an array * that contains parameters and their values that should be passed to the * transition step: * * array( 'PARAM1' => 'VALUE1', 'PARAM2' => 'VALUE2', ...) * * * @param string $text Text that should be parsed for issue numbers and transitions. * * @return An array with two elements, one denoting the matched issues, one * denoting the transitions for issues. These elements can be accessed using * keys 'issues', and 'transitions'. The key 'issues' can be used for * accessing an array made-up of \thebuggenie\core\entities\Issue instances. The key 'transitions' * can be used for accessing an array containing transition information * about each issue. The 'transitions' array uses issue numbers as keys, * and contains ordered transition information (see above for detailed * description of format). */ public static function getIssuesFromTextByRegex($text) { $issue_match_regexes = \thebuggenie\core\helpers\TextParser::getIssueRegex(); $issue_numbers = array(); // Issue numbers $issues = array(); // Issue objects $transitions = array(); // Transition information // Iterate over all regular expressions that should be used for // issue/transition matching in commit message. foreach ($issue_match_regexes as $issue_match_regex) { $matched_issue_data = array(); // All data from regexp // If any match is found using the current regular expression, extract // the information. if (preg_match_all($issue_match_regex, $text, $matched_issue_data)) { // Identified issues are kept inside of named regex group. foreach ($matched_issue_data["issues"] as $key => $issue_number) { // Get the matched transitions for the issue. $matched_issue_transitions = $matched_issue_data["transitions"][$key]; // Create an empty array to store transitions for an issue. Don't // overwrite it. Use issue number as key for transitions. if (!array_key_exists($issue_number, $transitions)) { $transitions[$issue_number] = array(); } // Add the transition information (if any) for an issue. if ($matched_issue_transitions) { // Parse the transition information. Each transition string is in // format: // 'TRANSITION1: PARAM1_1=VALUE1_1 PARAM1_2=VALUE1_2; TRANSITION2: PARAM2_1=VALUE2_1 PARAM2_2=VALUE2_2' foreach (explode("; ", $matched_issue_transitions) as $transition) { // Split command from its parameters. $transition_data = explode(": ", $transition); $transition_command = $transition_data[0]; // Set-up array that will contain parameters $transition_parameters = array(); // Process parameters if they were present. if (count($transition_data) == 2) { // Split into induvidual parameters. foreach (explode(" ", $transition_data[1]) as $parameter) { // Only process proper parameters (of format 'PARAM=VALUE') if (mb_strpos($parameter, '=')) { list($param_key, $param_value) = explode('=', $parameter); $transition_parameters[$param_key] = $param_value; } } } // Append the transition information for the current issue number. $transitions[$issue_number][] = array($transition_command, $transition_parameters); } } // Add the issue number to the list. $issue_numbers[] = $issue_number; } } } // Make sure that each issue gets procssed only once for a single commit // (avoid duplication of commits). $unique_issue_numbers = array_unique($issue_numbers); // Fetch all issues affected by the commit. foreach ($unique_issue_numbers as $issue_no) { $issue = Issue::getIssueFromLink($issue_no); if ($issue instanceof \thebuggenie\core\entities\Issue) { $issues[] = $issue; } } // Return array consisting out of two arrays - one with \thebuggenie\core\entities\Issue // instances, and the second one with transition information for those // issues. return array("issues" => $issues, "transitions" => $transitions); }
public function extractIssues($matches) { $issue = entities\Issue::getIssueFromLink($matches["issues"]); if ($issue instanceof entities\Issue) { if (!framework\Context::isProjectContext() || framework\Context::isProjectContext() && $issue->getProjectID() == framework\Context::getCurrentProject()->getID()) { $this->foundissues[$issue->getID()] = $issue; $this->resultcount++; } } }
public static function parseIssuelink($matches) { $theIssue = \thebuggenie\core\entities\Issue::getIssueFromLink($matches[0]); $output = ''; $classname = ''; if ($theIssue instanceof \thebuggenie\core\entities\Issue && ($theIssue->isClosed() || $theIssue->isDeleted())) { $classname = 'closed'; } if ($theIssue instanceof \thebuggenie\core\entities\Issue) { $output = ' ' . link_tag(make_url('viewissue', array('issue_no' => $theIssue->getFormattedIssueNo(false), 'project_key' => $theIssue->getProject()->getKey())), $matches[0], array('class' => $classname, 'title' => $theIssue->getFormattedTitle())); } else { $output = $matches[1]; } return $output; }