/** * Save changes made to the issue since last time * * @return boolean */ public function _preSave($is_new) { if ($is_new) { if (!$this->_issue_no) { $this->_issue_no = TBGIssuesTable::getTable()->getNextIssueNumberForProductID($this->getProject()->getID()); } if (!$this->_posted) { $this->_posted = NOW; } if (!$this->_last_updated) { $this->_last_updated = NOW; } if (!$this->_posted_by) { $this->_posted_by = TBGContext::getUser(); } $step = $this->getProject()->getWorkflowScheme()->getWorkflowForIssuetype($this->getIssueType())->getFirstStep(); $step->applyToIssue($this); return; } $this->_last_updated = NOW; $comment_lines = array(); $related_issues_to_save = array(); $is_saved_estimated = false; $is_saved_spent = false; $is_saved_assignee = false; $is_saved_owner = false; $changed_properties = $this->_getChangedProperties(); if (count($changed_properties) == 0) { return false; } foreach ($changed_properties as $property => $value) { $compare_value = is_object($this->{$property}) ? $this->{$property}->getID() : $this->{$property}; if ($value['original_value'] != $compare_value) { switch ($property) { case '_title': $this->addLogEntry(TBGLogTable::LOG_ISSUE_UPDATE, TBGContext::getI18n()->__("Title updated")); $comment_lines[] = TBGContext::getI18n()->__("This issue's title has been changed"); break; case '_description': $this->addLogEntry(TBGLogTable::LOG_ISSUE_UPDATE, TBGContext::getI18n()->__("Description updated")); $comment_lines[] = TBGContext::getI18n()->__("This issue's description has been changed"); break; case '_reproduction_steps': $this->addLogEntry(TBGLogTable::LOG_ISSUE_REPRODUCABILITY, TBGContext::getI18n()->__("Reproduction steps updated")); $comment_lines[] = TBGContext::getI18n()->__("This issue's reproduction steps has been changed"); break; case '_category': if ($value['original_value'] != 0) { $old_name = ($old_item = TBGContext::factory()->TBGCategory($value['original_value'])) ? $old_item->getName() : TBGContext::getI18n()->__('Not determined'); } else { $old_name = TBGContext::getI18n()->__('Not determined'); } $new_name = $this->getCategory() instanceof TBGDatatype ? $this->getCategory()->getName() : TBGContext::getI18n()->__('Not determined'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_CATEGORY, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The category has been updated, from '''%previous_category%''' to '''%new_category%'''.", array('%previous_category%' => $old_name, '%new_category%' => $new_name)); break; case '_pain_bug_type': if ($value['original_value'] != 0) { $old_name = ($old_item = self::getPainTypesOrLabel('pain_bug_type', $value['original_value'])) ? $old_item : TBGContext::getI18n()->__('Not determined'); } else { $old_name = TBGContext::getI18n()->__('Not determined'); } $new_name = ($new_item = self::getPainTypesOrLabel('pain_bug_type', $value['current_value'])) ? $new_item : TBGContext::getI18n()->__('Not determined'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_PAIN_BUG_TYPE, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The triaging criteria 'bug type' has been updated, from '''%previous_name%''' to '''%new_name%'''.", array('%previous_name%' => $old_name, '%new_name%' => $new_name)); break; case '_pain_effect': if ($value['original_value'] != 0) { $old_name = ($old_item = self::getPainTypesOrLabel('pain_effect', $value['original_value'])) ? $old_item : TBGContext::getI18n()->__('Not determined'); } else { $old_name = TBGContext::getI18n()->__('Not determined'); } $new_name = ($new_item = self::getPainTypesOrLabel('pain_effect', $value['current_value'])) ? $new_item : TBGContext::getI18n()->__('Not determined'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_PAIN_EFFECT, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The triaging criteria 'effect' has been updated, from '''%previous_name%''' to '''%new_name%'''.", array('%previous_name%' => $old_name, '%new_name%' => $new_name)); break; case '_pain_likelihood': if ($value['original_value'] != 0) { $old_name = ($old_item = self::getPainTypesOrLabel('pain_likelihood', $value['original_value'])) ? $old_item : TBGContext::getI18n()->__('Not determined'); } else { $old_name = TBGContext::getI18n()->__('Not determined'); } $new_name = ($new_item = self::getPainTypesOrLabel('pain_likelihood', $value['current_value'])) ? $new_item : TBGContext::getI18n()->__('Not determined'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_PAIN_LIKELIHOOD, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The triaging criteria 'likelihood' has been updated, from '''%previous_name%''' to '''%new_name%'''.", array('%previous_name%' => $old_name, '%new_name%' => $new_name)); break; case '_user_pain': $this->addLogEntry(TBGLogTable::LOG_ISSUE_PAIN_CALCULATED, $value['original_value'] . ' ⇒ ' . $value['current_value']); $comment_lines[] = TBGContext::getI18n()->__("The calculated user pain has changed, from '''%previous_value%''' to '''%new_value%'''.", array('%previous_value%' => $value['original_value'], '%new_value%' => $value['current_value'])); break; case '_status': if ($value['original_value'] != 0) { $old_name = ($old_item = TBGContext::factory()->TBGStatus($value['original_value'])) ? $old_item->getName() : TBGContext::getI18n()->__('Unknown'); } else { $old_name = TBGContext::getI18n()->__('Not determined'); } $new_name = $this->getStatus() instanceof TBGDatatype ? $this->getStatus()->getName() : TBGContext::getI18n()->__('Not determined'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_STATUS, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The status has been updated, from '''%previous_status%''' to '''%new_status%'''.", array('%previous_status%' => $old_name, '%new_status%' => $new_name)); break; case '_reproducability': if ($value['original_value'] != 0) { $old_name = ($old_item = TBGContext::factory()->TBGReproducability($value['original_value'])) ? $old_item->getName() : TBGContext::getI18n()->__('Unknown'); } else { $old_name = TBGContext::getI18n()->__('Not determined'); } $new_name = $this->getReproducability() instanceof TBGDatatype ? $this->getReproducability()->getName() : TBGContext::getI18n()->__('Not determined'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_REPRODUCABILITY, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The reproducability has been updated, from '''%previous_reproducability%''' to '''%new_reproducability%'''.", array('%previous_reproducability%' => $old_name, '%new_reproducability%' => $new_name)); break; case '_priority': if ($value['original_value'] != 0) { $old_name = ($old_item = TBGContext::factory()->TBGPriority($value['original_value'])) ? $old_item->getName() : TBGContext::getI18n()->__('Unknown'); } else { $old_name = TBGContext::getI18n()->__('Not determined'); } $new_name = $this->getPriority() instanceof TBGDatatype ? $this->getPriority()->getName() : TBGContext::getI18n()->__('Not determined'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_PRIORITY, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The priority has been updated, from '''%previous_priority%''' to '''%new_priority%'''.", array('%previous_priority%' => $old_name, '%new_priority%' => $new_name)); break; case '_assigned_to': case '_assigned_type': if (!$is_saved_assignee) { if ($value['original_value'] != 0) { $old_identifiable = null; if ($this->getChangedPropertyOriginal('_assigned_type') == TBGIdentifiableClass::TYPE_USER) { $old_identifiable = TBGContext::factory()->TBGUser($value['original_value']); } elseif ($this->getChangedPropertyOriginal('_assigned_type') == TBGIdentifiableClass::TYPE_TEAM) { $old_identifiable = TBGContext::factory()->TBGTeam($value['original_value']); } $old_name = $old_identifiable instanceof TBGIdentifiableClass ? $old_identifiable->getName() : TBGContext::getI18n()->__('Unknown'); } else { $old_name = TBGContext::getI18n()->__('Not assigned'); } $new_name = $this->getAssignee() instanceof TBGIdentifiableClass ? $this->getAssignee()->getName() : TBGContext::getI18n()->__('Not assigned'); if (!$this->isAssigned() || $this->getAssigneeType() == TBGIdentifiableClass::TYPE_TEAM) { $this->stopWorkingOnIssue(); } elseif ($this->getAssigneeType() == TBGIdentifiableClass::TYPE_USER) { $this->startWorkingOnIssue($this->getAssignee()); } $this->addLogEntry(TBGLogTable::LOG_ISSUE_ASSIGNED, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The assignee has been changed, from '''%previous_name%''' to '''%new_name%'''.", array('%previous_name%' => $old_name, '%new_name%' => $new_name)); $is_saved_assignee = true; } break; case '_posted_by': $old_identifiable = $value['original_value'] ? TBGContext::factory()->TBGUser($value['original_value']) : TBGContext::getI18n()->__('Unknown'); $old_name = $old_identifiable instanceof TBGIdentifiableClass ? $old_identifiable->getName() : TBGContext::getI18n()->__('Unknown'); $new_name = $this->getPostedBy()->getName(); $this->addLogEntry(TBGLogTable::LOG_ISSUE_POSTED, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The issue's poster has been changed, from '''%previous_name%''' to '''%new_name%'''.", array('%previous_name%' => $old_name, '%new_name%' => $new_name)); break; case '_being_worked_on_by_user': if ($value['original_value'] != 0) { $old_identifiable = TBGContext::factory()->TBGUser($value['original_value']); $old_name = $old_identifiable instanceof TBGIdentifiableClass ? $old_identifiable->getName() : TBGContext::getI18n()->__('Unknown'); } else { $old_name = TBGContext::getI18n()->__('Not being worked on'); } $new_name = $this->getUserWorkingOnIssue() instanceof TBGIdentifiableClass ? $this->getUserWorkingOnIssue()->getName() : TBGContext::getI18n()->__('Not being worked on'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_USERS, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("Information about the user working on this issue has been changed, from '''%previous_name%''' to '''%new_name%'''.", array('%previous_name%' => $old_name, '%new_name%' => $new_name)); break; case '_owner': case '_owner_type': if (!$is_saved_owner) { if ($value['original_value'] != 0) { $old_identifiable = null; if ($this->getChangedPropertyOriginal('_owner_type') == TBGIdentifiableClass::TYPE_USER) { $old_identifiable = TBGContext::factory()->TBGUser($value['original_value']); } elseif ($this->getChangedPropertyOriginal('_owner_type') == TBGIdentifiableClass::TYPE_TEAM) { $old_identifiable = TBGContext::factory()->TBGTeam($value['original_value']); } $old_name = $old_identifiable instanceof TBGIdentifiableClass ? $old_identifiable->getName() : TBGContext::getI18n()->__('Unknown'); } else { $old_name = TBGContext::getI18n()->__('Not owned by anyone'); } $new_name = $this->getOwner() instanceof TBGIdentifiableClass ? $this->getOwner()->getName() : TBGContext::getI18n()->__('Not owned by anyone'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_OWNED, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The owner has been changed, from '''%previous_name%''' to '''%new_name%'''.", array('%previous_name%' => $old_name, '%new_name%' => $new_name)); $is_saved_owner = true; } break; case '_percent_complete': $this->addLogEntry(TBGLogTable::LOG_ISSUE_PERCENT, $value['original_value'] . '% ⇒ ' . $this->getPercentCompleted() . '%'); $comment_lines[] = TBGContext::getI18n()->__("This issue's progression has been updated to %percent_completed% percent completed.", array('%percent_completed%' => $this->getPercentCompleted())); break; case '_resolution': if ($value['original_value'] != 0) { $old_name = ($old_item = TBGContext::factory()->TBGResolution($value['original_value'])) ? $old_item->getName() : TBGContext::getI18n()->__('Unknown'); } else { $old_name = TBGContext::getI18n()->__('Not determined'); } $new_name = $this->getResolution() instanceof TBGDatatype ? $this->getResolution()->getName() : TBGContext::getI18n()->__('Not determined'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_RESOLUTION, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The resolution has been updated, from '''%previous_resolution%''' to '''%new_resolution%'''.", array('%previous_resolution%' => $old_name, '%new_resolution%' => $new_name)); break; case '_severity': if ($value['original_value'] != 0) { $old_name = ($old_item = TBGContext::factory()->TBGSeverity($value['original_value'])) ? $old_item->getName() : TBGContext::getI18n()->__('Unknown'); } else { $old_name = TBGContext::getI18n()->__('Not determined'); } $new_name = $this->getSeverity() instanceof TBGDatatype ? $this->getSeverity()->getName() : TBGContext::getI18n()->__('Not determined'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_SEVERITY, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The severity has been updated, from '''%previous_severity%''' to '''%new_severity%'''.", array('%previous_severity%' => $old_name, '%new_severity%' => $new_name)); break; case '_milestone': if ($value['original_value'] != 0) { $old_name = ($old_item = TBGContext::factory()->TBGMilestone($value['original_value'])) ? $old_item->getName() : TBGContext::getI18n()->__('Not determined'); } else { $old_name = TBGContext::getI18n()->__('Not determined'); } $new_name = $this->getMilestone() instanceof TBGMilestone ? $this->getMilestone()->getName() : TBGContext::getI18n()->__('Not determined'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_MILESTONE, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The milestone has been updated, from '''%previous_milestone%''' to '''%new_milestone%'''.", array('%previous_milestone%' => $old_name, '%new_milestone%' => $new_name)); break; case '_issuetype': if ($value['original_value'] != 0) { $old_name = ($old_item = TBGContext::factory()->TBGIssuetype($value['original_value'])) ? $old_item->getName() : TBGContext::getI18n()->__('Unknown'); } else { $old_name = TBGContext::getI18n()->__('Unknown'); } $new_name = $this->getIssuetype() instanceof TBGIssuetype ? $this->getIssuetype()->getName() : TBGContext::getI18n()->__('Unknown'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_ISSUETYPE, $old_name . ' ⇒ ' . $new_name); $comment_lines[] = TBGContext::getI18n()->__("The issue type has been updated, from '''%previous_type%''' to '''%new_type%'''.", array('%previous_type%' => $old_name, '%new_type%' => $new_name)); break; case '_estimated_months': case '_estimated_weeks': case '_estimated_days': case '_estimated_hours': case '_estimated_points': if (!$is_saved_estimated) { $old_time = array('months' => $this->getChangedPropertyOriginal('_estimated_months'), 'weeks' => $this->getChangedPropertyOriginal('_estimated_weeks'), 'days' => $this->getChangedPropertyOriginal('_estimated_days'), 'hours' => $this->getChangedPropertyOriginal('_estimated_hours'), 'points' => $this->getChangedPropertyOriginal('_estimated_points')); $old_formatted_time = array_sum($old_time) > 0 ? $this->getFormattedTime($old_time) : TBGContext::getI18n()->__('Not estimated'); $new_formatted_time = $this->hasEstimatedTime() ? $this->getFormattedTime($this->getEstimatedTime()) : TBGContext::getI18n()->__('Not estimated'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_TIME_ESTIMATED, $old_formatted_time . ' ⇒ ' . $new_formatted_time); $comment_lines[] = TBGContext::getI18n()->__("The issue has been (re-)estimated, from '''%previous_time%''' to '''%new_time%'''.", array('%previous_time%' => $old_formatted_time, '%new_time%' => $new_formatted_time)); $is_saved_estimated = true; } break; case '_spent_months': case '_spent_weeks': case '_spent_days': case '_spent_hours': case '_spent_points': if (!$is_saved_spent) { $old_time = array('months' => $this->getChangedPropertyOriginal('_spent_months'), 'weeks' => $this->getChangedPropertyOriginal('_spent_weeks'), 'days' => $this->getChangedPropertyOriginal('_spent_days'), 'hours' => $this->getChangedPropertyOriginal('_spent_hours'), 'points' => $this->getChangedPropertyOriginal('_spent_points')); $old_formatted_time = array_sum($old_time) > 0 ? $this->getFormattedTime($old_time) : TBGContext::getI18n()->__('No time spent'); $new_formatted_time = $this->hasSpentTime() ? $this->getFormattedTime($this->getSpentTime()) : TBGContext::getI18n()->__('No time spent'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_TIME_SPENT, $old_formatted_time . ' ⇒ ' . $new_formatted_time); $comment_lines[] = TBGContext::getI18n()->__("Time spent on this issue, from '''%previous_time%''' to '''%new_time%'''.", array('%previous_time%' => $old_formatted_time, '%new_time%' => $new_formatted_time)); $is_saved_spent = true; } break; case '_state': if ($this->isClosed()) { $this->addLogEntry(TBGLogTable::LOG_ISSUE_CLOSE); $comment_lines[] = TBGContext::getI18n()->__("This issue has been closed"); if ($this->getMilestone() instanceof TBGMilestone) { if ($this->getMilestone()->isSprint()) { if (!$this->getIssueType()->isTask()) { $this->setSpentPoints($this->getEstimatedPoints()); } else { if ($this->getSpentHours() < $this->getEstimatedHours()) { $this->setSpentHours($this->getEstimatedHours()); } foreach ($this->getParentIssues() as $parent_issue) { if ($parent_issue->checkTaskStates()) { $related_issues_to_save[$parent_issue->getID()] = true; } } } } $this->getMilestone()->updateStatus(); } } else { $this->addLogEntry(TBGLogTable::LOG_ISSUE_REOPEN); $comment_lines[] = TBGContext::getI18n()->__("This issue has been reopened"); } break; case '_blocking': if ($this->isBlocking()) { $this->addLogEntry(TBGLogTable::LOG_ISSUE_BLOCKED); $comment_lines[] = TBGContext::getI18n()->__("This issue is now blocking the next release"); } else { $this->addLogEntry(TBGLogTable::LOG_ISSUE_UNBLOCKED); $comment_lines[] = TBGContext::getI18n()->__("This issue is no longer blocking the next release"); } break; default: if (substr($property, 0, 12) == '_customfield') { $key = substr($property, 12); $customdatatype = TBGCustomDatatype::getByKey($key); switch ($customdatatype->getType()) { case TBGCustomDatatype::INPUT_TEXT: $new_value = $this->getCustomField($key) != '' ? $this->getCustomField($key) : TBGContext::getI18n()->__('Unknown'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_CUSTOMFIELD_CHANGED, $new_value); $comment_lines[] = TBGContext::getI18n()->__("The custom field %customfield_name% has been changed to '''%new_value%'''.", array('%customfield_name%' => $customdatatype->getDescription(), '%new_value%' => $new_value)); break; case TBGCustomDatatype::INPUT_TEXTAREA_SMALL: case TBGCustomDatatype::INPUT_TEXTAREA_MAIN: $new_value = $this->getCustomField($key) != '' ? $this->getCustomField($key) : TBGContext::getI18n()->__('Unknown'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_CUSTOMFIELD_CHANGED, $new_value); $comment_lines[] = TBGContext::getI18n()->__("The custom field %customfield_name% has been changed.", array('%customfield_name%' => $customdatatype->getDescription())); break; case TBGCustomDatatype::EDITIONS_CHOICE: case TBGCustomDatatype::COMPONENTS_CHOICE: case TBGCustomDatatype::RELEASES_CHOICE: case TBGCustomDatatype::STATUS_CHOICE: $old_object = null; $new_object = null; try { switch ($customdatatype->getType()) { case TBGCustomDatatype::EDITIONS_CHOICE: $old_object = TBGContext::factory()->TBGEdition($value['original_value']); break; case TBGCustomDatatype::COMPONENTS_CHOICE: $old_object = TBGContext::factory()->TBGComponent($value['original_value']); break; case TBGCustomDatatype::RELEASES_CHOICE: $old_object = TBGContext::factory()->TBGBuild($value['original_value']); case TBGCustomDatatype::STATUS_CHOICE: $old_object = TBGContext::factory()->TBGStatus($value['original_value']); break; } } catch (Exception $e) { } try { switch ($customdatatype->getType()) { case TBGCustomDatatype::EDITIONS_CHOICE: $new_object = TBGContext::factory()->TBGEdition($this->getCustomField($key)); break; case TBGCustomDatatype::COMPONENTS_CHOICE: $new_object = TBGContext::factory()->TBGComponent($this->getCustomField($key)); break; case TBGCustomDatatype::RELEASES_CHOICE: $new_object = TBGContext::factory()->TBGBuild($this->getCustomField($key)); case TBGCustomDatatype::STATUS_CHOICE: $new_object = TBGContext::factory()->TBGStatus($this->getCustomField($key)); break; } } catch (Exception $e) { } $old_value = is_object($old_object) ? $old_object->getName() : TBGContext::getI18n()->__('Unknown'); $new_value = is_object($new_object) ? $new_object->getName() : TBGContext::getI18n()->__('Unknown'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_CUSTOMFIELD_CHANGED, $old_value . ' ⇒ ' . $new_value); $comment_lines[] = TBGContext::getI18n()->__("The custom field %customfield_name% has been updated, from '''%previous_value%''' to '''%new_value%'''.", array('%customfield_name%' => $customdatatype->getDescription(), '%previous_value%' => $old_value, '%new_value%' => $new_value)); break; default: $old_value = ($old_item = TBGCustomDatatypeOption::getByValueAndKey($value['original_value'], $key)) ? $old_item->getName() : TBGContext::getI18n()->__('Unknown'); $new_value = $this->getCustomField($key) instanceof TBGCustomDatatypeOption ? $this->getCustomField($key)->getName() : TBGContext::getI18n()->__('Unknown'); $this->addLogEntry(TBGLogTable::LOG_ISSUE_CUSTOMFIELD_CHANGED, $old_value . ' ⇒ ' . $new_value); $comment_lines[] = TBGContext::getI18n()->__("The custom field %customfield_name% has been updated, from '''%previous_value%''' to '''%new_value%'''.", array('%customfield_name%' => $customdatatype->getDescription(), '%previous_value%' => $old_value, '%new_value%' => $new_value)); break; } } break; } } } if (count($comment_lines) == 0) { $comment = TBGContext::getI18n()->__("The issue has been updated"); $this->comment = $this->addSystemComment(TBGContext::getI18n()->__('Issue updated'), $comment, TBGContext::getUser()->getID()); $this->comment_lines = $comment_lines; } else { $comment = TBGContext::getI18n()->__("The issue was updated with the following change(s):%list_of_changes%", array('%list_of_changes%' => "\n* " . join("\n* ", $comment_lines)), true); $this->comment = $this->addSystemComment(TBGContext::getI18n()->__('Issue updated'), $comment, TBGContext::getUser()->getID()); $this->comment_lines = $comment_lines; } if ($is_saved_estimated) { B2DB::getTable('TBGIssueEstimates')->saveEstimate($this->getID(), $this->_estimated_months, $this->_estimated_weeks, $this->_estimated_days, $this->_estimated_hours, $this->_estimated_points); } if ($is_saved_spent) { B2DB::getTable('TBGIssueSpentTimes')->saveSpentTime($this->getID(), $this->_spent_months, $this->_spent_weeks, $this->_spent_days, $this->_spent_hours, $this->_spent_points); } $this->related_issues_to_save = $related_issues_to_save; $this->_clearChangedProperties(); }
/** * Sets an issue field to a specified value * * @param TBGRequest $request */ public function runIssueSetField(TBGRequest $request) { if ($issue_id = $request->getParameter('issue_id')) { try { $issue = TBGContext::factory()->TBGIssue($issue_id); } catch (Exception $e) { $this->getResponse()->setHttpStatus(400); return $this->renderText('fail'); } } else { $this->getResponse()->setHttpStatus(400); return $this->renderText('no issue'); } TBGContext::loadLibrary('common'); if (!$issue instanceof TBGIssue) { return false; } switch ($request->getParameter('field')) { case 'description': if (!$issue->canEditDescription()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } $issue->setDescription($request->getRawParameter('value')); return $this->renderJSON(array('changed' => $issue->isDescriptionChanged(), 'field' => array('id' => (int) ($issue->getDescription() != ''), 'name' => tbg_parse_text($issue->getDescription(), false, null, array('issue' => $issue))), 'description' => tbg_parse_text($issue->getDescription(), false, null, array('issue' => $issue)))); break; case 'reproduction_steps': if (!$issue->canEditReproductionSteps()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } $issue->setReproductionSteps($request->getRawParameter('value')); return $this->renderJSON(array('changed' => $issue->isReproductionStepsChanged(), 'field' => array('id' => (int) ($issue->getReproductionSteps() != ''), 'name' => tbg_parse_text($issue->getReproductionSteps(), false, null, array('issue' => $issue))), 'reproduction_steps' => tbg_parse_text($issue->getReproductionSteps(), false, null, array('issue' => $issue)))); break; case 'title': if (!$issue->canEditTitle()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } if ($request->getParameter('value') == '') { return $this->renderJSON(array('changed' => false, 'failed' => true, 'error' => TBGContext::getI18n()->__('You have to provide a title'))); } else { $issue->setTitle($request->getRawParameter('value')); return $this->renderJSON(array('changed' => $issue->isTitleChanged(), 'field' => array('id' => 1, 'name' => strip_tags($issue->getTitle())), 'title' => strip_tags($issue->getTitle()))); } break; case 'percent': if (!$issue->canEditPercentage()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } $issue->setPercentCompleted($request->getParameter('percent')); return $this->renderJSON(array('changed' => $issue->isPercentCompletedChanged(), 'percent' => $issue->getPercentCompleted())); break; case 'estimated_time': if (!$issue->canEditEstimatedTime()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } if ($request->getParameter('estimated_time') != TBGContext::getI18n()->__('Enter your estimate here') && $request->getParameter('estimated_time')) { $issue->setEstimatedTime($request->getParameter('estimated_time')); } elseif ($request->hasParameter('value')) { $issue->setEstimatedTime($request->getParameter('value')); } else { $issue->setEstimatedMonths($request->getParameter('estimated_time_months')); $issue->setEstimatedWeeks($request->getParameter('estimated_time_weeks')); $issue->setEstimatedDays($request->getParameter('estimated_time_days')); $issue->setEstimatedHours($request->getParameter('estimated_time_hours')); $issue->setEstimatedPoints($request->getParameter('estimated_time_points')); } return $this->renderJSON(array('changed' => $issue->isEstimatedTimeChanged(), 'field' => $issue->hasEstimatedTime() ? array('id' => 1, 'name' => $issue->getFormattedTime($issue->getEstimatedTime())) : array('id' => 0), 'values' => $issue->getEstimatedTime())); break; case 'owned_by': case 'posted_by': case 'assigned_to': if ($request->getParameter('field') == 'owned_by' && !$issue->canEditOwnedBy()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } elseif ($request->getParameter('field') == 'posted_by' && !$issue->canEditPostedBy()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } elseif ($request->getParameter('field') == 'assigned_to' && !$issue->canEditAssignedTo()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } if ($request->hasParameter('value')) { if ($request->hasParameter('identifiable_type')) { if (in_array($request->getParameter('identifiable_type'), array(TBGIdentifiableClass::TYPE_USER, TBGIdentifiableClass::TYPE_TEAM))) { switch ($request->getParameter('identifiable_type')) { case TBGIdentifiableClass::TYPE_USER: $identified = TBGContext::factory()->TBGUser($request->getParameter('value')); break; case TBGIdentifiableClass::TYPE_TEAM: $identified = TBGContext::factory()->TBGTeam($request->getParameter('value')); break; } if ($identified instanceof TBGIdentifiableClass) { if ((bool) $request->getParameter('teamup', false)) { $team = new TBGTeam(); $team->setName($identified->getBuddyname() . ' & ' . TBGContext::getUser()->getBuddyname()); $team->setOndemand(true); $team->save(); $team->addMember($identified); $team->addMember(TBGContext::getUser()); $identified = $team; } if ($request->getParameter('field') == 'owned_by') { $issue->setOwner($identified); } elseif ($request->getParameter('field') == 'assigned_to') { $issue->setAssignee($identified); } } } else { if ($request->getParameter('field') == 'owned_by') { $issue->unsetOwner(); } elseif ($request->getParameter('field') == 'assigned_to') { $issue->unsetAssignee(); } } } elseif ($request->getParameter('field') == 'posted_by') { $identified = TBGContext::factory()->TBGUser($request->getParameter('value')); if ($identified instanceof TBGIdentifiableClass) { $issue->setPostedBy($identified); } } if ($request->getParameter('field') == 'owned_by') { return $this->renderJSON(array('changed' => $issue->isOwnedByChanged(), 'field' => $issue->isOwned() ? array('id' => $issue->getOwnerID(), 'name' => $issue->getOwnerType() == TBGIdentifiableClass::TYPE_USER ? $this->getComponentHTML('main/userdropdown', array('user' => $issue->getOwner())) : $this->getComponentHTML('main/teamdropdown', array('team' => $issue->getOwner()))) : array('id' => 0))); } if ($request->getParameter('field') == 'posted_by') { return $this->renderJSON(array('changed' => $issue->isPostedByChanged(), 'field' => array('id' => $issue->getPostedByID(), 'name' => $this->getComponentHTML('main/userdropdown', array('user' => $issue->getPostedBy()))))); } if ($request->getParameter('field') == 'assigned_to') { return $this->renderJSON(array('changed' => $issue->isAssignedToChanged(), 'field' => $issue->isAssigned() ? array('id' => $issue->getAssigneeID(), 'name' => $issue->getAssigneeType() == TBGIdentifiableClass::TYPE_USER ? $this->getComponentHTML('main/userdropdown', array('user' => $issue->getAssignee())) : $this->getComponentHTML('main/teamdropdown', array('team' => $issue->getAssignee()))) : array('id' => 0))); } } break; case 'spent_time': if (!$issue->canEditSpentTime()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } if ($request->getParameter('spent_time') != TBGContext::getI18n()->__('Enter time spent here') && $request->getParameter('spent_time')) { $function = $request->hasParameter('spent_time_added_text') ? 'addSpentTime' : 'setSpentTime'; $issue->{$function}($request->getParameter('spent_time')); } elseif ($request->hasParameter('value')) { $issue->setSpentTime($request->getParameter('value')); } else { if ($request->hasParameter('spent_time_added_input')) { $issue->addSpentMonths($request->getParameter('spent_time_months')); $issue->addSpentWeeks($request->getParameter('spent_time_weeks')); $issue->addSpentDays($request->getParameter('spent_time_days')); $issue->addSpentHours($request->getParameter('spent_time_hours')); $issue->addSpentPoints($request->getParameter('spent_time_points')); } else { $issue->setSpentMonths($request->getParameter('spent_time_months')); $issue->setSpentWeeks($request->getParameter('spent_time_weeks')); $issue->setSpentDays($request->getParameter('spent_time_days')); $issue->setSpentHours($request->getParameter('spent_time_hours')); $issue->setSpentPoints($request->getParameter('spent_time_points')); } } return $this->renderJSON(array('changed' => $issue->isSpentTimeChanged(), 'field' => $issue->hasSpentTime() ? array('id' => 1, 'name' => $issue->getFormattedTime($issue->getSpentTime())) : array('id' => 0), 'values' => $issue->getSpentTime())); break; case 'category': case 'resolution': case 'severity': case 'reproducability': case 'priority': case 'milestone': case 'issuetype': case 'status': case 'pain_bug_type': case 'pain_likelihood': case 'pain_effect': if ($request->getParameter('field') == 'category' && !$issue->canEditCategory()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } elseif ($request->getParameter('field') == 'resolution' && !$issue->canEditResolution()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } elseif ($request->getParameter('field') == 'severity' && !$issue->canEditSeverity()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } elseif ($request->getParameter('field') == 'reproducability' && !$issue->canEditReproducability()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } elseif ($request->getParameter('field') == 'priority' && !$issue->canEditPriority()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } elseif ($request->getParameter('field') == 'milestone' && !$issue->canEditMilestone()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } elseif ($request->getParameter('field') == 'issuetype' && !$issue->canEditIssuetype()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } elseif ($request->getParameter('field') == 'status' && !$issue->canEditStatus()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } elseif (in_array($request->getParameter('field'), array('pain_bug_type', 'pain_likelihood', 'pain_effect')) && !$issue->canEditUserPain()) { return $this->renderJSON(array('changed' => false, 'error' => TBGContext::getI18n()->__('You do not have permission to perform this action'))); } try { $classname = null; $parameter_name = strtolower($request->getParameter('field')); $parameter_id_name = "{$parameter_name}_id"; $is_pain = in_array($parameter_name, array('pain_bug_type', 'pain_likelihood', 'pain_effect')); if ($is_pain) { switch ($parameter_name) { case 'pain_bug_type': $set_function_name = 'setPainBugType'; $is_changed_function_name = 'isPainBugTypeChanged'; $get_pain_type_label_function = 'getPainBugTypeLabel'; break; case 'pain_likelihood': $set_function_name = 'setPainLikelihood'; $is_changed_function_name = 'isPainLikelihoodChanged'; $get_pain_type_label_function = 'getPainLikelihoodLabel'; break; case 'pain_effect': $set_function_name = 'setPainEffect'; $is_changed_function_name = 'isPainEffectChanged'; $get_pain_type_label_function = 'getPainEffectLabel'; break; } } else { $classname = 'TBG' . ucfirst($parameter_name); $lab_function_name = $classname; $set_function_name = 'set' . ucfirst($parameter_name); $is_changed_function_name = 'is' . ucfirst($parameter_name) . 'Changed'; } if ($request->hasParameter($parameter_id_name)) { $parameter_id = $request->getParameter($parameter_id_name); if ($parameter_id !== 0) { $is_valid = $is_pain ? in_array($parameter_id, array_keys(TBGIssue::getPainTypesOrLabel($parameter_name))) : $parameter_id == 0 || ($parameter = TBGContext::factory()->{$lab_function_name}($parameter_id)) instanceof TBGIdentifiableClass; } if ($parameter_id == 0 || $parameter_id !== 0 && $is_valid) { if ($classname == 'TBGIssuetype') { $visible_fields = $issue->getIssuetype() instanceof TBGIssuetype ? $issue->getProject()->getVisibleFieldsArray($issue->getIssuetype()->getID()) : array(); } else { $visible_fields = null; } $issue->{$set_function_name}($parameter_id); if ($is_pain) { if (!$issue->{$is_changed_function_name}()) { return $this->renderJSON(array('changed' => false, 'field' => array('id' => 0), 'user_pain' => $issue->getUserPain(), 'user_pain_diff_text' => $issue->getUserPainDiffText())); } return $parameter_id == 0 ? $this->renderJSON(array('changed' => true, 'field' => array('id' => 0), 'user_pain' => $issue->getUserPain(), 'user_pain_diff_text' => $issue->getUserPainDiffText())) : $this->renderJSON(array('changed' => true, 'field' => array('id' => $parameter_id, 'name' => $issue->{$get_pain_type_label_function}()), 'user_pain' => $issue->getUserPain(), 'user_pain_diff_text' => $issue->getUserPainDiffText())); } else { if (!$issue->{$is_changed_function_name}()) { return $this->renderJSON(array('changed' => false)); } if (isset($parameter)) { $name = $parameter->getName(); } else { $name = null; } $field = array('id' => $parameter_id, 'name' => $name); if ($classname == 'TBGIssuetype') { TBGContext::loadLibrary('ui'); $field['src'] = htmlspecialchars(TBGContext::getTBGPath() . 'themes/' . TBGSettings::getThemeName() . '/' . $issue->getIssuetype()->getIcon() . '_small.png'); } return $parameter_id == 0 ? $this->renderJSON(array('changed' => true, 'field' => array('id' => 0))) : $this->renderJSON(array('changed' => true, 'visible_fields' => $visible_fields, 'field' => $field)); } } } } catch (Exception $e) { $this->getResponse()->setHttpStatus(400); return $this->renderJSON(array('error' => $e->getMessage())); } $this->getResponse()->setHttpStatus(400); return $this->renderJSON(array('error' => TBGContext::getI18n()->__('No valid field value specified'))); break; default: if ($customdatatype = TBGCustomDatatype::getByKey($request->getParameter('field'))) { $key = $customdatatype->getKey(); $customdatatypeoption_value = $request->getParameter("{$key}_value"); if (!$customdatatype->hasCustomOptions()) { switch ($customdatatype->getType()) { case TBGCustomDatatype::EDITIONS_CHOICE: case TBGCustomDatatype::COMPONENTS_CHOICE: case TBGCustomDatatype::RELEASES_CHOICE: case TBGCustomDatatype::STATUS_CHOICE: if ($customdatatypeoption_value == '') { $issue->setCustomField($key, ""); } else { switch ($customdatatype->getType()) { case TBGCustomDatatype::EDITIONS_CHOICE: $temp = new TBGEdition($request->getRawParameter("{$key}_value")); $finalvalue = $temp->getName(); break; case TBGCustomDatatype::COMPONENTS_CHOICE: $temp = new TBGComponent($request->getRawParameter("{$key}_value")); $finalvalue = $temp->getName(); break; case TBGCustomDatatype::RELEASES_CHOICE: $temp = new TBGBuild($request->getRawParameter("{$key}_value")); $finalvalue = $temp->getName(); break; case TBGCustomDatatype::STATUS_CHOICE: $temp = new TBGStatus($request->getRawParameter("{$key}_value")); $finalvalue = $temp->getName(); break; } $issue->setCustomField($key, $request->getRawParameter("{$key}_value")); } if (isset($temp) && $customdatatype->getType() == TBGCustomDatatype::STATUS_CHOICE && is_object($temp)) { $finalvalue = '<div style="border: 1px solid #AAA; background-color: ' . $temp->getColor() . '; font-size: 1px; width: 20px; height: 15px; margin-right: 5px; float: left;" id="status_color"> </div>' . $finalvalue; } $changed_methodname = "isCustomfield{$key}Changed"; if (!$issue->{$changed_methodname}()) { return $this->renderJSON(array('changed' => false)); } return $customdatatypeoption_value == '' ? $this->renderJSON(array('changed' => true, 'field' => array('id' => 0))) : $this->renderJSON(array('changed' => true, 'field' => array('value' => $key, 'name' => $finalvalue))); break; case TBGCustomDatatype::INPUT_TEXTAREA_MAIN: case TBGCustomDatatype::INPUT_TEXTAREA_SMALL: if ($customdatatypeoption_value == '') { $issue->setCustomField($key, ""); } else { $issue->setCustomField($key, $request->getRawParameter("{$key}_value")); } $changed_methodname = "isCustomfield{$key}Changed"; if (!$issue->{$changed_methodname}()) { return $this->renderJSON(array('changed' => false)); } return $customdatatypeoption_value == '' ? $this->renderJSON(array('changed' => true, 'field' => array('id' => 0))) : $this->renderJSON(array('changed' => true, 'field' => array('value' => $key, 'name' => tbg_parse_text($request->getRawParameter("{$key}_value"), false, null, array('issue' => $issue))))); break; default: if ($customdatatypeoption_value == '') { $issue->setCustomField($key, ""); } else { $issue->setCustomField($key, $request->getParameter("{$key}_value")); } $changed_methodname = "isCustomfield{$key}Changed"; if (!$issue->{$changed_methodname}()) { return $this->renderJSON(array('changed' => false)); } return $customdatatypeoption_value == '' ? $this->renderJSON(array('changed' => true, 'field' => array('id' => 0))) : $this->renderJSON(array('changed' => true, 'field' => array('value' => $key, 'name' => $customdatatypeoption_value))); break; } } if ($customdatatypeoption_value == '' || $customdatatypeoption_value && ($customdatatypeoption = TBGCustomDatatypeOption::getByValueAndKey($customdatatypeoption_value, $key)) instanceof TBGCustomDatatypeOption) { if ($customdatatypeoption_value == '') { $issue->setCustomField($key, ""); } else { $issue->setCustomField($key, $customdatatypeoption->getValue()); } $changed_methodname = "isCustomfield{$key}Changed"; if (!$issue->{$changed_methodname}()) { return $this->renderJSON(array('changed' => false)); } return $customdatatypeoption_value == '' ? $this->renderJSON(array('changed' => true, 'field' => array('id' => 0))) : $this->renderJSON(array('changed' => true, 'field' => array('value' => $customdatatypeoption_value, 'name' => $customdatatypeoption->getName()))); } } break; } $this->getResponse()->setHttpStatus(400); return $this->renderJSON(array('error' => TBGContext::getI18n()->__('No valid field specified (%field%)', array('%field%' => $request->getParameter('field'))))); }