<?php␊ |
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */␊ |
/*␊ |
# ***** BEGIN LICENSE BLOCK *****␊ |
# This file is part of InDefero, an open source project management application.␊ |
# Copyright (C) 2008-2011 CĂ©ondo Ltd and contributors.␊ |
#␊ |
# InDefero is free software; you can redistribute it and/or modify␊ |
# it under the terms of the GNU General Public License as published by␊ |
# the Free Software Foundation; either version 2 of the License, or␊ |
# (at your option) any later version.␊ |
#␊ |
# InDefero is distributed in the hope that it will be useful,␊ |
# but WITHOUT ANY WARRANTY; without even the implied warranty of␊ |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the␊ |
# GNU General Public License for more details.␊ |
#␊ |
# You should have received a copy of the GNU General Public License␊ |
# along with this program; if not, write to the Free Software␊ |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA␊ |
#␊ |
# ***** END LICENSE BLOCK ***** */␊ |
␊ |
/**␊ |
* Update an issue.␊ |
*␊ |
* We extend IDF_Form_IssueCreate to reuse the validation logic.␊ |
*/␊ |
class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate␊ |
{␊ |
public $issue = null;␊ |
␊ |
public function initFields($extra=array())␊ |
{␊ |
$this->user = $extra['user'];␊ |
$this->project = $extra['project'];␊ |
$this->issue = $extra['issue'];␊ |
if ($this->user->hasPerm('IDF.project-owner', $this->project)␊ |
or $this->user->hasPerm('IDF.project-member', $this->project)) {␊ |
$this->show_full = true;␊ |
}␊ |
$this->relation_types = $this->project->getRelationsFromConfig();␊ |
if ($this->show_full) {␊ |
$this->fields['summary'] = new Pluf_Form_Field_Varchar(␊ |
array('required' => true,␊ |
'label' => __('Summary'),␊ |
'initial' => $this->issue->summary,␊ |
'widget_attrs' => array(␊ |
'maxlength' => 200,␊ |
'size' => 67,␊ |
),␊ |
));␊ |
}␊ |
$this->fields['content'] = new Pluf_Form_Field_Varchar(␊ |
array('required' => false,␊ |
'label' => __('Comment'),␊ |
'initial' => '',␊ |
'widget' => 'Pluf_Form_Widget_TextareaInput',␊ |
'widget_attrs' => array(␊ |
'cols' => 58,␊ |
'rows' => 9,␊ |
),␊ |
));␊ |
$upload_path = Pluf::f('upload_issue_path', false);␊ |
if (false === $upload_path) {␊ |
throw new Pluf_Exception_SettingError(__('The "upload_issue_path" configuration variable was not set.'));␊ |
}␊ |
$md5 = md5(rand().microtime().Pluf_Utils::getRandomString());␊ |
// We add .dummy to try to mitigate security issues in the␊ |
// case of someone allowing the upload path to be accessible␊ |
// to everybody.␊ |
for ($i=1;$i<4;$i++) {␊ |
$filename = substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/'.substr($md5, 4).'/%s.dummy';␊ |
$this->fields['attachment'.$i] = new Pluf_Form_Field_File(␊ |
array('required' => false,␊ |
'label' => __('Attach a file'),␊ |
'move_function_params' =>␊ |
array('upload_path' => $upload_path,␊ |
'upload_path_create' => true,␊ |
'file_name' => $filename,␊ |
)␊ |
)␊ |
);␊ |
}␊ |
␊ |
if ($this->show_full) {␊ |
$this->fields['status'] = new Pluf_Form_Field_Varchar(␊ |
array('required' => true,␊ |
'label' => __('Status'),␊ |
'initial' => $this->issue->get_status()->name,␊ |
'widget_attrs' => array(␊ |
'maxlength' => 20,␊ |
'size' => 15,␊ |
),␊ |
));␊ |
$initial = ($this->issue->get_owner() == null) ? '' : $this->issue->get_owner()->login;␊ |
$this->fields['owner'] = new Pluf_Form_Field_Varchar(␊ |
array('required' => false,␊ |
'label' => __('Owner'),␊ |
'initial' => $initial,␊ |
'widget_attrs' => array(␊ |
'maxlength' => 20,␊ |
'size' => 15,␊ |
),␊ |
));␊ |
<<<<<<< HEAD␊ |
$this->fields['due_dtime'] = new Pluf_Form_Field_Date(␊ |
=======␊ |
$due_dtime = substr($this->issue->due_dtime, 0, 10);␊ |
$this->fields['due_dtime'] = new IDF_Form_Field_Date(␊ |
>>>>>>> 6996e1185fec6ff4735beab7ccd2267bf1dccac2␊ |
array('required' => false,␊ |
'label' => __('Due date'),␊ |
'initial' => $due_dtime,␊ |
'widget_attrs' => array('size' => 15,),␊ |
));␊ |
␊ |
$idx = 0;␊ |
// note: clean_relation_type0 and clean_relation_issue0 already␊ |
// exist in the base class␊ |
$this->fields['relation_type'.$idx] = new Pluf_Form_Field_Varchar(␊ |
array('required' => false,␊ |
'label' => __('This issue'),␊ |
'initial' => current($this->relation_types),␊ |
'widget_attrs' => array('size' => 15),␊ |
));␊ |
␊ |
$this->fields['relation_issue'.$idx] = new Pluf_Form_Field_Varchar(␊ |
array('required' => false,␊ |
'label' => null,␊ |
'initial' => '',␊ |
'widget_attrs' => array('size' => 10),␊ |
));␊ |
␊ |
++$idx;␊ |
$relatedIssues = $this->issue->getGroupedRelatedIssues(array(), true);␊ |
foreach ($relatedIssues as $verb => $ids) {␊ |
$this->fields['relation_type'.$idx] = new Pluf_Form_Field_Varchar(␊ |
array('required' => false,␊ |
'label' => __('This issue'),␊ |
'initial' => $verb,␊ |
'widget_attrs' => array('size' => 15),␊ |
));␊ |
$m = 'clean_relation_type'.$idx;␊ |
$this->$m = create_function('$form', '␊ |
return $form->clean_relation_type($form->cleaned_data["relation_type'.$idx.'"]);␊ |
');␊ |
␊ |
$this->fields['relation_issue'.$idx] = new Pluf_Form_Field_Varchar(␊ |
array('required' => false,␊ |
'label' => null,␊ |
'initial' => implode(', ', $ids),␊ |
'widget_attrs' => array('size' => 10),␊ |
));␊ |
$m = 'clean_relation_issue'.$idx;␊ |
$this->$m = create_function('$form', '␊ |
return $form->clean_relation_issue($form->cleaned_data["relation_issue'.$idx.'"]);␊ |
');␊ |
␊ |
++$idx;␊ |
}␊ |
␊ |
$tags = $this->issue->get_tags_list();␊ |
for ($i=1;$i<7;$i++) {␊ |
$initial = '';␊ |
if (isset($tags[$i-1])) {␊ |
if ($tags[$i-1]->class != 'Other') {␊ |
$initial = (string) $tags[$i-1];␊ |
} else {␊ |
$initial = $tags[$i-1]->name;␊ |
}␊ |
}␊ |
$this->fields['label'.$i] = new Pluf_Form_Field_Varchar(␊ |
array('required' => false,␊ |
'label' => __('Labels'),␊ |
'initial' => $initial,␊ |
'widget_attrs' => array(␊ |
'maxlength' => 50,␊ |
'size' => 20,␊ |
),␊ |
));␊ |
}␊ |
}␊ |
}␊ |
␊ |
/**␊ |
* Clean the attachments post failure.␊ |
*/␊ |
function failed()␊ |
{␊ |
$upload_path = Pluf::f('upload_issue_path', false);␊ |
if ($upload_path == false) return;␊ |
for ($i=1;$i<4;$i++) {␊ |
if (!empty($this->cleaned_data['attachment'.$i]) and␊ |
file_exists($upload_path.'/'.$this->cleaned_data['attachment'.$i])) {␊ |
@unlink($upload_path.'/'.$this->cleaned_data['attachment'.$i]);␊ |
}␊ |
}␊ |
}␊ |
␊ |
function clean_content()␊ |
{␊ |
$content = trim($this->cleaned_data['content']);␊ |
if (!$this->show_full and strlen($content) == 0) {␊ |
throw new Pluf_Form_Invalid(__('You need to provide a description of the issue.'));␊ |
}␊ |
return $content;␊ |
}␊ |
␊ |
/**␊ |
* We check that something is really changed.␊ |
*/␊ |
public function clean()␊ |
{␊ |
$this->cleaned_data = parent::clean();␊ |
␊ |
// normalize the user's input by removing dublettes and by combining␊ |
// ids from identical verbs in different input fields into one array␊ |
$normRelatedIssues = array();␊ |
for ($idx = 0; isset($this->cleaned_data['relation_type'.$idx]); ++$idx) {␊ |
$verb = $this->cleaned_data['relation_type'.$idx];␊ |
if (empty($verb))␊ |
continue;␊ |
␊ |
$ids = preg_split('/\s*,\s*/', $this->cleaned_data['relation_issue'.$idx],␊ |
-1, PREG_SPLIT_NO_EMPTY);␊ |
if (count($ids) == 0)␊ |
continue;␊ |
␊ |
if (!array_key_exists($verb, $normRelatedIssues))␊ |
$normRelatedIssues[$verb] = array();␊ |
foreach ($ids as $id) {␊ |
if (!in_array($id, $normRelatedIssues[$verb]))␊ |
$normRelatedIssues[$verb][] = $id;␊ |
}␊ |
}␊ |
␊ |
// now look at any added / removed ids␊ |
$added = $removed = array();␊ |
$relatedIssues = $this->issue->getGroupedRelatedIssues(array(), true);␊ |
$added = array_diff_key($normRelatedIssues, $relatedIssues);␊ |
$removed = array_diff_key($relatedIssues, $normRelatedIssues);␊ |
␊ |
$keysToLookAt = array_keys(␊ |
array_intersect_key($relatedIssues, $normRelatedIssues)␊ |
);␊ |
foreach ($keysToLookAt as $key) {␊ |
$a = array_diff($normRelatedIssues[$key], $relatedIssues[$key]);␊ |
if (count($a) > 0)␊ |
$added[$key] = $a;␊ |
$r = array_diff($relatedIssues[$key], $normRelatedIssues[$key]);␊ |
if (count($r) > 0)␊ |
$removed[$key] = $r;␊ |
}␊ |
␊ |
// cache the added / removed data, so we do not have to␊ |
// calculate that again␊ |
$this->cleaned_data['_added_issue_relations'] = $added;␊ |
$this->cleaned_data['_removed_issue_relations'] = $removed;␊ |
␊ |
// As soon as we know that at least one change was done, we␊ |
// return the cleaned data and do not go further.␊ |
if (strlen(trim($this->cleaned_data['content']))) {␊ |
return $this->cleaned_data;␊ |
}␊ |
if ($this->show_full) {␊ |
$status = $this->issue->get_status();␊ |
if (trim($this->cleaned_data['status']) != $status->name) {␊ |
return $this->cleaned_data;␊ |
}␊ |
if (trim($this->issue->summary) != trim($this->cleaned_data['summary'])) {␊ |
return $this->cleaned_data;␊ |
}␊ |
$owner = self::findUser($this->cleaned_data['owner']);␊ |
if ((is_null($owner) and !is_null($this->issue->get_owner()))␊ |
or (!is_null($owner) and is_null($this->issue->get_owner()))␊ |
or ((!is_null($owner) and !is_null($this->issue->get_owner())) and $owner->id != $this->issue->get_owner()->id)) {␊ |
return $this->cleaned_data;␊ |
}␊ |
if (trim($this->issue->due_dtime) != trim($this->cleaned_data['due_dtime'])) {␊ |
return $this->cleaned_data;␊ |
}␊ |
$tags = array();␊ |
for ($i=1;$i<7;$i++) {␊ |
if (strlen($this->cleaned_data['label'.$i]) > 0) {␊ |
if (strpos($this->cleaned_data['label'.$i], ':') !== false) {␊ |
list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2);␊ |
list($class, $name) = array(trim($class), trim($name));␊ |
} else {␊ |
$class = 'Other';␊ |
$name = trim($this->cleaned_data['label'.$i]);␊ |
}␊ |
$tags[] = array($class, $name);␊ |
}␊ |
}␊ |
$oldtags = $this->issue->get_tags_list();␊ |
foreach ($tags as $tag) {␊ |
$found = false;␊ |
foreach ($oldtags as $otag) {␊ |
if ($otag->class == $tag[0] and $otag->name == $tag[1]) {␊ |
$found = true;␊ |
break;␊ |
}␊ |
}␊ |
if (!$found) {␊ |
// new tag not found in the old tags␊ |
return $this->cleaned_data;␊ |
}␊ |
}␊ |
foreach ($oldtags as $otag) {␊ |
$found = false;␊ |
foreach ($tags as $tag) {␊ |
if ($otag->class == $tag[0] and $otag->name == $tag[1]) {␊ |
$found = true;␊ |
break;␊ |
}␊ |
}␊ |
if (!$found) {␊ |
// old tag not found in the new tags␊ |
return $this->cleaned_data;␊ |
}␊ |
}␊ |
␊ |
if (count($this->cleaned_data['_added_issue_relations']) != 0 ||␊ |
count($this->cleaned_data['_removed_issue_relations']) != 0) {␊ |
return $this->cleaned_data;␊ |
}␊ |
}␊ |
// no changes!␊ |
throw new Pluf_Form_Invalid(__('No changes were entered.'));␊ |
}␊ |
␊ |
/**␊ |
* Save the model in the database.␊ |
*␊ |
* @param bool Commit in the database or not. If not, the object␊ |
* is returned but not saved in the database.␊ |
* @return Object Model with data set from the form.␊ |
*/␊ |
function save($commit=true)␊ |
{␊ |
if (!$this->isValid()) {␊ |
throw new Exception(__('Cannot save the model from an invalid form.'));␊ |
}␊ |
if ($this->show_full) {␊ |
// Add a tag for each label␊ |
$tags = array();␊ |
$tagids = array();␊ |
for ($i=1;$i<7;$i++) {␊ |
if (strlen($this->cleaned_data['label'.$i]) > 0) {␊ |
if (strpos($this->cleaned_data['label'.$i], ':') !== false) {␊ |
list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2);␊ |
list($class, $name) = array(trim($class), trim($name));␊ |
} else {␊ |
$class = 'Other';␊ |
$name = trim($this->cleaned_data['label'.$i]);␊ |
}␊ |
$tag = IDF_Tag::add($name, $this->project, $class);␊ |
$tags[] = $tag;␊ |
$tagids[] = $tag->id;␊ |
}␊ |
}␊ |
// Compare between the old and the new data␊ |
$changes = array();␊ |
$oldtags = $this->issue->get_tags_list();␊ |
foreach ($tags as $tag) {␊ |
if (!Pluf_Model_InArray($tag, $oldtags)) {␊ |
if (!isset($changes['lb'])) $changes['lb'] = array();␊ |
if (!isset($changes['lb']['add'])) $changes['lb']['add'] = array();␊ |
if ($tag->class != 'Other') {␊ |
$changes['lb']['add'][] = (string) $tag; //new tag␊ |
} else {␊ |
$changes['lb']['add'][] = (string) $tag->name;␊ |
}␊ |
}␊ |
}␊ |
foreach ($oldtags as $tag) {␊ |
if (!Pluf_Model_InArray($tag, $tags)) {␊ |
if (!isset($changes['lb'])) $changes['lb'] = array();␊ |
if (!isset($changes['lb']['rem'])) $changes['lb']['rem'] = array();␊ |
if ($tag->class != 'Other') {␊ |
$changes['lb']['rem'][] = (string) $tag; //new tag␊ |
} else {␊ |
$changes['lb']['rem'][] = (string) $tag->name;␊ |
}␊ |
}␊ |
}␊ |
// Status, summary and owner␊ |
$status = IDF_Tag::add(trim($this->cleaned_data['status']), $this->project, 'Status');␊ |
if ($status->id != $this->issue->status) {␊ |
$changes['st'] = $status->name;␊ |
}␊ |
if (trim($this->issue->summary) != trim($this->cleaned_data['summary'])) {␊ |
$changes['su'] = trim($this->cleaned_data['summary']);␊ |
}␊ |
$owner = self::findUser($this->cleaned_data['owner']);␊ |
if ((is_null($owner) and !is_null($this->issue->get_owner()))␊ |
or (!is_null($owner) and is_null($this->issue->get_owner()))␊ |
or ((!is_null($owner) and !is_null($this->issue->get_owner())) and $owner->id != $this->issue->get_owner()->id)) {␊ |
$changes['ow'] = (is_null($owner)) ? '---' : $owner->login;␊ |
}␊ |
if (trim($this->issue->due_dtime) != trim($this->cleaned_data['due_dtime'])) {␊ |
$changes['du'] = Pluf_Template_dateFormat($this->cleaned_data['due_dtime']);␊ |
}␊ |
// Issue relations - additions␊ |
foreach ($this->cleaned_data['_added_issue_relations'] as $verb => $ids) {␊ |
$other_verb = $this->relation_types[$verb];␊ |
foreach ($ids as $id) {␊ |
$related_issue = new IDF_Issue($id);␊ |
$rel = new IDF_IssueRelation();␊ |
$rel->issue = $this->issue;␊ |
$rel->verb = $verb;␊ |
$rel->other_issue = $related_issue;␊ |
$rel->submitter = $this->user;␊ |
$rel->create();␊ |
␊ |
$other_rel = new IDF_IssueRelation();␊ |
$other_rel->issue = $related_issue;␊ |
$other_rel->verb = $other_verb;␊ |
$other_rel->other_issue = $this->issue;␊ |
$other_rel->submitter = $this->user;␊ |
$other_rel->create();␊ |
}␊ |
if (!isset($changes['rel'])) $changes['rel'] = array();␊ |
if (!isset($changes['rel']['add'])) $changes['rel']['add'] = array();␊ |
$changes['rel']['add'][] = $verb.' '.implode(', ', $ids);␊ |
}␊ |
// Issue relations - removals␊ |
foreach ($this->cleaned_data['_removed_issue_relations'] as $verb => $ids) {␊ |
foreach ($ids as $id) {␊ |
$db = &Pluf::db();␊ |
$table = Pluf::factory('IDF_IssueRelation')->getSqlTable();␊ |
$sql = new Pluf_SQL('verb=%s AND (␊ |
(issue=%s AND other_issue=%s) OR␊ |
(other_issue=%s AND issue=%s))',␊ |
array($verb,␊ |
$this->issue->id, $id,␊ |
$this->issue->id, $id));␊ |
$db->execute('DELETE FROM '.$table.' WHERE '.$sql->gen());␊ |
}␊ |
␊ |
if (!isset($changes['rel'])) $changes['rel'] = array();␊ |
if (!isset($changes['rel']['rem'])) $changes['rel']['rem'] = array();␊ |
$changes['rel']['rem'][] = $verb.' '.implode(', ', $ids);␊ |
}␊ |
// Update the issue␊ |
$this->issue->batchAssoc('IDF_Tag', $tagids);␊ |
$this->issue->summary = trim($this->cleaned_data['summary']);␊ |
$this->issue->status = $status;␊ |
$this->issue->owner = $owner;␊ |
$this->issue->due_dtime = $this->cleaned_data['due_dtime'];␊ |
}␊ |
// Create the comment␊ |
$comment = new IDF_IssueComment();␊ |
$comment->issue = $this->issue;␊ |
$comment->content = $this->cleaned_data['content'];␊ |
$comment->submitter = $this->user;␊ |
if (!$this->show_full) $changes = array();␊ |
$comment->changes = $changes;␊ |
$comment->create();␊ |
$this->issue->update();␊ |
if ($this->issue->owner != $this->user->id and␊ |
$this->issue->submitter != $this->user->id) {␊ |
$this->issue->setAssoc($this->user); // interested user.␊ |
}␊ |
$attached_files = array();␊ |
for ($i=1;$i<4;$i++) {␊ |
if ($this->cleaned_data['attachment'.$i]) {␊ |
$file = new IDF_IssueFile();␊ |
$file->attachment = $this->cleaned_data['attachment'.$i];␊ |
$file->submitter = $this->user;␊ |
$file->comment = $comment;␊ |
$file->create();␊ |
$attached_files[] = $file;␊ |
}␊ |
}␊ |
/**␊ |
* [signal]␊ |
*␊ |
* IDF_Issue::update␊ |
*␊ |
* [sender]␊ |
*␊ |
* IDF_Form_IssueUpdate␊ |
*␊ |
* [description]␊ |
*␊ |
* This signal allows an application to perform a set of tasks␊ |
* just after the update of an issue.␊ |
*␊ |
* [parameters]␊ |
*␊ |
* array('issue' => $issue,␊ |
* 'comment' => $comment,␊ |
* 'files' => $attached_files);␊ |
*␊ |
*/␊ |
$params = array('issue' => $this->issue,␊ |
'comment' => $comment,␊ |
'files' => $attached_files);␊ |
Pluf_Signal::send('IDF_Issue::update', 'IDF_Form_IssueUpdate',␊ |
$params);␊ |
␊ |
return $this->issue;␊ |
}␊ |
}␊ |