/** * Send an email to watchers detailing the updated fields * @param int $issue_id * @param int $update_id */ public function issue_update($issue_id, $update_id) { $f3 = \Base::instance(); if ($f3->get("mail.from")) { $log = new \Log("mail.log"); // Get issue and update data $issue = new \Model\Issue(); $issue->load($issue_id); $f3->set("issue", $issue); $update = new \Model\Custom("issue_update_detail"); $update->load($update_id); // Get issue parent if set if ($issue->parent_id) { $parent = new \Model\Issue(); $parent->load($issue->parent_id); $f3->set("parent", $parent); } // Avoid errors from bad calls if (!$issue->id || !$update->id) { return false; } $changes = new \Model\Issue\Update\Field(); $f3->set("changes", $changes->find(array("issue_update_id = ?", $update->id))); // Get recipient list and remove update user $recipients = $this->_issue_watchers($issue_id); $recipients = array_diff($recipients, array($update->user_email)); // Render message body $f3->set("issue", $issue); $f3->set("update", $update); $text = $this->_render("notification/update.txt"); $body = $this->_render("notification/update.html"); $changes->load(array("issue_update_id = ? AND `field` = 'closed_date' AND old_value = '' and new_value != ''", $update->id)); if ($changes && $changes->id) { $subject = "[#{$issue->id}] - {$issue->name} closed"; } else { $subject = "[#{$issue->id}] - {$issue->name} updated"; } // Send to recipients foreach ($recipients as $recipient) { $this->utf8mail($recipient, $subject, $body, $text); $log->write("Sent update notification to: " . $recipient); } } }
/** * Log and save an issue update * @param boolean $notify * @return Issue\Update */ protected function _saveUpdate($notify = true) { $f3 = \Base::instance(); // Ensure issue is not tied to itself as a parent if ($this->get("id") == $this->get("parent_id")) { $this->set("parent_id", $this->_getPrev("parent_id")); } // Log update $update = new \Model\Issue\Update(); $update->issue_id = $this->id; $update->user_id = $f3->get("user.id"); $update->created_date = date("Y-m-d H:i:s"); if ($f3->exists('update_comment')) { $update->comment_id = $f3->get('update_comment')->id; if ($notify) { $update->notify = 1; } } else { $update->notify = 0; } $update->save(); // Set hours_total to the hours_remaining value if it's 0 or null if ($this->get("hours_remaining") && !$this->get("hours_total")) { $this->set("hours_total", $this->get("hours_remaining")); } // Set hours remaining to 0 if the issue has been closed if ($this->get("closed_date") && $this->get("hours_remaining")) { $this->set("hours_remaining", 0); } // Create a new issue if repeating if ($this->get("closed_date") && $this->get("repeat_cycle") && $this->get("repeat_cycle") != "none") { $repeat_issue = new \Model\Issue(); $repeat_issue->name = $this->get("name"); $repeat_issue->type_id = $this->get("type_id"); $repeat_issue->parent_id = $this->get("parent_id"); $repeat_issue->author_id = $this->get("author_id"); $repeat_issue->owner_id = $this->get("owner_id"); $repeat_issue->description = $this->get("description"); $repeat_issue->priority = $this->get("priority"); $repeat_issue->repeat_cycle = $this->get("repeat_cycle"); $repeat_issue->hours_total = $this->get("hours_total"); $repeat_issue->hours_remaining = $this->get("hours_total"); // Reset hours remaining to start hours $repeat_issue->created_date = date("Y-m-d H:i:s"); // Find a due date in the future switch ($repeat_issue->repeat_cycle) { case 'daily': $repeat_issue->start_date = $this->get("start_date") ? date("Y-m-d", strtotime("tomorrow")) : NULL; $repeat_issue->due_date = date("Y-m-d", strtotime("tomorrow")); break; case 'weekly': $repeat_issue->start_date = $this->get("start_date") ? date("Y-m-d", strtotime($this->get("start_date") . " +1 week")) : NULL; $repeat_issue->due_date = date("Y-m-d", strtotime($this->get("due_date") . " +1 week")); break; case 'monthly': $repeat_issue->start_date = $this->get("start_date") ? date("Y-m-d", strtotime($this->get("start_date") . " +1 month")) : NULL; $repeat_issue->due_date = date("Y-m-d", strtotime($this->get("due_date") . " +1 month")); break; case 'sprint': $sprint = new \Model\Sprint(); $sprint->load(array("start_date > NOW()"), array('order' => 'start_date')); $repeat_issue->start_date = $this->get("start_date") ? $sprint->start_date : NULL; $repeat_issue->due_date = $sprint->end_date; break; default: $repeat_issue->repeat_cycle = 'none'; } // If the issue was in a sprint before, put it in a sprint again. if ($this->get("sprint_id")) { $sprint = new \Model\Sprint(); $sprint->load(array("end_date >= ? AND start_date <= ?", $repeat_issue->due_date, $repeat_issue->due_date), array('order' => 'start_date')); $repeat_issue->sprint_id = $sprint->id; } $repeat_issue->save(); $notification = \Helper\Notification::instance(); $notification->issue_create($repeat_issue->id); $this->set("repeat_cycle", null); } // Log updated fields $updated = 0; $important_changes = 0; $important_fields = array('status', 'name', 'description', 'owner_id', 'priority', 'due_date'); foreach ($this->fields as $key => $field) { if ($field["changed"] && $field["value"] != $this->_getPrev($key)) { $update_field = new \Model\Issue\Update\Field(); $update_field->issue_update_id = $update->id; $update_field->field = $key; $update_field->old_value = $this->_getPrev($key); $update_field->new_value = $field["value"]; $update_field->save(); $updated++; if ($key == 'sprint_id') { $this->resetTaskSprints(); } if (in_array($key, $important_fields)) { $important_changes++; } } } // Delete update if no fields were changed if (!$updated) { $update->delete(); } // Set notify flag if important changes occurred if ($notify && $important_changes) { $update->notify = 1; $update->save(); } // Send back the update return $update->id ? $update : false; }
/** * GET /issues/@id/history * AJAX call for issue history * * @param \Base $f3 * @param array $params */ public function single_history($f3, $params) { // Build updates array $updates_array = array(); $update_model = new \Model\Custom("issue_update_detail"); $updates = $update_model->find(array("issue_id = ?", $params["id"]), array("order" => "created_date DESC")); foreach ($updates as $update) { $update_array = $update->cast(); $update_field_model = new \Model\Issue\Update\Field(); $update_array["changes"] = $update_field_model->find(array("issue_update_id = ?", $update["id"])); $updates_array[] = $update_array; } $f3->set("updates", $updates_array); $this->_printJson(array("total" => count($updates), "html" => $this->_cleanJson(\Helper\View::instance()->render("issues/single/history.html")))); }
/** * Log and save an issue update * @param boolean $notify * @return Issue\Update */ protected function _saveUpdate($notify = true) { $f3 = \Base::instance(); // Ensure issue is not tied to itself as a parent if ($this->id == $this->parent_id) { $this->parent_id = $this->_getPrev("parent_id"); } // Log update $update = new \Model\Issue\Update(); $update->issue_id = $this->id; $update->user_id = $f3->get("user.id"); $update->created_date = date("Y-m-d H:i:s"); if ($f3->exists("update_comment")) { $update->comment_id = $f3->get("update_comment")->id; $update->notify = (int) $notify; } else { $update->notify = 0; } $update->save(); // Set hours_total to the hours_remaining value under certain conditions if ($this->hours_remaining && !$this->hours_total && !$this->_getPrev('hours_remaining') && !$this->_getPrev('hours_total')) { $this->hours_total = $this->hours_remaining; } // Set hours remaining to 0 if the issue has been closed if ($this->closed_date && $this->hours_remaining) { $this->hours_remaining = 0; } // Create a new issue if repeating if ($this->closed_date && $this->repeat_cycle) { $this->repeat($notify); $this->repeat_cycle = null; } // Log updated fields $updated = 0; $important_changes = 0; $important_fields = array('status', 'name', 'description', 'owner_id', 'priority', 'due_date'); foreach ($this->fields as $key => $field) { if ($field["changed"] && $field["value"] != $this->_getPrev($key)) { $update_field = new \Model\Issue\Update\Field(); $update_field->issue_update_id = $update->id; $update_field->field = $key; $update_field->old_value = $this->_getPrev($key); $update_field->new_value = $field["value"]; $update_field->save(); $updated++; if ($key == 'sprint_id') { $this->resetTaskSprints(); } if (in_array($key, $important_fields)) { $important_changes++; } } } // Delete update if no fields were changed if (!$updated) { $update->delete(); } // Set notify flag if important changes occurred if ($notify && $important_changes) { $update->notify = 1; $update->save(); } // Send back the update return $update->id ? $update : false; }