srchub-old

srchub-old Mercurial Source Tree


Root/indefero/src/IDF/Form/IssueUpdate.php

<?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,
                                                                    ),
                                            ));

            $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;
            }
            $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;
            }
            // 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;
        }
        // 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;
    }
}
Source at commit 55305a934bac created 10 years 4 months ago.
By Nathan Adams, Fixing bug where password would not be hashed in database if user updated password

Archive Download this file

Branches

Tags

Page rendered in 0.76922s using 11 queries.