Started ticket 39, add code review.
We now have a limited support of the code review. Still some work to be done to allow the submission of new patches on a given review and update the status. For the moment, only pre-commit review is supported.
This commit is contained in:
		
							
								
								
									
										158
									
								
								src/IDF/Diff.php
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								src/IDF/Diff.php
									
									
									
									
									
								
							@@ -169,4 +169,162 @@ class IDF_Diff
 | 
			
		||||
        return $res;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Review patch.
 | 
			
		||||
     *
 | 
			
		||||
     * Given the original file as a string and the parsed
 | 
			
		||||
     * corresponding diff chunks, generate a side by side view of the
 | 
			
		||||
     * original file and new file with added/removed lines.
 | 
			
		||||
     *
 | 
			
		||||
     * Example of use:
 | 
			
		||||
     *
 | 
			
		||||
     * $diff = new IDF_Diff(file_get_contents($diff_file));
 | 
			
		||||
     * $orig = file_get_contents($orig_file);
 | 
			
		||||
     * $diff->parse();
 | 
			
		||||
     * echo $diff->fileCompare($orig, $diff->files[$orig_file], $diff_file);
 | 
			
		||||
     *
 | 
			
		||||
     * @param string Original file
 | 
			
		||||
     * @param array Chunk description of the diff corresponding to the file
 | 
			
		||||
     * @param string Original file name
 | 
			
		||||
     * @param int Number of lines before/after the chunk to be displayed (10)
 | 
			
		||||
     * @return Pluf_Template_SafeString The table body
 | 
			
		||||
     */
 | 
			
		||||
    public function fileCompare($orig, $chunks, $filename, $context=10) 
 | 
			
		||||
    {
 | 
			
		||||
        $orig_lines = preg_split("/\015\012|\015|\012/", $orig);
 | 
			
		||||
        $new_chunks = $this->mergeChunks($orig_lines, $chunks, $context);
 | 
			
		||||
        return $this->renderCompared($new_chunks, $filename);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function mergeChunks($orig_lines, $chunks, $context=10) 
 | 
			
		||||
    {
 | 
			
		||||
        $spans = array();
 | 
			
		||||
        $new_chunks = array();
 | 
			
		||||
        $min_line = 0;
 | 
			
		||||
        $max_line = 0;
 | 
			
		||||
        //if (count($chunks['chunks_def']) == 0) return '';
 | 
			
		||||
        foreach ($chunks['chunks_def'] as $chunk) {
 | 
			
		||||
            $start = ($chunk[0][0] > $context) ? $chunk[0][0]-$context : 0;
 | 
			
		||||
            $end = (($chunk[0][0]+$chunk[0][1]+$context-1) < count($orig_lines)) ? $chunk[0][0]+$chunk[0][1]+$context-1 : count($orig_lines);
 | 
			
		||||
            $spans[] = array($start, $end);
 | 
			
		||||
        }
 | 
			
		||||
        // merge chunks/get the chunk lines
 | 
			
		||||
        // these are reference lines
 | 
			
		||||
        $chunk_lines = array();
 | 
			
		||||
        foreach ($chunks['chunks'] as $chunk) {
 | 
			
		||||
            foreach ($chunk as $line) {
 | 
			
		||||
                $chunk_lines[] = $line;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $i = 0;
 | 
			
		||||
        foreach ($chunks['chunks'] as $chunk) {
 | 
			
		||||
            $n_chunk = array();
 | 
			
		||||
            // add lines before
 | 
			
		||||
            if ($chunk[0][0] > $spans[$i][0]) {
 | 
			
		||||
                for ($lc=$spans[$i][0];$lc<$chunk[0][0];$lc++) {
 | 
			
		||||
                    $exists = false;
 | 
			
		||||
                    foreach ($chunk_lines as $line) {
 | 
			
		||||
                        if ($lc == $line[0] or ($chunk[0][1]-$chunk[0][0]+$lc) == $line[1]) {
 | 
			
		||||
                            $exists = true;
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!$exists) {
 | 
			
		||||
                        $n_chunk[] = array(
 | 
			
		||||
                                           $lc, 
 | 
			
		||||
                                           $chunk[0][1]-$chunk[0][0]+$lc,
 | 
			
		||||
                                           $orig_lines[$lc-1]
 | 
			
		||||
                                           );
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // add chunk lines
 | 
			
		||||
            foreach ($chunk as $line) {
 | 
			
		||||
                $n_chunk[] = $line;
 | 
			
		||||
            }
 | 
			
		||||
            // add lines after
 | 
			
		||||
            $lline = $line;
 | 
			
		||||
            if (!empty($lline[0]) and $lline[0] < $spans[$i][1]) {
 | 
			
		||||
                for ($lc=$lline[0];$lc<=$spans[$i][1];$lc++) {
 | 
			
		||||
                    $exists = false;
 | 
			
		||||
                    foreach ($chunk_lines as $line) {
 | 
			
		||||
                        if ($lc == $line[0] or ($lline[1]-$lline[0]+$lc) == $line[1]) {
 | 
			
		||||
                            $exists = true;
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!$exists) {
 | 
			
		||||
                        $n_chunk[] = array(
 | 
			
		||||
                                           $lc, 
 | 
			
		||||
                                           $lline[1]-$lline[0]+$lc,
 | 
			
		||||
                                           $orig_lines[$lc-1]
 | 
			
		||||
                                           );
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $new_chunks[] = $n_chunk;
 | 
			
		||||
            $i++;
 | 
			
		||||
        }
 | 
			
		||||
        // Now, each chunk has the right length, we need to merge them
 | 
			
		||||
        // when needed
 | 
			
		||||
        $nnew_chunks = array();
 | 
			
		||||
        $i = 0;
 | 
			
		||||
        foreach ($new_chunks as $chunk) {
 | 
			
		||||
            if ($i>0) {
 | 
			
		||||
                $lline = end($nnew_chunks[$i-1]);
 | 
			
		||||
                if ($chunk[0][0] <= $lline[0]+1) {
 | 
			
		||||
                    // need merging
 | 
			
		||||
                    foreach ($chunk as $line) {
 | 
			
		||||
                        if ($line[0] > $lline[0] or empty($line[0])) {
 | 
			
		||||
                            $nnew_chunks[$i-1][] = $line;
 | 
			
		||||
                        } 
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    $nnew_chunks[] = $chunk;
 | 
			
		||||
                    $i++;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $nnew_chunks[] = $chunk;
 | 
			
		||||
                $i++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $nnew_chunks;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function renderCompared($chunks, $filename)
 | 
			
		||||
    {
 | 
			
		||||
        $fileinfo = IDF_Views_Source::getMimeType($filename);
 | 
			
		||||
        $pretty = '';
 | 
			
		||||
        if (IDF_Views_Source::isSupportedExtension($fileinfo[2])) {
 | 
			
		||||
            $pretty = ' prettyprint';
 | 
			
		||||
        }
 | 
			
		||||
        $out = '';
 | 
			
		||||
        $cc = 1;
 | 
			
		||||
        $i = 0;
 | 
			
		||||
        foreach ($chunks as $chunk) {
 | 
			
		||||
            foreach ($chunk as $line) {
 | 
			
		||||
                $line1 = ' ';
 | 
			
		||||
                $line2 = ' ';
 | 
			
		||||
                $line[2] = (strlen($line[2])) ? self::padLine(Pluf_esc($line[2])) : ' ';
 | 
			
		||||
                if ($line[0] and $line[1]) {
 | 
			
		||||
                    $class = 'diff-c';
 | 
			
		||||
                    $line1 = $line2 = $line[2];
 | 
			
		||||
                } elseif ($line[0]) {
 | 
			
		||||
                    $class = 'diff-r';
 | 
			
		||||
                    $line1 = $line[2];
 | 
			
		||||
                } else {
 | 
			
		||||
                    $class = 'diff-a';
 | 
			
		||||
                    $line2 = $line[2];
 | 
			
		||||
                }
 | 
			
		||||
                $out .= sprintf('<tr class="diff-line"><td class="diff-lc">%s</td><td class="%s mono%s"><code>%s</code></td><td class="diff-lc">%s</td><td class="%s mono%s"><code>%s</code></td></tr>'."\n", $line[0], $class, $pretty, $line1, $line[1], $class, $pretty, $line2);
 | 
			
		||||
            }
 | 
			
		||||
            if (count($chunks) > $cc)
 | 
			
		||||
                $out .= '<tr class="diff-next"><td>...</td><td> </td><td>...</td><td> </td></tr>'."\n";
 | 
			
		||||
            $cc++;
 | 
			
		||||
            $i++;
 | 
			
		||||
        }
 | 
			
		||||
        return Pluf_Template::markSafe($out);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										203
									
								
								src/IDF/Form/ReviewCommentFile.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								src/IDF/Form/ReviewCommentFile.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,203 @@
 | 
			
		||||
<?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 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 ***** */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a new documentation page.
 | 
			
		||||
 *
 | 
			
		||||
 * This create a new page and the corresponding revision.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class IDF_Form_WikiCreate extends Pluf_Form
 | 
			
		||||
{
 | 
			
		||||
    public $user = null;
 | 
			
		||||
    public $project = null;
 | 
			
		||||
    public $show_full = false;
 | 
			
		||||
 | 
			
		||||
    public function initFields($extra=array())
 | 
			
		||||
    {
 | 
			
		||||
        $initial = __('# Introduction
 | 
			
		||||
 | 
			
		||||
Add your content here.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Details
 | 
			
		||||
 | 
			
		||||
Add your content here. Format your content with:
 | 
			
		||||
 | 
			
		||||
* Text in **bold** or *italic*.
 | 
			
		||||
* Headings, paragraphs, and lists.
 | 
			
		||||
* Links to other [[WikiPage]].
 | 
			
		||||
');
 | 
			
		||||
        $this->user = $extra['user'];
 | 
			
		||||
        $this->project = $extra['project'];
 | 
			
		||||
        if ($this->user->hasPerm('IDF.project-owner', $this->project)
 | 
			
		||||
            or $this->user->hasPerm('IDF.project-member', $this->project)) {
 | 
			
		||||
            $this->show_full = true;
 | 
			
		||||
        }
 | 
			
		||||
        $this->fields['title'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => true,
 | 
			
		||||
                                            'label' => __('Page title'),
 | 
			
		||||
                                            'initial' => __('PageName'),
 | 
			
		||||
                                            'widget_attrs' => array(
 | 
			
		||||
                                                       'maxlength' => 200,
 | 
			
		||||
                                                       'size' => 67,
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                            'help_text' => __('The page name must contains only letters, digits and the dash (-) character.'),
 | 
			
		||||
                                            ));
 | 
			
		||||
        $this->fields['summary'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => true,
 | 
			
		||||
                                            'label' => __('Description'),
 | 
			
		||||
                                            'help_text' => __('This one line description is displayed in the list of pages.'),
 | 
			
		||||
                                            'initial' => '',
 | 
			
		||||
                                            'widget_attrs' => array(
 | 
			
		||||
                                                       'maxlength' => 200,
 | 
			
		||||
                                                       'size' => 67,
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                            ));
 | 
			
		||||
        $this->fields['content'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => true,
 | 
			
		||||
                                            'label' => __('Content'),
 | 
			
		||||
                                            'initial' => $initial,
 | 
			
		||||
                                            'widget' => 'Pluf_Form_Widget_TextareaInput',
 | 
			
		||||
                                            'widget_attrs' => array(
 | 
			
		||||
                                                       'cols' => 58,
 | 
			
		||||
                                                       'rows' => 26,
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                            ));
 | 
			
		||||
 | 
			
		||||
        if ($this->show_full) {
 | 
			
		||||
            for ($i=1;$i<4;$i++) {
 | 
			
		||||
                $this->fields['label'.$i] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                            array('required' => false,
 | 
			
		||||
                                                  'label' => __('Labels'),
 | 
			
		||||
                                                  'initial' => '',
 | 
			
		||||
                                                  'widget_attrs' => array(
 | 
			
		||||
                                                       'maxlength' => 50,
 | 
			
		||||
                                                       'size' => 20,
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                                  ));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function clean_title()
 | 
			
		||||
    {
 | 
			
		||||
        $title = $this->cleaned_data['title'];
 | 
			
		||||
        if (preg_match('/[^a-zA-Z0-9\-]/', $title)) {
 | 
			
		||||
            throw new Pluf_Form_Invalid(__('The title contains invalid characters.'));
 | 
			
		||||
        }
 | 
			
		||||
        $sql = new Pluf_SQL('project=%s AND title=%s', 
 | 
			
		||||
                            array($this->project->id, $title));
 | 
			
		||||
        $pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
 | 
			
		||||
        if ($pages->count() > 0) {
 | 
			
		||||
            throw new Pluf_Form_Invalid(__('A page with this title already exists.'));
 | 
			
		||||
        }
 | 
			
		||||
        return $title;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validate the interconnection in the form.
 | 
			
		||||
     */
 | 
			
		||||
    public function clean()
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->show_full) {
 | 
			
		||||
            return $this->cleaned_data;
 | 
			
		||||
        }
 | 
			
		||||
        $conf = new IDF_Conf();
 | 
			
		||||
        $conf->setProject($this->project);
 | 
			
		||||
        $onemax = array();
 | 
			
		||||
        foreach (split(',', $conf->getVal('labels_wiki_one_max', IDF_Form_WikiConf::init_one_max)) as $class) {
 | 
			
		||||
            if (trim($class) != '') {
 | 
			
		||||
                $onemax[] = mb_strtolower(trim($class));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $count = array();
 | 
			
		||||
        for ($i=1;$i<4;$i++) {
 | 
			
		||||
            $this->cleaned_data['label'.$i] = trim($this->cleaned_data['label'.$i]);
 | 
			
		||||
            if (strpos($this->cleaned_data['label'.$i], ':') !== false) {
 | 
			
		||||
                list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2);
 | 
			
		||||
                list($class, $name) = array(mb_strtolower(trim($class)), 
 | 
			
		||||
                                            trim($name));
 | 
			
		||||
            } else {
 | 
			
		||||
                $class = 'other';
 | 
			
		||||
                $name = $this->cleaned_data['label'.$i];
 | 
			
		||||
            }
 | 
			
		||||
            if (!isset($count[$class])) $count[$class] = 1;
 | 
			
		||||
            else $count[$class] += 1;
 | 
			
		||||
            if (in_array($class, $onemax) and $count[$class] > 1) {
 | 
			
		||||
                if (!isset($this->errors['label'.$i])) $this->errors['label'.$i] = array();
 | 
			
		||||
                $this->errors['label'.$i][] = sprintf(__('You cannot provide more than label from the %s class to a page.'), $class);
 | 
			
		||||
                throw new Pluf_Form_Invalid(__('You provided an invalid label.'));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $this->cleaned_data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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.'));
 | 
			
		||||
        }
 | 
			
		||||
        // Add a tag for each label
 | 
			
		||||
        $tags = array();
 | 
			
		||||
        if ($this->show_full) {
 | 
			
		||||
            for ($i=1;$i<4;$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[] = IDF_Tag::add($name, $this->project, $class);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } 
 | 
			
		||||
        // Create the page
 | 
			
		||||
        $page = new IDF_WikiPage();
 | 
			
		||||
        $page->project = $this->project;
 | 
			
		||||
        $page->submitter = $this->user;
 | 
			
		||||
        $page->summary = trim($this->cleaned_data['summary']);
 | 
			
		||||
        $page->title = trim($this->cleaned_data['title']);
 | 
			
		||||
        $page->create();
 | 
			
		||||
        foreach ($tags as $tag) {
 | 
			
		||||
            $page->setAssoc($tag);
 | 
			
		||||
        }
 | 
			
		||||
        // add the first revision
 | 
			
		||||
        $rev = new IDF_WikiRevision();
 | 
			
		||||
        $rev->wikipage = $page;
 | 
			
		||||
        $rev->content = $this->cleaned_data['content'];
 | 
			
		||||
        $rev->submitter = $this->user;
 | 
			
		||||
        $rev->summary = __('Initial page creation');
 | 
			
		||||
        $rev->create();
 | 
			
		||||
        return $page;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										205
									
								
								src/IDF/Form/ReviewCreate.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								src/IDF/Form/ReviewCreate.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,205 @@
 | 
			
		||||
<?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 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 ***** */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a new code review.
 | 
			
		||||
 *
 | 
			
		||||
 * This creates an IDF_Review and the corresponding IDF_Review_Patch.
 | 
			
		||||
 */
 | 
			
		||||
class IDF_Form_ReviewCreate extends Pluf_Form
 | 
			
		||||
{
 | 
			
		||||
    public $user = null;
 | 
			
		||||
    public $project = null;
 | 
			
		||||
    public $show_full = false;
 | 
			
		||||
 | 
			
		||||
    public function initFields($extra=array())
 | 
			
		||||
    {
 | 
			
		||||
        $this->user = $extra['user'];
 | 
			
		||||
        $this->project = $extra['project'];
 | 
			
		||||
        if ($this->user->hasPerm('IDF.project-owner', $this->project)
 | 
			
		||||
            or $this->user->hasPerm('IDF.project-member', $this->project)) {
 | 
			
		||||
            $this->show_full = true;
 | 
			
		||||
        }
 | 
			
		||||
        $this->fields['summary'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => true,
 | 
			
		||||
                                            'label' => __('Summary'),
 | 
			
		||||
                                            'initial' => '',
 | 
			
		||||
                                            'widget_attrs' => array(
 | 
			
		||||
                                                       'maxlength' => 200,
 | 
			
		||||
                                                       'size' => 67,
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                            ));
 | 
			
		||||
        $this->fields['description'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => true,
 | 
			
		||||
                                            'label' => __('Description'),
 | 
			
		||||
                                            'initial' => '',
 | 
			
		||||
                                            'widget' => 'Pluf_Form_Widget_TextareaInput',
 | 
			
		||||
                                            'widget_attrs' => array(
 | 
			
		||||
                                                       'cols' => 58,
 | 
			
		||||
                                                       'rows' => 7,
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                            ));
 | 
			
		||||
        $this->fields['commit'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => true,
 | 
			
		||||
                                            'label' => __('Commit'),
 | 
			
		||||
                                            'initial' => '',
 | 
			
		||||
                                            'widget_attrs' => array(
 | 
			
		||||
                                                       'size' => 42,
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                            ));
 | 
			
		||||
        $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.
 | 
			
		||||
        $filename = substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/'.substr($md5, 4).'/%s.dummy'; 
 | 
			
		||||
        $this->fields['patch'] = new Pluf_Form_Field_File(
 | 
			
		||||
                array('required' => true,
 | 
			
		||||
                      'label' => __('Patch'),
 | 
			
		||||
                      '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' => 'New',
 | 
			
		||||
                                            'widget_attrs' => array(
 | 
			
		||||
                                                       'maxlength' => 20,
 | 
			
		||||
                                                       'size' => 15,
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                            ));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function clean_commit()
 | 
			
		||||
    {
 | 
			
		||||
        $commit = self::findCommit($this->cleaned_data['commit']);
 | 
			
		||||
        if (null == $commit) {
 | 
			
		||||
            throw new Pluf_Form_Invalid(__('You provided an invalid commit.'));
 | 
			
		||||
        }
 | 
			
		||||
        return $this->cleaned_data['commit'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validate the interconnection in the form.
 | 
			
		||||
     */
 | 
			
		||||
    public function clean()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->cleaned_data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function clean_status()
 | 
			
		||||
    {
 | 
			
		||||
        // Check that the status is in the list of official status
 | 
			
		||||
        $tags = $this->project->getTagsFromConfig('labels_issue_open', 
 | 
			
		||||
                                          IDF_Form_IssueTrackingConf::init_open,
 | 
			
		||||
                                          'Status');
 | 
			
		||||
        $tags = array_merge($this->project->getTagsFromConfig('labels_issue_closed', 
 | 
			
		||||
                                          IDF_Form_IssueTrackingConf::init_closed,
 | 
			
		||||
                                          'Status')
 | 
			
		||||
                            , $tags);
 | 
			
		||||
        $found = false;
 | 
			
		||||
        foreach ($tags as $tag) {
 | 
			
		||||
            if ($tag->name == trim($this->cleaned_data['status'])) {
 | 
			
		||||
                $found = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!$found) {
 | 
			
		||||
            throw new Pluf_Form_Invalid(__('You provided an invalid status.'));
 | 
			
		||||
        }
 | 
			
		||||
        return $this->cleaned_data['status'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clean the attachments post failure.
 | 
			
		||||
     */
 | 
			
		||||
    function failed()
 | 
			
		||||
    {
 | 
			
		||||
        $upload_path = Pluf::f('upload_issue_path', false);
 | 
			
		||||
        if ($upload_path == false) return;
 | 
			
		||||
        if (!empty($this->cleaned_data['patch']) and
 | 
			
		||||
            file_exists($upload_path.'/'.$this->cleaned_data['patch'])) {
 | 
			
		||||
                @unlink($upload_path.'/'.$this->cleaned_data['patch']);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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.'));
 | 
			
		||||
        }
 | 
			
		||||
        // Create the review
 | 
			
		||||
        $review = new IDF_Review();
 | 
			
		||||
        $review->project = $this->project;
 | 
			
		||||
        $review->summary = $this->cleaned_data['summary'];
 | 
			
		||||
        $review->submitter = $this->user;
 | 
			
		||||
        $review->status = IDF_Tag::add(trim($this->cleaned_data['status']), $this->project, 'Status');
 | 
			
		||||
        $review->create();
 | 
			
		||||
        // add the first patch
 | 
			
		||||
        $patch = new IDF_Review_Patch();
 | 
			
		||||
        $patch->review = $review;
 | 
			
		||||
        $patch->summary = __('Initial patch to be reviewed.');
 | 
			
		||||
        $patch->description = $this->cleaned_data['description'];
 | 
			
		||||
        $patch->commit = self::findCommit($this->cleaned_data['commit']);
 | 
			
		||||
        $patch->patch = $this->cleaned_data['patch'];
 | 
			
		||||
        $patch->create();
 | 
			
		||||
        return $review;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Based on the given string, try to find the matching commit.
 | 
			
		||||
     *
 | 
			
		||||
     * If no user found, simply returns null.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string Commit
 | 
			
		||||
     * @return IDF_Commit or null
 | 
			
		||||
     */
 | 
			
		||||
    public static function findCommit($string)
 | 
			
		||||
    {
 | 
			
		||||
        $string = trim($string);
 | 
			
		||||
        if (strlen($string) == 0) return null;
 | 
			
		||||
        $gc = new IDF_Commit();
 | 
			
		||||
        $sql = new Pluf_SQL('scm_id=%s', array($string));
 | 
			
		||||
        $gcs = $gc->getList(array('filter' => $sql->gen()));
 | 
			
		||||
        if ($gcs->count() > 0) {
 | 
			
		||||
            return $gcs[0];
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										94
									
								
								src/IDF/Form/ReviewFileComment.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/IDF/Form/ReviewFileComment.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
<?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 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 ***** */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add comments to files in a review.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class IDF_Form_ReviewFileComment extends Pluf_Form
 | 
			
		||||
{
 | 
			
		||||
    public $files = null;
 | 
			
		||||
    public $patch = null;
 | 
			
		||||
    public $user = null;
 | 
			
		||||
 | 
			
		||||
    public function initFields($extra=array())
 | 
			
		||||
    {
 | 
			
		||||
        $this->files = $extra['files'];
 | 
			
		||||
        $this->patch = $extra['patch'];
 | 
			
		||||
        $this->user = $extra['user'];
 | 
			
		||||
        foreach ($this->files as $filename => $def) {
 | 
			
		||||
            $this->fields[md5($filename)] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => false,
 | 
			
		||||
                                            'label' => __('Comment'),
 | 
			
		||||
                                            'initial' => '',
 | 
			
		||||
                                            'widget' => 'Pluf_Form_Widget_TextareaInput',
 | 
			
		||||
                                            'widget_attrs' => array(
 | 
			
		||||
                                                       'cols' => 58,
 | 
			
		||||
                                                       'rows' => 9,
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                            ));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validate the interconnection in the form.
 | 
			
		||||
     */
 | 
			
		||||
    public function clean()
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($this->files as $filename => $def) {
 | 
			
		||||
            if (!empty($this->cleaned_data[md5($filename)])) {
 | 
			
		||||
                return $this->cleaned_data;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        throw new Pluf_Form_Invalid(__('You need to provide comments on at least one file.'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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.'));
 | 
			
		||||
        }
 | 
			
		||||
        foreach ($this->files as $filename => $def) {
 | 
			
		||||
            if (!empty($this->cleaned_data[md5($filename)])) {
 | 
			
		||||
                // Add a comment.
 | 
			
		||||
                $c = new IDF_Review_FileComment();
 | 
			
		||||
                $c->patch = $this->patch;
 | 
			
		||||
                $c->cfile = $filename;
 | 
			
		||||
                $c->submitter = $this->user;
 | 
			
		||||
                $c->content = $this->cleaned_data[md5($filename)];
 | 
			
		||||
                $c->create();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $this->patch->get_review()->update(); // reindex and put up in
 | 
			
		||||
                                              // the list.
 | 
			
		||||
        return $this->patch;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -36,6 +36,7 @@ class IDF_Form_TabsConf extends Pluf_Form
 | 
			
		||||
        $this->project = $extra['project'];
 | 
			
		||||
 | 
			
		||||
        $ak = array('downloads_access_rights' => __('Downloads'),
 | 
			
		||||
                    'review_access_rights' => __('Code Review'),
 | 
			
		||||
                    'wiki_access_rights' => __('Documentation'),
 | 
			
		||||
                    'source_access_rights' => __('Source'),
 | 
			
		||||
                    'issues_access_rights' => __('Issues'),);
 | 
			
		||||
 
 | 
			
		||||
@@ -54,6 +54,7 @@ class IDF_Middleware
 | 
			
		||||
            $request->conf->setProject($request->project);
 | 
			
		||||
            $ak = array('downloads_access_rights' => 'hasDownloadsAccess',
 | 
			
		||||
                        'wiki_access_rights' => 'hasWikiAccess',
 | 
			
		||||
                        'review_access_rights' => 'hasReviewAccess',
 | 
			
		||||
                        'source_access_rights' => 'hasSourceAccess',
 | 
			
		||||
                        'issues_access_rights' => 'hasIssuesAccess');
 | 
			
		||||
            $request->rights = array();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										56
									
								
								src/IDF/Migrations/8CodeReview.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/IDF/Migrations/8CodeReview.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
<?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 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 ***** */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add the code review.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
function IDF_Migrations_8CodeReview_up($params=null)
 | 
			
		||||
{
 | 
			
		||||
    $models = array(
 | 
			
		||||
                    'IDF_Review',
 | 
			
		||||
                    'IDF_Review_Patch',
 | 
			
		||||
                    'IDF_Review_FileComment',
 | 
			
		||||
                    );
 | 
			
		||||
    $db = Pluf::db();
 | 
			
		||||
    $schema = new Pluf_DB_Schema($db);
 | 
			
		||||
    foreach ($models as $model) {
 | 
			
		||||
        $schema->model = new $model();
 | 
			
		||||
        $schema->createTables();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function IDF_Migrations_8CodeReview_down($params=null)
 | 
			
		||||
{
 | 
			
		||||
    $models = array(
 | 
			
		||||
                    'IDF_Review_FileComment',
 | 
			
		||||
                    'IDF_Review_Patch',
 | 
			
		||||
                    'IDF_Review',
 | 
			
		||||
                    );
 | 
			
		||||
    $db = Pluf::db();
 | 
			
		||||
    $schema = new Pluf_DB_Schema($db);
 | 
			
		||||
    foreach ($models as $model) {
 | 
			
		||||
        $schema->model = new $model();
 | 
			
		||||
        $schema->dropTables();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -42,6 +42,9 @@ function IDF_Migrations_Install_setup($params=null)
 | 
			
		||||
                    'IDF_Timeline',
 | 
			
		||||
                    'IDF_WikiPage',
 | 
			
		||||
                    'IDF_WikiRevision',
 | 
			
		||||
                    'IDF_Review',
 | 
			
		||||
                    'IDF_Review_Patch',
 | 
			
		||||
                    'IDF_Review_FileComment',
 | 
			
		||||
                    );
 | 
			
		||||
    $db = Pluf::db();
 | 
			
		||||
    $schema = new Pluf_DB_Schema($db);
 | 
			
		||||
@@ -79,6 +82,9 @@ function IDF_Migrations_Install_teardown($params=null)
 | 
			
		||||
    $perm = Pluf_Permission::getFromString('IDF.project-authorized-user');
 | 
			
		||||
    if ($perm) $perm->delete();
 | 
			
		||||
    $models = array(
 | 
			
		||||
                    'IDF_Review_FileComment',
 | 
			
		||||
                    'IDF_Review_Patch',
 | 
			
		||||
                    'IDF_Review',
 | 
			
		||||
                    'IDF_WikiRevision',
 | 
			
		||||
                    'IDF_WikiPage',
 | 
			
		||||
                    'IDF_Timeline',
 | 
			
		||||
 
 | 
			
		||||
@@ -152,6 +152,15 @@ class IDF_Precondition
 | 
			
		||||
        return self::accessTabGeneric($request, 'wiki_access_rights');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static public function accessReview($request)
 | 
			
		||||
    {
 | 
			
		||||
        $res = self::baseAccess($request);
 | 
			
		||||
        if (true !== $res) {
 | 
			
		||||
            return $res;
 | 
			
		||||
        }
 | 
			
		||||
        return self::accessTabGeneric($request, 'review_access_rights');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
     /**
 | 
			
		||||
     * Based on the request, it is automatically setting the user.
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										168
									
								
								src/IDF/Review.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								src/IDF/Review.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,168 @@
 | 
			
		||||
<?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 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 ***** */
 | 
			
		||||
 | 
			
		||||
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
 | 
			
		||||
Pluf::loadFunction('Pluf_Template_dateAgo');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Base definition of a code review.
 | 
			
		||||
 *
 | 
			
		||||
 * A code review has a status, submitter, summary, description and is
 | 
			
		||||
 * associated to a project.
 | 
			
		||||
 *
 | 
			
		||||
 * The real content of the review is in the IDF_Review_Patch which
 | 
			
		||||
 * contains a given patch and associated comments from reviewers.
 | 
			
		||||
 */
 | 
			
		||||
class IDF_Review extends Pluf_Model
 | 
			
		||||
{
 | 
			
		||||
    public $_model = __CLASS__;
 | 
			
		||||
 | 
			
		||||
    function init()
 | 
			
		||||
    {
 | 
			
		||||
        $this->_a['table'] = 'idf_reviews';
 | 
			
		||||
        $this->_a['model'] = __CLASS__;
 | 
			
		||||
        $this->_a['cols'] = array(
 | 
			
		||||
                             // It is mandatory to have an "id" column.
 | 
			
		||||
                            'id' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Sequence',
 | 
			
		||||
                                  'blank' => true, 
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'project' => 
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Foreignkey',
 | 
			
		||||
                                  'model' => 'IDF_Project',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('project'),
 | 
			
		||||
                                  'relate_name' => 'reviews',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'summary' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Varchar',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'size' => 250,
 | 
			
		||||
                                  'verbose' => __('summary'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'submitter' => 
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Foreignkey',
 | 
			
		||||
                                  'model' => 'Pluf_User',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('submitter'),
 | 
			
		||||
                                  'relate_name' => 'submitted_review',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'reviewers' => 
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Manytomany',
 | 
			
		||||
                                  'model' => 'Pluf_User',
 | 
			
		||||
                                  'blank' => true,
 | 
			
		||||
                                  'help_text' => 'Reviewers will get an email notification when the review is changed.',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'tags' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Manytomany', 
 | 
			
		||||
                                  'blank' => true,
 | 
			
		||||
                                  'model' => 'IDF_Tag',
 | 
			
		||||
                                  'verbose' => __('labels'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'status' => 
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Foreignkey', 
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'model' => 'IDF_Tag',
 | 
			
		||||
                                  'verbose' => __('status'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'creation_dtime' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Datetime',
 | 
			
		||||
                                  'blank' => true,
 | 
			
		||||
                                  'verbose' => __('creation date'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'modif_dtime' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Datetime',
 | 
			
		||||
                                  'blank' => true,
 | 
			
		||||
                                  'verbose' => __('modification date'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            );
 | 
			
		||||
        $this->_a['idx'] = array(                           
 | 
			
		||||
                            'modif_dtime_idx' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'col' => 'modif_dtime',
 | 
			
		||||
                                  'type' => 'normal',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            );
 | 
			
		||||
        $table = $this->_con->pfx.'idf_review_idf_tag_assoc';
 | 
			
		||||
        $this->_a['views'] = array(
 | 
			
		||||
                              'join_tags' => 
 | 
			
		||||
                              array(
 | 
			
		||||
                                    'join' => 'LEFT JOIN '.$table
 | 
			
		||||
                                    .' ON idf_issue_id=id',
 | 
			
		||||
                                    ),
 | 
			
		||||
                                   );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function __toString()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->id.' - '.$this->summary;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _toIndex()
 | 
			
		||||
    {
 | 
			
		||||
        return '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function preDelete()
 | 
			
		||||
    {
 | 
			
		||||
        IDF_Timeline::remove($this);
 | 
			
		||||
        IDF_Search::remove($this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function preSave($create=false)
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->id == '') {
 | 
			
		||||
            $this->creation_dtime = gmdate('Y-m-d H:i:s');
 | 
			
		||||
        }
 | 
			
		||||
        $this->modif_dtime = gmdate('Y-m-d H:i:s');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function postSave($create=false)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns an HTML fragment used to display this review in the
 | 
			
		||||
     * timeline.
 | 
			
		||||
     *
 | 
			
		||||
     * The request object is given to be able to check the rights and
 | 
			
		||||
     * as such create links to other items etc. You can consider that
 | 
			
		||||
     * if displayed, you can create a link to it.
 | 
			
		||||
     *
 | 
			
		||||
     * @param Pluf_HTTP_Request 
 | 
			
		||||
     * @return Pluf_Template_SafeString
 | 
			
		||||
     */
 | 
			
		||||
    public function timelineFragment($request)
 | 
			
		||||
    {
 | 
			
		||||
        return '';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										113
									
								
								src/IDF/Review/FileComment.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/IDF/Review/FileComment.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
<?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 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 ***** */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A comment to a file affected by a patch.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class IDF_Review_FileComment extends Pluf_Model
 | 
			
		||||
{
 | 
			
		||||
    public $_model = __CLASS__;
 | 
			
		||||
 | 
			
		||||
    function init()
 | 
			
		||||
    {
 | 
			
		||||
        $this->_a['table'] = 'idf_review_filecomments';
 | 
			
		||||
        $this->_a['model'] = __CLASS__;
 | 
			
		||||
        $this->_a['cols'] = array(
 | 
			
		||||
                             // It is mandatory to have an "id" column.
 | 
			
		||||
                            'id' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Sequence',
 | 
			
		||||
                                  'blank' => true, 
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'patch' => 
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Foreignkey',
 | 
			
		||||
                                  'model' => 'IDF_Review_Patch',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('patch'),
 | 
			
		||||
                                  'relate_name' => 'filecomments',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'cfile' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Varchar',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'size' => 250,
 | 
			
		||||
                                  'help_text' => 'The changed file, for example src/foo/bar.txt, this is the path to access it in the repository.',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'content' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Text',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('comment'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'submitter' => 
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Foreignkey',
 | 
			
		||||
                                  'model' => 'Pluf_User',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('submitter'),
 | 
			
		||||
                                  'relate_name' => 'commented_patched_files',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'creation_dtime' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Datetime',
 | 
			
		||||
                                  'blank' => true,
 | 
			
		||||
                                  'verbose' => __('creation date'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            );
 | 
			
		||||
        $this->_a['idx'] = array(                           
 | 
			
		||||
                            'creation_dtime_idx' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'col' => 'creation_dtime',
 | 
			
		||||
                                  'type' => 'normal',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _toIndex()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->cfile.' '.$this->content;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function preDelete()
 | 
			
		||||
    {
 | 
			
		||||
        IDF_Timeline::remove($this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function preSave($create=false)
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->id == '') {
 | 
			
		||||
            $this->creation_dtime = gmdate('Y-m-d H:i:s');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function postSave($create=false)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function timelineFragment($request)
 | 
			
		||||
    {
 | 
			
		||||
        return '';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										118
									
								
								src/IDF/Review/Patch.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/IDF/Review/Patch.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
<?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 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 ***** */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A patch to be reviewed.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class IDF_Review_Patch extends Pluf_Model
 | 
			
		||||
{
 | 
			
		||||
    public $_model = __CLASS__;
 | 
			
		||||
 | 
			
		||||
    function init()
 | 
			
		||||
    {
 | 
			
		||||
        $this->_a['table'] = 'idf_review_patches';
 | 
			
		||||
        $this->_a['model'] = __CLASS__;
 | 
			
		||||
        $this->_a['cols'] = array(
 | 
			
		||||
                             // It is mandatory to have an "id" column.
 | 
			
		||||
                            'id' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Sequence',
 | 
			
		||||
                                  'blank' => true, 
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'review' => 
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Foreignkey',
 | 
			
		||||
                                  'model' => 'IDF_Review',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('review'),
 | 
			
		||||
                                  'relate_name' => 'patches',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'summary' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Varchar',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'size' => 250,
 | 
			
		||||
                                  'verbose' => __('summary'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'commit' => 
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Foreignkey',
 | 
			
		||||
                                  'model' => 'IDF_Commit',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('commit'),
 | 
			
		||||
                                  'relate_name' => 'patches',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'description' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Text',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('description'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'patch' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_File',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('patch'),
 | 
			
		||||
                                  'help_text' => 'The patch is stored at the same place as the issue attachments with the same approach for the name.',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'creation_dtime' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Datetime',
 | 
			
		||||
                                  'blank' => true,
 | 
			
		||||
                                  'verbose' => __('creation date'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            );
 | 
			
		||||
        $this->_a['idx'] = array(                           
 | 
			
		||||
                            'creation_dtime_idx' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'col' => 'creation_dtime',
 | 
			
		||||
                                  'type' => 'normal',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _toIndex()
 | 
			
		||||
    {
 | 
			
		||||
        return '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function preDelete()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function preSave($create=false)
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->id == '') {
 | 
			
		||||
            $this->creation_dtime = gmdate('Y-m-d H:i:s');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function postSave($create=false)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function timelineFragment($request)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -55,7 +55,7 @@ class IDF_Scm
 | 
			
		||||
        $cache = Pluf_Cache::factory();
 | 
			
		||||
        if (null === ($res=$cache->get($key))) {
 | 
			
		||||
            $ll = exec($command, $output, $return);
 | 
			
		||||
            if ($return != 0 and Pluf::f('debug', false)) {
 | 
			
		||||
            if ($return != 0 and Pluf::f('debug_scm', false)) {
 | 
			
		||||
                throw new IDF_Scm_Exception(sprintf('Error when running command: "%s", return code: %d', $command, $return));
 | 
			
		||||
            }
 | 
			
		||||
            $cache->set($key, array($ll, $return, $output));
 | 
			
		||||
 
 | 
			
		||||
@@ -54,4 +54,21 @@ class IDF_Tests_TestDiff extends UnitTestCase
 | 
			
		||||
            $i++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testBinaryDiff()
 | 
			
		||||
    {
 | 
			
		||||
        $diff_content = file_get_contents(dirname(__FILE__).'/test-diff.diff');
 | 
			
		||||
        $orig = file_get_contents(dirname(__FILE__).'/test-diff-view.html');
 | 
			
		||||
        $diff = new IDF_Diff($diff_content);
 | 
			
		||||
        $diff->parse();
 | 
			
		||||
        $def = $diff->files['src/IDF/templates/idf/issues/view.html'];
 | 
			
		||||
 | 
			
		||||
        $orig_lines = preg_split("/\015\012|\015|\012/", $orig);
 | 
			
		||||
        $merged = $diff->mergeChunks($orig_lines, $def, 10);
 | 
			
		||||
        $lchunk = end($merged);
 | 
			
		||||
        $lline = end($lchunk);
 | 
			
		||||
        $this->assertEqual(array('', '166', '{/if}{/block}'),
 | 
			
		||||
                           $lline);
 | 
			
		||||
        //print_r($diff->mergeChunks($orig_lines, $def, 10));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										125
									
								
								src/IDF/Tests/test-diff-view.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/IDF/Tests/test-diff-view.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,125 @@
 | 
			
		||||
{extends "idf/issues/base.html"}
 | 
			
		||||
{block titleicon}{if $form}<form class="star" method="post" action="{url 'IDF_Views_Issue::star', array($project.shortname, $issue.id)}"><input type="image" src="{if $starred}{media '/idf/img/star.png'}{else}{media '/idf/img/star-grey.png'}{/if}" name="submit" /></form> {/if}{/block}
 | 
			
		||||
{block body}
 | 
			
		||||
{assign $i = 0} 
 | 
			
		||||
{assign $nc = $comments.count()} 
 | 
			
		||||
{foreach $comments as $c}
 | 
			
		||||
<div class="issue-comment{if $i == 0} issue-comment-first{/if}{if $i == ($nc-1)} issue-comment-last{/if}" id="ic{$c.id}">{assign $who = $c.get_submitter()}{aurl 'whourl', 'IDF_Views_User::view', array($who.login)}
 | 
			
		||||
{if $i == 0}
 | 
			
		||||
<p>{blocktrans}Reported by <a href="{$whourl}">{$who}</a>, {$c.creation_dtime|date}{/blocktrans}</p>
 | 
			
		||||
{else}
 | 
			
		||||
{aurl 'url', 'IDF_Views_Issue::view', array($project.shortname, $issue.id)}
 | 
			
		||||
{assign $id = $c.id}
 | 
			
		||||
{assign $url = $url~'#ic'~$c.id}
 | 
			
		||||
<p>{blocktrans}Comment <a href="{$url}">{$i}</a> by <a href="{$whourl}">{$who}</a>, {$c.creation_dtime|date}{/blocktrans}</p>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
<pre class="issue-comment-text">{if strlen($c.content) > 0}{issuetext $c.content, $request}{else}<i>{trans '(No comments were given for this change.)'}</i>{/if}</pre>
 | 
			
		||||
{assign $attachments = $c.get_attachment_list()}
 | 
			
		||||
{if $attachments.count() > 0}
 | 
			
		||||
<hr align="left" class="attach" />
 | 
			
		||||
<ul>
 | 
			
		||||
{foreach $attachments as $a}<li><a href="{url 'IDF_Views_Issue::getAttachment', array($project.shortname, $a.id, $a.filename)}">{$a.filename}</a> - {$a.filesize|size}</li>{/foreach}
 | 
			
		||||
</ul>{/if}
 | 
			
		||||
{if $i> 0 and $c.changedIssue()}
 | 
			
		||||
<div class="issue-changes"> 
 | 
			
		||||
{foreach $c.changes as $w => $v}
 | 
			
		||||
<strong>{if $w == 'su'}{trans 'Summary:'}{/if}{if $w == 'st'}{trans 'Status:'}{/if}{if $w == 'ow'}{trans 'Owner:'}{/if}{if $w == 'lb'}{trans 'Labels:'}{/if}</strong> {if $w == 'lb'}{assign $l = implode(', ', $v)}{$l}{else}{$v}{/if}<br />
 | 
			
		||||
{/foreach}
 | 
			
		||||
</div> 
 | 
			
		||||
{/if}
 | 
			
		||||
</div>{assign $i = $i + 1}{if $i == $nc and false == $form}
 | 
			
		||||
<div class="issue-comment-signin">
 | 
			
		||||
{aurl 'url', 'IDF_Views::login'}{blocktrans}<a href="{$url}">Sign in</a> to reply to this comment.{/blocktrans}
 | 
			
		||||
</div>
 | 
			
		||||
{/if}
 | 
			
		||||
{/foreach}
 | 
			
		||||
 | 
			
		||||
{if $form}
 | 
			
		||||
<hr />
 | 
			
		||||
 | 
			
		||||
{if $form.errors}
 | 
			
		||||
<div class="px-message-error">
 | 
			
		||||
<p>{trans 'The form contains some errors. Please correct them to change the issue.'}</p>
 | 
			
		||||
{if $form.get_top_errors}
 | 
			
		||||
{$form.render_top_errors|unsafe}
 | 
			
		||||
{/if}
 | 
			
		||||
</div>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
{if $closed and (!$isOwner and !$isMember)}
 | 
			
		||||
<p><img src="{media '/idf/img/warning.png'}" style="vertical-align: text-bottom;" alt=" " /> {blocktrans}This issue is marked as closed, add a comment only if you think this issue is still valid and more work is needed to fully fix it.{/blocktrans}</p>
 | 
			
		||||
 | 
			
		||||
{/if}
 | 
			
		||||
<form method="post" enctype="multipart/form-data" action="{url 'IDF_Views_Issue::view', array($project.shortname, $issue.id)}" >
 | 
			
		||||
<table class="form" summary="">
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{$form.f.content.labelTag}:</strong></th>
 | 
			
		||||
<td>{if $form.f.content.errors}{$form.f.content.fieldErrors}{/if}
 | 
			
		||||
{$form.f.content|unsafe}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th>{$form.f.attachment.labelTag}:</th>
 | 
			
		||||
<td>{if $form.f.attachment.errors}{$form.f.attachment.fieldErrors}{/if}
 | 
			
		||||
{$form.f.attachment|unsafe}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>{if $isOwner or $isMember}
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{$form.f.summary.labelTag}:</strong></th>
 | 
			
		||||
<td>{if $form.f.summary.errors}{$form.f.summary.fieldErrors}{/if}
 | 
			
		||||
{$form.f.summary|unsafe}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{$form.f.status.labelTag}:</strong></th>
 | 
			
		||||
<td>{if $form.f.status.errors}{$form.f.status.fieldErrors}{/if}
 | 
			
		||||
{$form.f.status|unsafe}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th>{$form.f.owner.labelTag}:</th>
 | 
			
		||||
<td>{if $form.f.owner.errors}{$form.f.owner.fieldErrors}{/if}
 | 
			
		||||
{$form.f.owner|unsafe}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th>{$form.f.label1.labelTag}:</th>
 | 
			
		||||
<td>
 | 
			
		||||
{if $form.f.label1.errors}{$form.f.label1.fieldErrors}{/if}{$form.f.label1|unsafe}
 | 
			
		||||
{if $form.f.label2.errors}{$form.f.label2.fieldErrors}{/if}{$form.f.label2|unsafe}
 | 
			
		||||
{if $form.f.label3.errors}{$form.f.label3.fieldErrors}{/if}{$form.f.label3|unsafe}<br />
 | 
			
		||||
{if $form.f.label4.errors}{$form.f.label4.fieldErrors}{/if}{$form.f.label4|unsafe}
 | 
			
		||||
{if $form.f.label5.errors}{$form.f.label5.fieldErrors}{/if}{$form.f.label5|unsafe}
 | 
			
		||||
{if $form.f.label6.errors}{$form.f.label6.fieldErrors}{/if}{$form.f.label6|unsafe}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>{/if}
 | 
			
		||||
<tr>
 | 
			
		||||
<td> </td>
 | 
			
		||||
<td><input type="submit" value="{trans 'Submit Changes'}" name="submit" /> | <a href="{url 'IDF_Views_Issue::view', array($project.shortname, $issue.id)}">{trans 'Cancel'}</a>
 | 
			
		||||
</td>
 | 
			
		||||
</tr>
 | 
			
		||||
</table>
 | 
			
		||||
</form>
 | 
			
		||||
{/if}
 | 
			
		||||
{/block}
 | 
			
		||||
{block context}
 | 
			
		||||
<div class="issue-info">
 | 
			
		||||
{assign $submitter = $issue.get_submitter()}{aurl 'url', 'IDF_Views_User::view', array($submitter.login)}
 | 
			
		||||
<p><strong>{trans 'Created:'}</strong> <span class="nobrk">{$issue.creation_dtime|dateago}</span> <span class="nobrk">{blocktrans}by <a href="{$url}">{$submitter}</a>{/blocktrans}</span></p>
 | 
			
		||||
{if $issue.modif_dtime != $issue.creation_dtime}<p>
 | 
			
		||||
<strong>{trans 'Updated:'}</strong> <span class="nobrk">{$issue.modif_dtime|dateago}</span></p>{/if}
 | 
			
		||||
<p>
 | 
			
		||||
<strong>{trans 'Status:'}</strong> {$issue.get_status.name}</p>
 | 
			
		||||
{if $issue.get_owner != null}<p>{aurl 'url', 'IDF_Views_User::view', array($issue.get_owner().login)}
 | 
			
		||||
<strong>{trans 'Owner:'}</strong> <a href="{$url}">{$issue.get_owner}</a>
 | 
			
		||||
</p>{/if}{assign $tags = $issue.get_tags_list()}{if $tags.count()}
 | 
			
		||||
<p>
 | 
			
		||||
<strong>{trans 'Labels:'}</strong><br />
 | 
			
		||||
{foreach $tags as $tag}{aurl 'url', 'IDF_Views_Issue::listLabel', array($project.shortname, $tag.id, 'open')}
 | 
			
		||||
<span class="label"><a href="{$url}" class="label"><strong>{$tag.class}:</strong>{$tag.name}</a></span><br />
 | 
			
		||||
{/foreach}
 | 
			
		||||
</p>{/if}
 | 
			
		||||
</div>
 | 
			
		||||
{/block}
 | 
			
		||||
{block javascript}{if $form}{include 'idf/issues/js-autocomplete.html'}{/if}{/block}
 | 
			
		||||
							
								
								
									
										515
									
								
								src/IDF/Tests/test-diff.diff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										515
									
								
								src/IDF/Tests/test-diff.diff
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,515 @@
 | 
			
		||||
diff --git a/src/IDF/Form/IssueCreate.php b/src/IDF/Form/IssueCreate.php
 | 
			
		||||
index 0743e72..67afca7 100644
 | 
			
		||||
--- a/src/IDF/Form/IssueCreate.php
 | 
			
		||||
+++ b/src/IDF/Form/IssueCreate.php
 | 
			
		||||
@@ -72,8 +72,9 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
         // We add .dummy to try to mitigate security issues in the
 | 
			
		||||
         // case of someone allowing the upload path to be accessible
 | 
			
		||||
         // to everybody.
 | 
			
		||||
-        $filename = substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/'.substr($md5, 4).'/%s.dummy';
 | 
			
		||||
-        $this->fields['attachment'] = new Pluf_Form_Field_File(
 | 
			
		||||
+        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' =>
 | 
			
		||||
@@ -83,6 +84,7 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
                             )
 | 
			
		||||
                       )
 | 
			
		||||
                 );
 | 
			
		||||
+        }
 | 
			
		||||
 | 
			
		||||
         if ($this->show_full) {
 | 
			
		||||
             $this->fields['status'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
@@ -195,6 +197,21 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     /**
 | 
			
		||||
+     * 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]);
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    /**
 | 
			
		||||
      * Save the model in the database.
 | 
			
		||||
      *
 | 
			
		||||
      * @param bool Commit in the database or not. If not, the object
 | 
			
		||||
@@ -203,61 +220,63 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
      */
 | 
			
		||||
     function save($commit=true)
 | 
			
		||||
     {
 | 
			
		||||
-        if ($this->isValid()) {
 | 
			
		||||
-            // Add a tag for each label
 | 
			
		||||
-            $tags = array();
 | 
			
		||||
-            if ($this->show_full) {
 | 
			
		||||
-                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[] = IDF_Tag::add($name, $this->project, $class);
 | 
			
		||||
+        if (!$this->isValid()) {
 | 
			
		||||
+            throw new Exception(__('Cannot save the model from an invalid form.'));
 | 
			
		||||
+        }
 | 
			
		||||
+        // Add a tag for each label
 | 
			
		||||
+        $tags = array();
 | 
			
		||||
+        if ($this->show_full) {
 | 
			
		||||
+            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[] = IDF_Tag::add($name, $this->project, $class);
 | 
			
		||||
                 }
 | 
			
		||||
-            } else {
 | 
			
		||||
-                $tags[] = IDF_Tag::add('Medium', $this->project, 'Priority');
 | 
			
		||||
-                $tags[] = IDF_Tag::add('Defect', $this->project, 'Type');
 | 
			
		||||
-            }
 | 
			
		||||
-            // Create the issue
 | 
			
		||||
-            $issue = new IDF_Issue();
 | 
			
		||||
-            $issue->project = $this->project;
 | 
			
		||||
-            $issue->submitter = $this->user;
 | 
			
		||||
-            if ($this->show_full) {
 | 
			
		||||
-                $issue->status = IDF_Tag::add(trim($this->cleaned_data['status']), $this->project, 'Status');
 | 
			
		||||
-                $issue->owner = self::findUser($this->cleaned_data['owner']);
 | 
			
		||||
-            } else {
 | 
			
		||||
-                $_t = $this->project->getTagIdsByStatus('open');
 | 
			
		||||
-                $issue->status = new IDF_Tag($_t[0]); // first one is the default
 | 
			
		||||
-                $issue->owner = null;
 | 
			
		||||
-            }
 | 
			
		||||
-            $issue->summary = trim($this->cleaned_data['summary']);
 | 
			
		||||
-            $issue->create();
 | 
			
		||||
-            foreach ($tags as $tag) {
 | 
			
		||||
-                $issue->setAssoc($tag);
 | 
			
		||||
             }
 | 
			
		||||
-            // add the first comment
 | 
			
		||||
-            $comment = new IDF_IssueComment();
 | 
			
		||||
-            $comment->issue = $issue;
 | 
			
		||||
-            $comment->content = $this->cleaned_data['content'];
 | 
			
		||||
-            $comment->submitter = $this->user;
 | 
			
		||||
-            $comment->create();
 | 
			
		||||
-            // If we have a file, create the IDF_IssueFile and attach
 | 
			
		||||
-            // it to the comment.
 | 
			
		||||
-            if ($this->cleaned_data['attachment']) {
 | 
			
		||||
+        } else {
 | 
			
		||||
+            $tags[] = IDF_Tag::add('Medium', $this->project, 'Priority');
 | 
			
		||||
+            $tags[] = IDF_Tag::add('Defect', $this->project, 'Type');
 | 
			
		||||
+        }
 | 
			
		||||
+        // Create the issue
 | 
			
		||||
+        $issue = new IDF_Issue();
 | 
			
		||||
+        $issue->project = $this->project;
 | 
			
		||||
+        $issue->submitter = $this->user;
 | 
			
		||||
+        if ($this->show_full) {
 | 
			
		||||
+            $issue->status = IDF_Tag::add(trim($this->cleaned_data['status']), $this->project, 'Status');
 | 
			
		||||
+            $issue->owner = self::findUser($this->cleaned_data['owner']);
 | 
			
		||||
+        } else {
 | 
			
		||||
+            $_t = $this->project->getTagIdsByStatus('open');
 | 
			
		||||
+            $issue->status = new IDF_Tag($_t[0]); // first one is the default
 | 
			
		||||
+            $issue->owner = null;
 | 
			
		||||
+        }
 | 
			
		||||
+        $issue->summary = trim($this->cleaned_data['summary']);
 | 
			
		||||
+        $issue->create();
 | 
			
		||||
+        foreach ($tags as $tag) {
 | 
			
		||||
+            $issue->setAssoc($tag);
 | 
			
		||||
+        }
 | 
			
		||||
+        // add the first comment
 | 
			
		||||
+        $comment = new IDF_IssueComment();
 | 
			
		||||
+        $comment->issue = $issue;
 | 
			
		||||
+        $comment->content = $this->cleaned_data['content'];
 | 
			
		||||
+        $comment->submitter = $this->user;
 | 
			
		||||
+        $comment->create();
 | 
			
		||||
+        // If we have a file, create the IDF_IssueFile and attach
 | 
			
		||||
+        // it to the comment.
 | 
			
		||||
+        for ($i=1;$i<4;$i++) {
 | 
			
		||||
+            if ($this->cleaned_data['attachment'.$i]) {
 | 
			
		||||
                 $file = new IDF_IssueFile();
 | 
			
		||||
-                $file->attachment = $this->cleaned_data['attachment'];
 | 
			
		||||
+                $file->attachment = $this->cleaned_data['attachment'.$i];
 | 
			
		||||
                 $file->submitter = $this->user;
 | 
			
		||||
                 $file->comment = $comment;
 | 
			
		||||
                 $file->create();
 | 
			
		||||
             }
 | 
			
		||||
-            return $issue;
 | 
			
		||||
         }
 | 
			
		||||
-        throw new Exception(__('Cannot save the model from an invalid form.'));
 | 
			
		||||
+        return $issue;
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     /**
 | 
			
		||||
diff --git a/src/IDF/Form/IssueUpdate.php b/src/IDF/Form/IssueUpdate.php
 | 
			
		||||
index 550889e..0d36e72 100644
 | 
			
		||||
--- a/src/IDF/Form/IssueUpdate.php
 | 
			
		||||
+++ b/src/IDF/Form/IssueUpdate.php
 | 
			
		||||
@@ -68,8 +68,9 @@ class IDF_Form_IssueUpdate  extends IDF_Form_IssueCreate
 | 
			
		||||
         // We add .dummy to try to mitigate security issues in the
 | 
			
		||||
         // case of someone allowing the upload path to be accessible
 | 
			
		||||
         // to everybody.
 | 
			
		||||
-        $filename = substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/'.substr($md5, 4).'/%s.dummy';
 | 
			
		||||
-        $this->fields['attachment'] = new Pluf_Form_Field_File(
 | 
			
		||||
+        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' =>
 | 
			
		||||
@@ -79,6 +80,7 @@ class IDF_Form_IssueUpdate  extends IDF_Form_IssueCreate
 | 
			
		||||
                             )
 | 
			
		||||
                       )
 | 
			
		||||
                 );
 | 
			
		||||
+        }
 | 
			
		||||
 | 
			
		||||
         if ($this->show_full) {
 | 
			
		||||
             $this->fields['status'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
@@ -124,6 +126,21 @@ class IDF_Form_IssueUpdate  extends IDF_Form_IssueCreate
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     /**
 | 
			
		||||
+     * 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]);
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    /**
 | 
			
		||||
      * We check that something is really changed.
 | 
			
		||||
      */
 | 
			
		||||
     public function clean()
 | 
			
		||||
@@ -202,90 +219,92 @@ class IDF_Form_IssueUpdate  extends IDF_Form_IssueCreate
 | 
			
		||||
      */
 | 
			
		||||
     function save($commit=true)
 | 
			
		||||
     {
 | 
			
		||||
-        if ($this->isValid()) {
 | 
			
		||||
-            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;
 | 
			
		||||
+        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 ($tag->class != 'Other') {
 | 
			
		||||
-                            $changes['lb'][] = (string) $tag; //new tag
 | 
			
		||||
-                        } else {
 | 
			
		||||
-                            $changes['lb'][] = (string) $tag->name;
 | 
			
		||||
-                        }
 | 
			
		||||
+            }
 | 
			
		||||
+            // 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 ($tag->class != 'Other') {
 | 
			
		||||
+                        $changes['lb'][] = (string) $tag; //new tag
 | 
			
		||||
+                    } else {
 | 
			
		||||
+                        $changes['lb'][] = (string) $tag->name;
 | 
			
		||||
                     }
 | 
			
		||||
                 }
 | 
			
		||||
-                foreach ($oldtags as $tag) {
 | 
			
		||||
-                    if (!Pluf_Model_InArray($tag, $tags)) {
 | 
			
		||||
-                        if (!isset($changes['lb'])) $changes['lb'] = array();
 | 
			
		||||
-                        if ($tag->class != 'Other') {
 | 
			
		||||
-                            $changes['lb'][] = '-'.(string) $tag; //new tag
 | 
			
		||||
-                        } else {
 | 
			
		||||
-                            $changes['lb'][] = '-'.(string) $tag->name;
 | 
			
		||||
-                        }
 | 
			
		||||
+            }
 | 
			
		||||
+            foreach ($oldtags as $tag) {
 | 
			
		||||
+                if (!Pluf_Model_InArray($tag, $tags)) {
 | 
			
		||||
+                    if (!isset($changes['lb'])) $changes['lb'] = array();
 | 
			
		||||
+                    if ($tag->class != 'Other') {
 | 
			
		||||
+                        $changes['lb'][] = '-'.(string) $tag; //new tag
 | 
			
		||||
+                    } else {
 | 
			
		||||
+                        $changes['lb'][] = '-'.(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;
 | 
			
		||||
-                }
 | 
			
		||||
-                // 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.
 | 
			
		||||
+            // 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 ($this->cleaned_data['attachment']) {
 | 
			
		||||
+            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;
 | 
			
		||||
+            }
 | 
			
		||||
+            // 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.
 | 
			
		||||
+        }
 | 
			
		||||
+        for ($i=1;$i<4;$i++) {
 | 
			
		||||
+            if ($this->cleaned_data['attachment'.$i]) {
 | 
			
		||||
                 $file = new IDF_IssueFile();
 | 
			
		||||
-                $file->attachment = $this->cleaned_data['attachment'];
 | 
			
		||||
+                $file->attachment = $this->cleaned_data['attachment'.$i];
 | 
			
		||||
                 $file->submitter = $this->user;
 | 
			
		||||
                 $file->comment = $comment;
 | 
			
		||||
                 $file->create();
 | 
			
		||||
             }
 | 
			
		||||
-            return $this->issue;
 | 
			
		||||
         }
 | 
			
		||||
-        throw new Exception(__('Cannot save the model from an invalid form.'));
 | 
			
		||||
+        return $this->issue;
 | 
			
		||||
     }
 | 
			
		||||
 }
 | 
			
		||||
diff --git a/src/IDF/IssueFile.php b/src/IDF/IssueFile.php
 | 
			
		||||
index f4367dd..f0745e8 100644
 | 
			
		||||
--- a/src/IDF/IssueFile.php
 | 
			
		||||
+++ b/src/IDF/IssueFile.php
 | 
			
		||||
@@ -114,6 +114,7 @@ class IDF_IssueFile extends Pluf_Model
 | 
			
		||||
             $this->filename = substr(basename($file), 0, -6);
 | 
			
		||||
             $img_extensions = array('jpeg', 'jpg', 'png', 'gif');
 | 
			
		||||
             $info = pathinfo($this->filename);
 | 
			
		||||
+            if (!isset($info['extension'])) $info['extension'] = '';
 | 
			
		||||
             if (in_array(strtolower($info['extension']), $img_extensions)) {
 | 
			
		||||
                 $this->type = 'img';
 | 
			
		||||
             } else {
 | 
			
		||||
diff --git a/src/IDF/templates/idf/issues/create.html b/src/IDF/templates/idf/issues/create.html
 | 
			
		||||
index e8f4a5b..faaa743 100644
 | 
			
		||||
--- a/src/IDF/templates/idf/issues/create.html
 | 
			
		||||
+++ b/src/IDF/templates/idf/issues/create.html
 | 
			
		||||
@@ -24,10 +24,22 @@
 | 
			
		||||
 {$form.f.content|unsafe}
 | 
			
		||||
 </td>
 | 
			
		||||
 </tr>
 | 
			
		||||
-<tr>
 | 
			
		||||
-<th>{$form.f.attachment.labelTag}:</th>
 | 
			
		||||
-<td>{if $form.f.attachment.errors}{$form.f.attachment.fieldErrors}{/if}
 | 
			
		||||
-{$form.f.attachment|unsafe}
 | 
			
		||||
+<tr id="form-attachment-1">
 | 
			
		||||
+<th>{$form.f.attachment1.labelTag}:</th>
 | 
			
		||||
+<td>{if $form.f.attachment1.errors}{$form.f.attachment1.fieldErrors}{/if}
 | 
			
		||||
+{$form.f.attachment1|unsafe}
 | 
			
		||||
+</td>
 | 
			
		||||
+</tr>
 | 
			
		||||
+<tr id="form-attachment-2">
 | 
			
		||||
+<th>{$form.f.attachment2.labelTag}:</th>
 | 
			
		||||
+<td>{if $form.f.attachment2.errors}{$form.f.attachment2.fieldErrors}{/if}
 | 
			
		||||
+{$form.f.attachment2|unsafe}
 | 
			
		||||
+</td>
 | 
			
		||||
+</tr>
 | 
			
		||||
+<tr id="form-attachment-3">
 | 
			
		||||
+<th>{$form.f.attachment3.labelTag}:</th>
 | 
			
		||||
+<td>{if $form.f.attachment3.errors}{$form.f.attachment3.fieldErrors}{/if}
 | 
			
		||||
+{$form.f.attachment3|unsafe}
 | 
			
		||||
 </td>
 | 
			
		||||
 </tr>{if $isOwner or $isMember}
 | 
			
		||||
 <tr>
 | 
			
		||||
@@ -74,7 +86,34 @@
 | 
			
		||||
 {/block}
 | 
			
		||||
 {block javascript}
 | 
			
		||||
 <script type="text/javascript">
 | 
			
		||||
-document.getElementById('id_summary').focus()
 | 
			
		||||
+document.getElementById('id_summary').focus();{literal}
 | 
			
		||||
+$(document).ready(function(){
 | 
			
		||||
+
 | 
			
		||||
+    // Hide the upload forms, we insert before the first attach file
 | 
			
		||||
+    // row an "Attach File" little link.
 | 
			
		||||
+    // We hide all the rows.
 | 
			
		||||
+    $("#form-attachment-1").before("{/literal}<tr id=\"form-block-0\"><td> </td><td><img style=\"vertical-align: text-bottom;\" src=\"{media '/idf/img/attachment.png'}\" alt=\" \" align=\"bottom\" /><a id=\"form-show-0\" href=\"#\">{trans 'Attach file'}{literal}</a></td></tr>");
 | 
			
		||||
+    $("#form-show-0").click(function(){
 | 
			
		||||
+        $("#form-attachment-1").show();
 | 
			
		||||
+        $("#form-block-0").hide();
 | 
			
		||||
+    });
 | 
			
		||||
+    $("#form-attachment-1 td").append("<span id=\"form-block-1\"><a id=\"form-show-1\" href=\"#\">{/literal}{trans 'Attach another file'}{literal}</a></span>");
 | 
			
		||||
+    $("#form-show-1").click(function(){
 | 
			
		||||
+            $("#form-attachment-2").show();
 | 
			
		||||
+            $("#form-block-1").hide();
 | 
			
		||||
+        });
 | 
			
		||||
+    $("#form-attachment-2 td").append("<span id=\"form-block-2\"><a id=\"form-show-2\" href=\"#\">{/literal}{trans 'Attach another file'}{literal}</a></span>");
 | 
			
		||||
+    $("#form-show-2").click(function(){
 | 
			
		||||
+            $("#form-attachment-3").show();
 | 
			
		||||
+            $("#form-block-2").hide();
 | 
			
		||||
+        });
 | 
			
		||||
+    var j=0;
 | 
			
		||||
+    for (j=1;j<4;j=j+1) {
 | 
			
		||||
+        $("#form-attachment-"+j).hide();
 | 
			
		||||
+    }
 | 
			
		||||
+    });
 | 
			
		||||
 </script>
 | 
			
		||||
+{/literal}{/block}
 | 
			
		||||
+
 | 
			
		||||
 {include 'idf/issues/js-autocomplete.html'}{/block}
 | 
			
		||||
 | 
			
		||||
diff --git a/src/IDF/templates/idf/issues/view.html b/src/IDF/templates/idf/issues/view.html
 | 
			
		||||
index cb9e085..ad56d05 100644
 | 
			
		||||
--- a/src/IDF/templates/idf/issues/view.html
 | 
			
		||||
+++ b/src/IDF/templates/idf/issues/view.html
 | 
			
		||||
@@ -59,10 +59,22 @@
 | 
			
		||||
 {$form.f.content|unsafe}
 | 
			
		||||
 </td>
 | 
			
		||||
 </tr>
 | 
			
		||||
-<tr>
 | 
			
		||||
-<th>{$form.f.attachment.labelTag}:</th>
 | 
			
		||||
-<td>{if $form.f.attachment.errors}{$form.f.attachment.fieldErrors}{/if}
 | 
			
		||||
-{$form.f.attachment|unsafe}
 | 
			
		||||
+<tr id="form-attachment-1">
 | 
			
		||||
+<th>{$form.f.attachment1.labelTag}:</th>
 | 
			
		||||
+<td>{if $form.f.attachment1.errors}{$form.f.attachment1.fieldErrors}{/if}
 | 
			
		||||
+{$form.f.attachment1|unsafe}
 | 
			
		||||
+</td>
 | 
			
		||||
+</tr>
 | 
			
		||||
+<tr id="form-attachment-2">
 | 
			
		||||
+<th>{$form.f.attachment2.labelTag}:</th>
 | 
			
		||||
+<td>{if $form.f.attachment2.errors}{$form.f.attachment2.fieldErrors}{/if}
 | 
			
		||||
+{$form.f.attachment2|unsafe}
 | 
			
		||||
+</td>
 | 
			
		||||
+</tr>
 | 
			
		||||
+<tr id="form-attachment-3">
 | 
			
		||||
+<th>{$form.f.attachment3.labelTag}:</th>
 | 
			
		||||
+<td>{if $form.f.attachment3.errors}{$form.f.attachment3.fieldErrors}{/if}
 | 
			
		||||
+{$form.f.attachment3|unsafe}
 | 
			
		||||
 </td>
 | 
			
		||||
 </tr>{if $isOwner or $isMember}
 | 
			
		||||
 <tr>
 | 
			
		||||
@@ -122,4 +134,33 @@
 | 
			
		||||
 </p>{/if}
 | 
			
		||||
 </div>
 | 
			
		||||
 {/block}
 | 
			
		||||
-{block javascript}{if $form}{include 'idf/issues/js-autocomplete.html'}{/if}{/block}
 | 
			
		||||
+{block javascript}{if $form}{include 'idf/issues/js-autocomplete.html'}
 | 
			
		||||
+<script type="text/javascript">
 | 
			
		||||
+{literal}
 | 
			
		||||
+$(document).ready(function(){
 | 
			
		||||
+
 | 
			
		||||
+    // Hide the upload forms, we insert before the first attach file
 | 
			
		||||
+    // row an "Attach File" little link.
 | 
			
		||||
+    // We hide all the rows.
 | 
			
		||||
+    $("#form-attachment-1").before("{/literal}<tr id=\"form-block-0\"><td> </td><td><img style=\"vertical-align: text-bottom;\" src=\"{media '/idf/img/attachment.png'}\" alt=\" \" align=\"bottom\" /><a id=\"form-show-0\" href=\"#\">{trans 'Attach file'}{literal}</a></td></tr>");
 | 
			
		||||
+    $("#form-show-0").click(function(){
 | 
			
		||||
+        $("#form-attachment-1").show();
 | 
			
		||||
+        $("#form-block-0").hide();
 | 
			
		||||
+    });
 | 
			
		||||
+    $("#form-attachment-1 td").append("<span id=\"form-block-1\"><a id=\"form-show-1\" href=\"#\">{/literal}{trans 'Attach another file'}{literal}</a></span>");
 | 
			
		||||
+    $("#form-show-1").click(function(){
 | 
			
		||||
+            $("#form-attachment-2").show();
 | 
			
		||||
+            $("#form-block-1").hide();
 | 
			
		||||
+        });
 | 
			
		||||
+    $("#form-attachment-2 td").append("<span id=\"form-block-2\"><a id=\"form-show-2\" href=\"#\">{/literal}{trans 'Attach another file'}{literal}</a></span>");
 | 
			
		||||
+    $("#form-show-2").click(function(){
 | 
			
		||||
+            $("#form-attachment-3").show();
 | 
			
		||||
+            $("#form-block-2").hide();
 | 
			
		||||
+        });
 | 
			
		||||
+    var j=0;
 | 
			
		||||
+    for (j=1;j<4;j=j+1) {
 | 
			
		||||
+        $("#form-attachment-"+j).hide();
 | 
			
		||||
+    }
 | 
			
		||||
+    });{/literal}
 | 
			
		||||
+</script>
 | 
			
		||||
+{/if}{/block}
 | 
			
		||||
diff --git a/www/media/idf/img/attachment.png b/www/media/idf/img/attachment.png
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 0000000..529bb7f
 | 
			
		||||
Binary files /dev/null and b/www/media/idf/img/attachment.png differ
 | 
			
		||||
@@ -347,7 +347,7 @@ class IDF_Views_Project
 | 
			
		||||
            $params = array();
 | 
			
		||||
            $keys = array('downloads_access_rights', 'source_access_rights',
 | 
			
		||||
                          'issues_access_rights', 'private_project',
 | 
			
		||||
                          'wiki_access_rights');
 | 
			
		||||
                          'review_access_rights', 'wiki_access_rights');
 | 
			
		||||
            foreach ($keys as $key) {
 | 
			
		||||
                $_val = $request->conf->getVal($key, false);
 | 
			
		||||
                if ($_val !== false) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										227
									
								
								src/IDF/Views/Review.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								src/IDF/Views/Review.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,227 @@
 | 
			
		||||
<?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 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 ***** */
 | 
			
		||||
 | 
			
		||||
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
 | 
			
		||||
Pluf::loadFunction('Pluf_Shortcuts_RenderToResponse');
 | 
			
		||||
Pluf::loadFunction('Pluf_Shortcuts_GetObjectOr404');
 | 
			
		||||
Pluf::loadFunction('Pluf_Shortcuts_GetFormForModel');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Review views.
 | 
			
		||||
 */
 | 
			
		||||
class IDF_Views_Review
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * View list of reviews for a given project.
 | 
			
		||||
     */
 | 
			
		||||
    public $index_precond = array('IDF_Precondition::accessReview');
 | 
			
		||||
    public function index($request, $match)
 | 
			
		||||
    {
 | 
			
		||||
        $prj = $request->project;
 | 
			
		||||
        $title = sprintf(__('%s Code Reviews'), (string) $prj);
 | 
			
		||||
        // Paginator to paginate the pages
 | 
			
		||||
        $pag = new Pluf_Paginator(new IDF_Review());
 | 
			
		||||
        $pag->class = 'recent-issues';
 | 
			
		||||
        $pag->item_extra_props = array('project_m' => $prj,
 | 
			
		||||
                                       'shortname' => $prj->shortname,
 | 
			
		||||
                                       'current_user' => $request->user);
 | 
			
		||||
        $pag->summary = __('This table shows the latest reviews.');
 | 
			
		||||
        $pag->action = array('IDF_Views_Review::index', array($prj->shortname));
 | 
			
		||||
        $otags = $prj->getTagIdsByStatus('open');
 | 
			
		||||
        if (count($otags) == 0) $otags[] = 0;
 | 
			
		||||
        $pag->forced_where = new Pluf_SQL('project=%s AND status IN ('.implode(', ', $otags).')', array($prj->id));
 | 
			
		||||
        $pag->action = array('IDF_Views_Issue::index', array($prj->shortname));
 | 
			
		||||
        $pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted
 | 
			
		||||
        $pag->sort_reverse_order = array('modif_dtime');
 | 
			
		||||
        $list_display = array(
 | 
			
		||||
             'id' => __('Id'),
 | 
			
		||||
             array('summary', 'IDF_Views_Review_SummaryAndLabels', __('Summary')),
 | 
			
		||||
             array('status', 'IDF_Views_Issue_ShowStatus', __('Status')),
 | 
			
		||||
             array('modif_dtime', 'Pluf_Paginator_DateAgo', __('Last Updated')),
 | 
			
		||||
                              );
 | 
			
		||||
        $pag->configure($list_display, array(), array('title', 'modif_dtime'));
 | 
			
		||||
        $pag->items_per_page = 25;
 | 
			
		||||
        $pag->no_results_text = __('No reviews were found.');
 | 
			
		||||
        $pag->sort_order = array('modif_dtime', 'ASC');
 | 
			
		||||
        $pag->setFromRequest($request);
 | 
			
		||||
        return Pluf_Shortcuts_RenderToResponse('idf/review/index.html',
 | 
			
		||||
                                               array(
 | 
			
		||||
                                                     'page_title' => $title,
 | 
			
		||||
                                                     'reviews' => $pag,
 | 
			
		||||
                                                     ),
 | 
			
		||||
                                               $request);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new code review.
 | 
			
		||||
     */
 | 
			
		||||
    public $create_precond = array('IDF_Precondition::accessReview',
 | 
			
		||||
                                   'Pluf_Precondition::loginRequired');
 | 
			
		||||
    public function create($request, $match)
 | 
			
		||||
    {
 | 
			
		||||
        $prj = $request->project;
 | 
			
		||||
        $title = __('Start Code Review');
 | 
			
		||||
        if ($request->method == 'POST') {
 | 
			
		||||
            $form = new IDF_Form_ReviewCreate(array_merge($request->POST,
 | 
			
		||||
                                                         $request->FILES),
 | 
			
		||||
                                              array('project' => $prj,
 | 
			
		||||
                                                    'user' => $request->user
 | 
			
		||||
                                                    ));
 | 
			
		||||
            if ($form->isValid()) {
 | 
			
		||||
                $review = $form->save();
 | 
			
		||||
                $urlr = Pluf_HTTP_URL_urlForView('IDF_Views_Review::view', 
 | 
			
		||||
                                                 array($prj->shortname, $review->id));
 | 
			
		||||
                $request->user->setMessage(sprintf(__('The <a href="%s">code review %d</a> has been created.'), $urlr, $review->id));
 | 
			
		||||
                $url = Pluf_HTTP_URL_urlForView('IDF_Views_Review::index', 
 | 
			
		||||
                                                array($prj->shortname));
 | 
			
		||||
                return new Pluf_HTTP_Response_Redirect($url);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $form = new IDF_Form_ReviewCreate(null,
 | 
			
		||||
                                              array('project' => $prj,
 | 
			
		||||
                                                    'user' => $request->user));
 | 
			
		||||
        }
 | 
			
		||||
        return Pluf_Shortcuts_RenderToResponse('idf/review/create.html',
 | 
			
		||||
                                               array(
 | 
			
		||||
                                                     'page_title' => $title,
 | 
			
		||||
                                                     'form' => $form,
 | 
			
		||||
                                                     ),
 | 
			
		||||
                                               $request);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Download the patch of a review.
 | 
			
		||||
     */
 | 
			
		||||
    public $getPatch_precond = array('IDF_Precondition::accessReview');
 | 
			
		||||
    public function getPatch($request, $match)
 | 
			
		||||
    {
 | 
			
		||||
        $prj = $request->project;
 | 
			
		||||
        $patch = Pluf_Shortcuts_GetObjectOr404('IDF_Review_Patch', $match[2]);
 | 
			
		||||
        $prj->inOr404($patch->get_review());
 | 
			
		||||
        $file = Pluf::f('upload_issue_path').'/'.$patch->patch;
 | 
			
		||||
 | 
			
		||||
        $rep = new Pluf_HTTP_Response_File($file, 'text/plain');
 | 
			
		||||
        $rep->headers['Content-Disposition'] = 'attachment; filename="'.$patch->id.'.diff"';
 | 
			
		||||
        return $rep;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * View a code review.
 | 
			
		||||
     */
 | 
			
		||||
    public $view_precond = array('IDF_Precondition::accessReview');
 | 
			
		||||
    public function view($request, $match)
 | 
			
		||||
    {
 | 
			
		||||
        $prj = $request->project;
 | 
			
		||||
        $review = Pluf_Shortcuts_GetObjectOr404('IDF_Review', $match[2]);
 | 
			
		||||
        $prj->inOr404($review);
 | 
			
		||||
        $url = Pluf_HTTP_URL_urlForView('IDF_Views_Review::view',
 | 
			
		||||
                                        array($prj->shortname, $review->id));
 | 
			
		||||
        $title = Pluf_Template::markSafe(sprintf(__('Review <a href="%s">%d</a>: %s'), $url, $review->id, $review->summary));
 | 
			
		||||
 | 
			
		||||
        $patches = $review->get_patches_list();
 | 
			
		||||
        $patch = $patches[0];
 | 
			
		||||
        $diff = new IDF_Diff(file_get_contents(Pluf::f('upload_issue_path').'/'.$patch->patch));
 | 
			
		||||
        $diff->parse();
 | 
			
		||||
        // The form to submit comments is based on the files in the
 | 
			
		||||
        // diff
 | 
			
		||||
        if ($request->method == 'POST' and !$request->user->isAnonymous()) {
 | 
			
		||||
            $form = new IDF_Form_ReviewFileComment($request->POST,
 | 
			
		||||
                                                   array('files' => $diff->files,
 | 
			
		||||
                                                         'user' => $request->user,
 | 
			
		||||
                                                         'patch' => $patch,
 | 
			
		||||
                                                    ));
 | 
			
		||||
            if ($form->isValid()) {
 | 
			
		||||
                $patch = $form->save();
 | 
			
		||||
                $urlr = Pluf_HTTP_URL_urlForView('IDF_Views_Review::view', 
 | 
			
		||||
                                                 array($prj->shortname, $patch->get_review()->id));
 | 
			
		||||
                $request->user->setMessage(sprintf(__('Your <a href="%s">code review %d</a> has been published.'), $urlr, $patch->get_review()->id));
 | 
			
		||||
                $url = Pluf_HTTP_URL_urlForView('IDF_Views_Review::index', 
 | 
			
		||||
                                                array($prj->shortname));
 | 
			
		||||
                return new Pluf_HTTP_Response_Redirect($url);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $form = new IDF_Form_ReviewFileComment(null,
 | 
			
		||||
                                              array('files' => $diff->files,
 | 
			
		||||
                                                    'user' => $request->user,
 | 
			
		||||
                                                    'patch' => $patch,));
 | 
			
		||||
        }
 | 
			
		||||
        $scm = IDF_Scm::get($request);
 | 
			
		||||
        $files = array();
 | 
			
		||||
        $reviewers = array();
 | 
			
		||||
        foreach ($diff->files as $filename => $def) {
 | 
			
		||||
            $fileinfo = $scm->getFileInfo($filename, $patch->get_commit()->scm_id);
 | 
			
		||||
 | 
			
		||||
            $sql = new Pluf_SQL('cfile=%s', array($filename));
 | 
			
		||||
            $cts = $patch->get_filecomments_list(array('filter'=>$sql->gen(),
 | 
			
		||||
                                                'order'=>'creation_dtime ASC'));
 | 
			
		||||
            foreach ($cts as $ct) {
 | 
			
		||||
                $reviewers[] = $ct->get_submitter();
 | 
			
		||||
            }
 | 
			
		||||
            if (count($def['chunks'])) { 
 | 
			
		||||
                $orig_file = $scm->getBlob($fileinfo);
 | 
			
		||||
                $files[$filename] = array(
 | 
			
		||||
                                          $diff->fileCompare($orig_file, $def, $filename),
 | 
			
		||||
                                          $form->f->{md5($filename)},
 | 
			
		||||
                                          $cts,
 | 
			
		||||
                                          );
 | 
			
		||||
            } else {
 | 
			
		||||
                $files[$filename] = array('', $form->f->{md5($filename)}, $cts);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $reviewers = Pluf_Model_RemoveDuplicates($reviewers);
 | 
			
		||||
        return Pluf_Shortcuts_RenderToResponse('idf/review/view.html',
 | 
			
		||||
                                               array(
 | 
			
		||||
                                                     'page_title' => $title,
 | 
			
		||||
                                                     'review' => $review,
 | 
			
		||||
                                                     'files' => $files,
 | 
			
		||||
                                                     'diff' => $diff,
 | 
			
		||||
                                                     'patch' => $patch,
 | 
			
		||||
                                                     'form' => $form,
 | 
			
		||||
                                                     'reviewers' => $reviewers,
 | 
			
		||||
                                                     ),
 | 
			
		||||
                                               $request);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Display the summary of an review, then on a new line, display the
 | 
			
		||||
 * list of labels with a link to a view "by label only".
 | 
			
		||||
 *
 | 
			
		||||
 * The summary of the review is linking to the review.
 | 
			
		||||
 */
 | 
			
		||||
function IDF_Views_Review_SummaryAndLabels($field, $review, $extra='')
 | 
			
		||||
{
 | 
			
		||||
    $edit = Pluf_HTTP_URL_urlForView('IDF_Views_Review::view', 
 | 
			
		||||
                                     array($review->shortname, $review->id));
 | 
			
		||||
    $tags = array();
 | 
			
		||||
    foreach ($review->get_tags_list() as $tag) {
 | 
			
		||||
        $tags[] = Pluf_esc($tag);
 | 
			
		||||
    }
 | 
			
		||||
    $out = '';
 | 
			
		||||
    if (count($tags)) {
 | 
			
		||||
        $out = '<br /><span class="label note">'.implode(', ', $tags).'</span>';
 | 
			
		||||
    }
 | 
			
		||||
    return sprintf('<a href="%s">%s</a>', $edit, Pluf_esc($review->summary)).$out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -24,9 +24,12 @@
 | 
			
		||||
$cfg = array();
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# You must set it to false once everything is running ok.
 | 
			
		||||
# You must set them to false once everything is running ok.
 | 
			
		||||
#
 | 
			
		||||
$cfg['debug'] = true;
 | 
			
		||||
# It will help you catch errors at beginning when configuring your 
 | 
			
		||||
# SCM backend. It must be turned off in production.
 | 
			
		||||
$cfg['debug_scm'] = true; 
 | 
			
		||||
 | 
			
		||||
# If you have a single git repository, just put the full path to it
 | 
			
		||||
# without trailing slash. The path is the path to the git database,
 | 
			
		||||
 
 | 
			
		||||
@@ -283,6 +283,32 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/(\d+)/delete/$#',
 | 
			
		||||
               'model' => 'IDF_Views_Download',
 | 
			
		||||
               'method' => 'delete');
 | 
			
		||||
 | 
			
		||||
// ---------- CODE REVIEW --------------------------------
 | 
			
		||||
 | 
			
		||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/review/$#',
 | 
			
		||||
               'base' => $base,
 | 
			
		||||
               'priority' => 4,
 | 
			
		||||
               'model' => 'IDF_Views_Review',
 | 
			
		||||
               'method' => 'index');
 | 
			
		||||
 | 
			
		||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/review/(\d+)/$#',
 | 
			
		||||
               'base' => $base,
 | 
			
		||||
               'priority' => 4,
 | 
			
		||||
               'model' => 'IDF_Views_Review',
 | 
			
		||||
               'method' => 'view');
 | 
			
		||||
 | 
			
		||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/review/create/$#',
 | 
			
		||||
               'base' => $base,
 | 
			
		||||
               'priority' => 4,
 | 
			
		||||
               'model' => 'IDF_Views_Review',
 | 
			
		||||
               'method' => 'create');
 | 
			
		||||
 | 
			
		||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/review/getpatch/(\d+)/$#',
 | 
			
		||||
               'base' => $base,
 | 
			
		||||
               'priority' => 4,
 | 
			
		||||
               'model' => 'IDF_Views_Review',
 | 
			
		||||
               'method' => 'getPatch');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// ---------- ADMIN --------------------------------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,5 +33,9 @@ $m['IDF_Search_Occ'] = array('relate_to' => array('IDF_Project'),);
 | 
			
		||||
$m['IDF_WikiPage'] = array('relate_to' => array('IDF_Project', 'Pluf_User'),
 | 
			
		||||
                           'relate_to_many' => array('IDF_Tag', 'Pluf_User'));
 | 
			
		||||
$m['IDF_WikiRevision'] = array('relate_to' => array('IDF_WikiPage', 'Pluf_User'));
 | 
			
		||||
$m['IDF_Review'] = array('relate_to' => array('IDF_Project', 'Pluf_User', 'IDF_Tag'),
 | 
			
		||||
                        'relate_to_many' => array('IDF_Tag', 'Pluf_User'));
 | 
			
		||||
$m['IDF_Review_Patch'] = array('relate_to' => array('IDF_Review', 'Pluf_User'));
 | 
			
		||||
$m['IDF_Review_FileComment'] = array('relate_to' => array('IDF_Review_Patch', 'Pluf_User'));
 | 
			
		||||
 | 
			
		||||
return $m;
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,12 @@
 | 
			
		||||
</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{$form.f.review_access_rights.labelTag}:</strong></th>
 | 
			
		||||
<td>{if $form.f.review_access_rights.errors}{$form.f.review_access_rights.fieldErrors}{/if}
 | 
			
		||||
{$form.f.review_access_rights|unsafe}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th>{if $form.f.private_project.errors}{$form.f.private_project.fieldErrors}{/if}
 | 
			
		||||
{$form.f.private_project|unsafe}
 | 
			
		||||
</th>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										87
									
								
								src/IDF/templates/idf/base-full.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/IDF/templates/idf/base-full.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
 | 
			
		||||
{*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 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 *****
 | 
			
		||||
*}<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 | 
			
		||||
  <link rel="stylesheet" type="text/css" href="{media '/idf/css/yui.css'}" />
 | 
			
		||||
  <link rel="stylesheet" type="text/css" href="{media '/idf/css/style.css'}" />
 | 
			
		||||
  <!--[if lt IE 7]>
 | 
			
		||||
  <link rel="stylesheet" type="text/css" href="{media '/idf/css/ie6.css'}" />
 | 
			
		||||
  <![endif]-->
 | 
			
		||||
  {block extraheader}{/block}
 | 
			
		||||
  <title>{block pagetitle}{$page_title|strip_tags}{/block}</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
<div id="{block docid}doc3{/block}">
 | 
			
		||||
  <div id="hd">
 | 
			
		||||
{if $project}<h1 class="project-title">{$project}</h1>{/if}
 | 
			
		||||
    <p class="top"><a href="#title" accesskey="2"></a>
 | 
			
		||||
{if !$user.isAnonymous()}{aurl 'url', 'IDF_Views_User::myAccount'}{blocktrans}Welcome, <strong><a class="userw" href="{$url}">{$user}</a></strong>.{/blocktrans} <a href="{url 'IDF_Views::logout'}">{trans 'Sign Out'}</a>{else}<a href="{url 'IDF_Views::login'}">{trans 'Sign in or create your account'}</a>{/if}
 | 
			
		||||
{if $project} | <a href="{url 'IDF_Views::index'}">{trans 'Project List'}</a>{/if}
 | 
			
		||||
| <a href="{url 'IDF_Views::faq'}" title="{trans 'Help and accessibility features'}">{trans 'Help'}</a>
 | 
			
		||||
    </p>
 | 
			
		||||
<div id="header">
 | 
			
		||||
<div id="main-tabs">
 | 
			
		||||
{if $project}
 | 
			
		||||
  <a accesskey="1" href="{url 'IDF_Views_Project::home', array($project.shortname)}"{block tabhome}{/block}>{trans 'Project Home'}</a> 
 | 
			
		||||
{if $hasDownloadsAccess}  <a href="{url 'IDF_Views_Download::index', array($project.shortname)}"{block tabdownloads}{/block}>{trans 'Downloads'}</a>{/if} 
 | 
			
		||||
{if $hasWikiAccess}  <a href="{url 'IDF_Views_Wiki::index', array($project.shortname)}"{block tabwiki}{/block}>{trans 'Documentation'}</a>{/if} 
 | 
			
		||||
{if $hasIssuesAccess} <a href="{url 'IDF_Views_Issue::index', array($project.shortname)}"{block tabissues}{/block}>{trans 'Issues'}</a>{/if}
 | 
			
		||||
{if $hasSourceAccess} <a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}"{block tabsource}{/block}>{trans 'Source'}</a>{/if}
 | 
			
		||||
{if $hasReviewAccess} <a href="{url 'IDF_Views_Review::index', array($project.shortname)}"{block tabreview}{/block}>{trans 'Code Review'}</a>{/if}
 | 
			
		||||
{if $isOwner}
 | 
			
		||||
  <a href="{url 'IDF_Views_Project::admin', array($project.shortname)}"{block tabadmin}{/block}>{trans 'Administer'}</a>{/if}{/if}
 | 
			
		||||
</div>
 | 
			
		||||
{block subtabs}{if $user.isAnonymous()} | {aurl 'url', 'IDF_Views::login'}{blocktrans}<a href="{$url}">Sign in or create your account</a> to create issues or add comments{/blocktrans}{/if}{/block}
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
	  <h1 class="title" id="title">{block titleicon}{/block}{block title}{$page_title}{/block}</h1>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div id="bd">
 | 
			
		||||
    <div id="yui-main"> 
 | 
			
		||||
      <div class="yui-b">
 | 
			
		||||
	<div class="yui-g"> 
 | 
			
		||||
          {if $user and $user.id}{getmsgs $user}{/if}
 | 
			
		||||
	  <div class="content">{block body}{/block}</div>
 | 
			
		||||
	</div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div id="ft">{block foot}{/block}</div>
 | 
			
		||||
</div>
 | 
			
		||||
<script type="text/javascript" src="{media '/idf/js/jquery-1.2.6.min.js'}"></script>
 | 
			
		||||
{include 'idf/js-hotkeys.html'}
 | 
			
		||||
{block javascript}{/block}
 | 
			
		||||
{if $project}
 | 
			
		||||
<script type="text/javascript">{literal}
 | 
			
		||||
<!-- //
 | 
			
		||||
 $(document).ready(function(){
 | 
			
		||||
	var frag = location.hash;
 | 
			
		||||
	if (frag.length > 3 && frag.substring(0, 3) == '#ic') {
 | 
			
		||||
		$(frag).addClass("issue-comment-focus");
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
//  -->{/literal}
 | 
			
		||||
</script>{/if}
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -51,7 +51,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="yui-b context">{block context}{/block}</div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div id="ft">{block foot}<a href="http://www.indefero.net" title="InDefero, bug tracking and more"><img src="{media '/idf/img/powered-by-indefero.png'}" alt="InDefero Logo" /></a>{/block}</div>
 | 
			
		||||
  <div id="ft">{block foot}{/block}</div>
 | 
			
		||||
</div>
 | 
			
		||||
<script type="text/javascript" src="{media '/idf/js/jquery-1.2.6.min.js'}"></script>
 | 
			
		||||
{include 'idf/js-hotkeys.html'}
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,7 @@
 | 
			
		||||
{if $hasWikiAccess}  <a href="{url 'IDF_Views_Wiki::index', array($project.shortname)}"{block tabwiki}{/block}>{trans 'Documentation'}</a>{/if} 
 | 
			
		||||
{if $hasIssuesAccess} <a href="{url 'IDF_Views_Issue::index', array($project.shortname)}"{block tabissues}{/block}>{trans 'Issues'}</a>{/if}
 | 
			
		||||
{if $hasSourceAccess} <a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}"{block tabsource}{/block}>{trans 'Source'}</a>{/if}
 | 
			
		||||
{if $hasReviewAccess} <a href="{url 'IDF_Views_Review::index', array($project.shortname)}"{block tabreview}{/block}>{trans 'Code Review'}</a>{/if}
 | 
			
		||||
{if $isOwner}
 | 
			
		||||
  <a href="{url 'IDF_Views_Project::admin', array($project.shortname)}"{block tabadmin}{/block}>{trans 'Administer'}</a>{/if}{/if}
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,3 +13,4 @@
 | 
			
		||||
{block context}
 | 
			
		||||
<p><strong>{trans 'Managed Projects:'}</strong> {$projects.count()}</p>
 | 
			
		||||
{/block}
 | 
			
		||||
{block foot}<div id="branding">Powered by <a href="http://www.indefero.net" title="InDefero, bug tracking and more">InDefero</a>,<br />a <a href="http://www.ceondo.com">Céondo Ltd</a> initiative.</div>{/block}
 | 
			
		||||
 
 | 
			
		||||
@@ -113,7 +113,6 @@ $(document).ready(function(){
 | 
			
		||||
    }
 | 
			
		||||
    });
 | 
			
		||||
</script>
 | 
			
		||||
{/literal}{/block}
 | 
			
		||||
 | 
			
		||||
{/literal}
 | 
			
		||||
{include 'idf/issues/js-autocomplete.html'}{/block}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,4 +40,4 @@
 | 
			
		||||
</p>
 | 
			
		||||
{/block}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{block foot}<div id="branding">Powered by <a href="http://www.indefero.net" title="InDefero, bug tracking and more">InDefero</a>,<br />a <a href="http://www.ceondo.com">Céondo Ltd</a> initiative.</div>{/block}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								src/IDF/templates/idf/review/base-full.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/IDF/templates/idf/review/base-full.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
{extends "idf/base-full.html"}
 | 
			
		||||
{block tabreview} class="active"{/block}
 | 
			
		||||
{block subtabs}
 | 
			
		||||
<div id="sub-tabs">
 | 
			
		||||
<a {if $inOpenReviews}class="active" {/if}href="{url 'IDF_Views_Review::index', array($project.shortname)}">{trans 'Open Reviews'}</a> {*
 | 
			
		||||
 | 
			
		||||
{if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a> | <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')}">{trans 'My Issues'}</a>{/if} |
 | 
			
		||||
<form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get">
 | 
			
		||||
<input accesskey="4" type="text" value="{$q}" name="q" size="20" />
 | 
			
		||||
<input type="submit" name="s" value="{trans 'Search'}" />
 | 
			
		||||
</form>
 | 
			
		||||
*}
 | 
			
		||||
{superblock}
 | 
			
		||||
</div>
 | 
			
		||||
{/block}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/IDF/templates/idf/review/base.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/IDF/templates/idf/review/base.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{extends "idf/base.html"}
 | 
			
		||||
{block tabreview} class="active"{/block}
 | 
			
		||||
{block subtabs}
 | 
			
		||||
<div id="sub-tabs">
 | 
			
		||||
<a {if $inOpenReviews}class="active" {/if}href="{url 'IDF_Views_Review::index', array($project.shortname)}">{trans 'Open Reviews'}</a> 
 | 
			
		||||
{if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Review::create', array($project.shortname)}">{trans 'Start Code Review'}</a> {/if} 
 | 
			
		||||
{superblock}
 | 
			
		||||
</div>
 | 
			
		||||
{/block}
 | 
			
		||||
							
								
								
									
										69
									
								
								src/IDF/templates/idf/review/create.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/IDF/templates/idf/review/create.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
{extends "idf/review/base.html"}
 | 
			
		||||
{block docclass}yui-t3{assign $inCreate = true}{/block}
 | 
			
		||||
{block body}
 | 
			
		||||
{if $form.errors}
 | 
			
		||||
<div class="px-message-error">
 | 
			
		||||
<p>{trans 'The form contains some errors. Please correct them to submit the code review.'}</p>
 | 
			
		||||
{if $form.get_top_errors}
 | 
			
		||||
{$form.render_top_errors|unsafe}
 | 
			
		||||
{/if}
 | 
			
		||||
</div>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
<form method="post" enctype="multipart/form-data" action=".">
 | 
			
		||||
<table class="form" summary="">
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{$form.f.summary.labelTag}:</strong></th>
 | 
			
		||||
<td>{if $form.f.summary.errors}{$form.f.summary.fieldErrors}{/if}
 | 
			
		||||
{$form.f.summary|unsafe}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{$form.f.description.labelTag}:</strong></th>
 | 
			
		||||
<td>{if $form.f.description.errors}{$form.f.description.fieldErrors}{/if}
 | 
			
		||||
{$form.f.description|unsafe}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{$form.f.commit.labelTag}:</strong></th>
 | 
			
		||||
<td>{if $form.f.commit.errors}{$form.f.commit.fieldErrors}{/if}
 | 
			
		||||
{$form.f.commit|unsafe}<br />
 | 
			
		||||
<span class="helptext">{trans 'Be sure to provide the right commit/revision reference for your patch to correctly apply.'}</span>
 | 
			
		||||
</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{$form.f.patch.labelTag}:</strong></th>
 | 
			
		||||
<td>{if $form.f.patch.errors}{$form.f.patch.fieldErrors}{/if}
 | 
			
		||||
{$form.f.patch|unsafe}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>{if $isOwner or $isMember}
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{$form.f.status.labelTag}:</strong></th>
 | 
			
		||||
<td>{if $form.f.status.errors}{$form.f.status.fieldErrors}{/if}
 | 
			
		||||
{$form.f.status|unsafe}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>{/if}
 | 
			
		||||
<tr>
 | 
			
		||||
<td> </td>
 | 
			
		||||
<td><input type="submit" value="{trans 'Start Code Review'}" name="submit" /> | <a href="{url 'IDF_Views_Review::index', array($project.shortname)}">{trans 'Cancel'}</a>
 | 
			
		||||
</td>
 | 
			
		||||
</tr>
 | 
			
		||||
</table>
 | 
			
		||||
</form>
 | 
			
		||||
{/block}
 | 
			
		||||
{block context}
 | 
			
		||||
<div class="issue-submit-info">
 | 
			
		||||
{blocktrans}<p>To start a code review, you need to provide:</p>
 | 
			
		||||
<ul>
 | 
			
		||||
<li>A commit or revision of the current code in the repository from which you started your work.</li>
 | 
			
		||||
<li>A patch describing your changes with respect to the reference commit.</li>
 | 
			
		||||
<li><strong>Check your patch to not provide any password or confidential information!</strong></li>
 | 
			
		||||
</ul>{/blocktrans}
 | 
			
		||||
</div>
 | 
			
		||||
{/block}
 | 
			
		||||
{block javascript}
 | 
			
		||||
<script type="text/javascript">
 | 
			
		||||
document.getElementById('id_summary').focus();
 | 
			
		||||
</script>{/block}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								src/IDF/templates/idf/review/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/IDF/templates/idf/review/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
{extends "idf/review/base.html"}
 | 
			
		||||
{block docclass}yui-t2{assign $inOpenReviews=true}{/block}
 | 
			
		||||
{block body}
 | 
			
		||||
{$reviews.render}
 | 
			
		||||
{if !$user.isAnonymous()}
 | 
			
		||||
{aurl 'url', 'IDF_Views_Review::create', array($project.shortname)}
 | 
			
		||||
<p><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/add.png'}" alt="+" align="bottom" /></a> <a href="{$url}">{trans 'New Code Review'}</a></p>{/if}
 | 
			
		||||
 | 
			
		||||
{/block}
 | 
			
		||||
{block context}
 | 
			
		||||
{*
 | 
			
		||||
{aurl 'open_url', 'IDF_Views_Issue::index', array($project.shortname)}
 | 
			
		||||
{aurl 'closed_url', 'IDF_Views_Issue::listStatus', array($project.shortname, 'closed')}
 | 
			
		||||
{blocktrans}<p><strong>Open issues:</strong> <a href="{$open_url}">{$open}</a></p>
 | 
			
		||||
<p><strong>Closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p>{/blocktrans}
 | 
			
		||||
{assign $class = ''}{assign $i = 0}
 | 
			
		||||
<p class="smaller">{foreach $project.getTagCloud($cloud) as $label}
 | 
			
		||||
{aurl 'url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'open')}
 | 
			
		||||
{if $class != $label.class}{if $i != 0}<br />{/if}<strong class="label">{$label.class}:</strong> {/if}
 | 
			
		||||
<a href="{$url}" class="label">{$label.name}</a>,{assign $class = $label.class}{assign $i = $i + 1}{/foreach}</p>
 | 
			
		||||
*}{/block}
 | 
			
		||||
							
								
								
									
										116
									
								
								src/IDF/templates/idf/review/view.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/IDF/templates/idf/review/view.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
{extends "idf/review/base-full.html"}
 | 
			
		||||
{block extraheader}<link rel="stylesheet" type="text/css" href="{media '/idf/css/prettify.css'}" />{/block}
 | 
			
		||||
{block docclass}yui-t1{assign $inCreate = true}{/block}
 | 
			
		||||
{block body}
 | 
			
		||||
{if $form.errors}
 | 
			
		||||
<div class="px-message-error">
 | 
			
		||||
<p>{trans 'The form contains some errors. Please correct them to submit your review.'}</p>
 | 
			
		||||
{if $form.get_top_errors}
 | 
			
		||||
{$form.render_top_errors|unsafe}
 | 
			
		||||
{/if}
 | 
			
		||||
</div>
 | 
			
		||||
{/if}
 | 
			
		||||
<table class="commit" summary="">
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{trans 'Created:'}</strong></th><td>{$patch.creation_dtime|date:"%Y-%m-%d %H:%M:%S"} ({$patch.creation_dtime|dateago})</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{trans 'Updated:'}</strong></th><td>{$review.modif_dtime|dateago}</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{trans 'Author:'}</strong></th><td>{$review.get_submitter()}</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{trans 'Commit:'}</strong></th><td class="mono"><a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $patch.get_commit().scm_id)}" title="{trans 'View corresponding source tree'}">{$patch.get_commit().scm_id}</a></td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{trans 'Description:'}</strong></th><td>{issuetext $review.summary, $request}<br /><br />{issuetext $patch.summary, $request}</td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{trans 'Reviewers:'}</strong></th><td>{if count($reviewers)}{foreach $reviewers as $r}{$r}, {/foreach}{else}{trans 'No reviewers at the moment.'}{/if}</td>
 | 
			
		||||
</tr>{if count($diff.files)}
 | 
			
		||||
<tr>
 | 
			
		||||
<th><strong>{trans 'Files:'}</strong></th>
 | 
			
		||||
<td>
 | 
			
		||||
{foreach $diff.files as $filename=>$diffdef}
 | 
			
		||||
{assign $ndiff = count($diffdef['chunks'])}
 | 
			
		||||
{assign $nc = $files[$filename][2]->count()}
 | 
			
		||||
<a href="{url 'IDF_Views_Source::tree', array($project.shortname, $patch.get_commit().scm_id, $filename)}">{$filename}</a> (<a href="#diff-{$filename|md5}">{blocktrans $ndiff}{$ndiff} diff{plural}{$ndiff} diffs{/blocktrans}</a>{if $nc}, <a href="#ct-{$filename|md5}">{blocktrans $nc}{$nc} comment{plural}{$nc} comments{/blocktrans}</a>{/if})<br />
 | 
			
		||||
{/foreach}
 | 
			
		||||
</td>
 | 
			
		||||
</tr>{/if}
 | 
			
		||||
<tr>{aurl 'url', 'IDF_Views_Review::getPatch', array($project.shortname, $patch.id)}
 | 
			
		||||
<th> </th><td><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/package-grey.png'}" alt="{trans 'Archive'}" align="bottom" /></a> <a href="{$url}" class="soft">{trans 'Download the corresponding diff file'}</a></td>
 | 
			
		||||
</tr>
 | 
			
		||||
</table>
 | 
			
		||||
 | 
			
		||||
{if !$user.isAnonymous()}
 | 
			
		||||
<div class="issue-submit-info" style="width: 50%">
 | 
			
		||||
<p><strong>{trans 'How to Participate in a Code Review'}</strong></p>
 | 
			
		||||
 | 
			
		||||
<p>{blocktrans}Code review is a process in which
 | 
			
		||||
after or before changes are commited into the code repository,
 | 
			
		||||
different people discuss the code changes. The goal is
 | 
			
		||||
to <strong>improve the quality of the code and the
 | 
			
		||||
contributions</strong>, as such, you must be pragmatic when writing
 | 
			
		||||
your review. Correctly mention the line numbers (in the old or in the
 | 
			
		||||
new file) and try to keep a good balance between seriousness and fun.
 | 
			
		||||
{/blocktrans}</p>
 | 
			
		||||
<p>{blocktrans}
 | 
			
		||||
<strong>Proposing code for review is intimidating</strong>, you know
 | 
			
		||||
you will receive critics, so please, as a reviewer, <strong>keep this
 | 
			
		||||
process fun</strong>, use it to help your contributor learn your
 | 
			
		||||
coding standards and the structure of the code and <strong>make them want
 | 
			
		||||
to propose more contributions</strong>.
 | 
			
		||||
{/blocktrans}</p></div>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<form method="post" action=".">
 | 
			
		||||
{foreach $files as $file=>$def}
 | 
			
		||||
<table class="diff" summary=" ">
 | 
			
		||||
<tbody>
 | 
			
		||||
<tr id="diff-{$file|md5}"><th colspan="4">{$file}</th></tr>
 | 
			
		||||
<tr><th colspan="2">{trans 'Old'}</th><th colspan="2">{trans 'New'}</th></tr>
 | 
			
		||||
{$def[0]}
 | 
			
		||||
</tbody>
 | 
			
		||||
</table>
 | 
			
		||||
{assign $comments = $def[2]}
 | 
			
		||||
{assign $nc = $comments.count()} 
 | 
			
		||||
{assign $i = 1}
 | 
			
		||||
{foreach $comments as $c}  
 | 
			
		||||
<div class="issue-comment{if $i == 1} issue-comment-first{/if}{if $i == $nc} issue-comment-last{/if}" id="ic{$c.id}">{assign $who = $c.get_submitter()}{aurl 'whourl', 'IDF_Views_User::view', array($who.login)}
 | 
			
		||||
{aurl 'url', 'IDF_Views_Review::view', array($project.shortname, $review.id)}
 | 
			
		||||
{assign $id = $c.id}
 | 
			
		||||
{assign $url = $url~'#ic'~$c.id}
 | 
			
		||||
<p{if $i == 1} id="ct-{$file|md5}"{/if}>{blocktrans}Comment <a href="{$url}">{$i}</a> by <a href="{$whourl}">{$who}</a>, {$c.creation_dtime|date}{/blocktrans}</p>
 | 
			
		||||
 | 
			
		||||
<pre class="issue-comment-text">{issuetext $c.content, $request}</pre>
 | 
			
		||||
</div> {assign $i = $i + 1}
 | 
			
		||||
{/foreach}
 | 
			
		||||
{if !$user.isAnonymous()}
 | 
			
		||||
<table class="form" summary=" ">
 | 
			
		||||
<tr>
 | 
			
		||||
<td> </td>
 | 
			
		||||
<td>
 | 
			
		||||
<p>{blocktrans}Your comments on the changes in file <em>{$file}</em>:{/blocktrans}<br />{$def[1]|safe}</p>
 | 
			
		||||
</td>
 | 
			
		||||
</tr></table>{/if}
 | 
			
		||||
{/foreach}
 | 
			
		||||
{if !$user.isAnonymous()}
 | 
			
		||||
<table class="form" summary=" ">
 | 
			
		||||
<tr>
 | 
			
		||||
<td> </td>
 | 
			
		||||
<td><input type="submit" value="{trans 'Submit Code Review'}" name="submit" /> | <a href="{url 'IDF_Views_Review::index', array($project.shortname)}">{trans 'Cancel'}</a>
 | 
			
		||||
</td>
 | 
			
		||||
</tr></table>
 | 
			
		||||
{/if}
 | 
			
		||||
</form>
 | 
			
		||||
{/block}
 | 
			
		||||
u
 | 
			
		||||
{block javascript}
 | 
			
		||||
<script type="text/javascript" src="{media '/idf/js/prettify.js'}"></script>
 | 
			
		||||
<script type="text/javascript">
 | 
			
		||||
prettyPrint();
 | 
			
		||||
</script>
 | 
			
		||||
{/block}
 | 
			
		||||
@@ -69,6 +69,14 @@ a.userw {
 | 
			
		||||
  color: #777;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a.soft { 
 | 
			
		||||
  color: #777;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a.soft:visited { 
 | 
			
		||||
  color: #777;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.context { 
 | 
			
		||||
  padding-left: 1em;
 | 
			
		||||
}
 | 
			
		||||
@@ -518,6 +526,7 @@ table.diff tr.diff-next td {
 | 
			
		||||
  padding: 1px 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * view file content
 | 
			
		||||
 */
 | 
			
		||||
@@ -633,3 +642,35 @@ div.deprecated-page {
 | 
			
		||||
.delp a { 
 | 
			
		||||
  color: #a00;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#branding { 
 | 
			
		||||
  float: right;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  margin-right: -10px;
 | 
			
		||||
  width: 115px;
 | 
			
		||||
  font-size: 8px;
 | 
			
		||||
  text-align: right;
 | 
			
		||||
  padding-right: 20px;
 | 
			
		||||
  padding-left: 0px;
 | 
			
		||||
  background-color: #eeeeec; 
 | 
			
		||||
  -moz-border-radius: 3px 0 0 3px;
 | 
			
		||||
  -webkit-border-radius: 3px 0 0 3px;
 | 
			
		||||
  color: #888a85;
 | 
			
		||||
  clear: both;
 | 
			
		||||
  background-image: url("../img/ceondo.png");
 | 
			
		||||
  background-repeat: no-repeat;
 | 
			
		||||
  background-position: top right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#branding a { 
 | 
			
		||||
  color: #777;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#branding a:visited { 
 | 
			
		||||
  color: #777;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ft { 
 | 
			
		||||
  padding: 0px;
 | 
			
		||||
  margin: 0px;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								www/media/idf/img/ceondo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								www/media/idf/img/ceondo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 662 B  | 
		Reference in New Issue
	
	Block a user