Merge branch 'develop' of projects.ceondo.com:indefero into feature.scilab

This commit is contained in:
Thomas Keller 2011-10-08 19:26:46 +02:00
commit 52383edfd0
24 changed files with 371 additions and 108 deletions

View File

@ -33,6 +33,7 @@ Much appreciated contributors (in alphabetical order):
Samuel Suther <info@suther.de> - German translation Samuel Suther <info@suther.de> - German translation
Sindre R. Myren <sindrero@stud.ntnu.no> Sindre R. Myren <sindrero@stud.ntnu.no>
Stewart Platt <stew@futurete.ch> Stewart Platt <stew@futurete.ch>
Stéphane Baron <sbaron>
Thomas Keller <me@thomaskeller.biz> - Monotone support Thomas Keller <me@thomaskeller.biz> - Monotone support
Vladimir Solomatin <slash> Vladimir Solomatin <slash>
William Martin <william.martin@lcpc.fr> William Martin <william.martin@lcpc.fr>

View File

@ -23,12 +23,19 @@ or newer to properly run this version of Indefero!
updated downloads for a specific project, similar to the available post-commit web hook updated downloads for a specific project, similar to the available post-commit web hook
- Indefero's issue tracker can now bi-directionally link issues with variable, configurable - Indefero's issue tracker can now bi-directionally link issues with variable, configurable
terms, such as "is related to", "is blocked by" or "is duplicated by" (issue 638) terms, such as "is related to", "is blocked by" or "is duplicated by" (issue 638)
- When you search for issues, the results can further be refined by issue state (open or closed)
and label (partially implements issue 548)
- Mercurial source views now show parent revisions (if any) and detailed change information - Mercurial source views now show parent revisions (if any) and detailed change information
- Subversion source views now show detailed change information (issue 622) - Subversion source views now show detailed change information (issue 622)
- File download URLs now contain the file name rather than the upload id; old links still work though (issues 559 and 686) - File download URLs now contain the file name rather than the upload id; old links still work though (issues 559 and 686)
- Display monotone file and directory attributes in the tree and file view - Display monotone file and directory attributes in the tree and file view
(needs a monotone with an interface version of 13.1 or newer) (needs a monotone with an interface version of 13.1 or newer)
- The context area is now kept in view when a page scrolls down several pages - The context area is now kept in view when a page scrolls down several pages
- Add a summary section to the issue tracker with statistics about open/close issues,
tags of open issue, and count of open tickets for each owner.
- Improved home page with an customizable icon for each project.
- The download section provide MD5 for each files.
- Wiki page have now a css for printer output (issue 713)
## Bugfixes ## Bugfixes
@ -56,6 +63,9 @@ or newer to properly run this version of Indefero!
as we have no information what the author string is actually encoded in as we have no information what the author string is actually encoded in
- Indefero no longer displays an empty parents paragraph in the commit view for root revisions of - Indefero no longer displays an empty parents paragraph in the commit view for root revisions of
a git repository a git repository
- Indefero now only shows the tags of the closed and not the open issues in the closed issues list
- Avatar URL generation use correctly the configuration (issue 732)
- Git cron job doesn't erase anymore manually added keys (issue 247)
## Documentation ## Documentation

View File

@ -88,6 +88,7 @@ class IDF_Middleware
'showuser' => 'IDF_Template_ShowUser', 'showuser' => 'IDF_Template_ShowUser',
'ashowuser' => 'IDF_Template_AssignShowUser', 'ashowuser' => 'IDF_Template_AssignShowUser',
'appversion' => 'IDF_Template_AppVersion', 'appversion' => 'IDF_Template_AppVersion',
'upload' => 'IDF_Template_Tag_UploadUrl',
)); ));
$params['modifiers'] = array_merge($params['modifiers'], $params['modifiers'] = array_merge($params['modifiers'],
array( array(

View File

@ -178,9 +178,10 @@ GROUP BY uid";
* *
* @param string Status ('open'), 'closed' * @param string Status ('open'), 'closed'
* @param IDF_Tag Subfilter with a label (null) * @param IDF_Tag Subfilter with a label (null)
* @param array Restrict further to a list of ids
* @return int Count * @return int Count
*/ */
public function getIssueCountByStatus($status='open', $label=null) public function getIssueCountByStatus($status='open', $label=null, $ids=array())
{ {
switch ($status) { switch ($status) {
case 'open': case 'open':
@ -203,12 +204,48 @@ GROUP BY uid";
$sql2 = new Pluf_SQL('idf_tag_id=%s', array($label->id)); $sql2 = new Pluf_SQL('idf_tag_id=%s', array($label->id));
$sql->SAnd($sql2); $sql->SAnd($sql2);
} }
if (count($ids) > 0) {
$sql2 = new Pluf_SQL(sprintf('id IN (%s)', implode(', ', $ids)));
$sql->SAnd($sql2);
}
$params = array('filter' => $sql->gen()); $params = array('filter' => $sql->gen());
if (!is_null($label)) { $params['view'] = 'join_tags'; } if (!is_null($label)) { $params['view'] = 'join_tags'; }
$gissue = new IDF_Issue(); $gissue = new IDF_Issue();
return $gissue->getCount($params); return $gissue->getCount($params);
} }
/**
* Get the tags for a specific list of issues.
*
* @param string Status ('open') or 'closed'
* @param array A list of issue ids
* @return array An array of tag objects
*/
public function getTagsByIssues($issue_ids=array())
{
// make the below query always a valid one
if (count($issue_ids) == 0) $issue_ids[] = 0;
$assocTable = $this->_con->pfx.'idf_issue_idf_tag_assoc';
$query = sprintf(
'SELECT DISTINCT idf_tag_id FROM %s '.
'WHERE idf_issue_id IN (%s) '.
'GROUP BY idf_tag_id',
$assocTable, implode(',', $issue_ids)
);
$db = Pluf::db();
$dbData = $db->select($query);
$ids = array(0);
foreach ($dbData as $data) {
$ids[] = $data['idf_tag_id'];
}
$sql = new Pluf_SQL(sprintf('id IN (%s)', implode(', ', $ids)));
$model = new IDF_Tag();
return $model->getList(array('filter' => $sql->gen()));
}
/** /**
* Get the open/closed tag ids as they are often used when doing * Get the open/closed tag ids as they are often used when doing
* listings. * listings.
@ -415,7 +452,11 @@ GROUP BY uid";
foreach ($this->_con->select($sql) as $idc) { foreach ($this->_con->select($sql) as $idc) {
$tag = new IDF_Tag($idc['id']); $tag = new IDF_Tag($idc['id']);
$tag->nb_use = $idc['nb_use']; $tag->nb_use = $idc['nb_use'];
$tags[] = $tag; // group by class
if (!array_key_exists($tag->class, $tags)) {
$tags[$tag->class] = array();
}
$tags[$tag->class][] = $tag;
} }
return new Pluf_Template_ContextVars($tags); return new Pluf_Template_ContextVars($tags);
} }

View File

@ -0,0 +1,35 @@
<?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) 2011 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
class IDF_Template_Tag_UploadUrl extends Pluf_Template_Tag
{
function start($file='')
{
echo IDF_Template_Tag_UploadUrl::url($file);
}
public static function url($file='')
{
return Pluf::f('url_upload', Pluf_Template_Tag_MediaUrl::url() . '/upload') . $file;
}
}

View File

@ -319,13 +319,11 @@ class IDF_Views_Download
$pag->no_results_text = __('No downloads were found.'); $pag->no_results_text = __('No downloads were found.');
$pag->sort_order = array('creation_dtime', 'DESC'); $pag->sort_order = array('creation_dtime', 'DESC');
$pag->setFromRequest($request); $pag->setFromRequest($request);
$tags = $prj->getTagCloud('downloads');
return Pluf_Shortcuts_RenderToResponse('idf/downloads/index.html', return Pluf_Shortcuts_RenderToResponse('idf/downloads/index.html',
array( array(
'page_title' => $title, 'page_title' => $title,
'label' => $tag, 'label' => $tag,
'downloads' => $pag, 'downloads' => $pag,
'tags' => $tags,
'dlabel' => $dtag, 'dlabel' => $dtag,
), ),
$request); $request);

View File

@ -71,7 +71,9 @@ class IDF_Views_Issue
'page_title' => $title, 'page_title' => $title,
'open' => $open, 'open' => $open,
'closed' => $closed, 'closed' => $closed,
'issues' => $pag); 'issues' => $pag,
'cloud' => 'issues',
);
if ($api) return $params; if ($api) return $params;
return Pluf_Shortcuts_RenderToResponse('idf/issues/index.html', return Pluf_Shortcuts_RenderToResponse('idf/issues/index.html',
$params, $request); $params, $request);
@ -109,17 +111,21 @@ class IDF_Views_Issue
foreach ($owners as $user => $nb) { foreach ($owners as $user => $nb) {
if ($user === '') { if ($user === '') {
$key = __('Not assigned'); $key = __('Not assigned');
$login = null;
} else { } else {
$obj = Pluf::factory('Pluf_User')->getOne(array('filter'=>'id='.$user)); $obj = Pluf::factory('Pluf_User')->getOne(array('filter'=>'id='.$user));
$key = $obj->first_name . ' ' . $obj->last_name; $key = $obj->first_name . ' ' . $obj->last_name;
$login = $obj->login;
} }
$ownerStatistics[$key] = array($nb, (int)(100 * $nb / $opened)); $ownerStatistics[$key] = array($nb, (int)(100 * $nb / $opened), $login);
} }
// Issue class tag statistics // Issue class tag statistics
$tags = $prj->getTagCloud(); $grouped_tags = $prj->getTagCloud();
foreach ($tags as $t) { foreach ($grouped_tags as $class => $tags) {
$tagStatistics[$t->class][$t->name] = array($t->nb_use, $t->id); foreach ($tags as $tag) {
$tagStatistics[$class][$tag->name] = array($tag->nb_use, $tag->id);
}
} }
foreach($tagStatistics as $k => $v) { foreach($tagStatistics as $k => $v) {
$nbIssueInClass = 0; $nbIssueInClass = 0;
@ -313,42 +319,55 @@ class IDF_Views_Issue
* *
* Only open issues are shown. * Only open issues are shown.
*/ */
public $myIssues_precond = array('IDF_Precondition::accessIssues', public $userIssues_precond = array('IDF_Precondition::accessIssues');
'Pluf_Precondition::loginRequired'); public function userIssues($request, $match)
public function myIssues($request, $match)
{ {
$prj = $request->project; $prj = $request->project;
$sql = new Pluf_SQL('login=%s', array($match[2]));
$user = Pluf::factory('Pluf_User')->getOne(array('filter' => $sql->gen()));
if ($user === null) {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::index',
array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url);
}
$otags = $prj->getTagIdsByStatus('open'); $otags = $prj->getTagIdsByStatus('open');
$ctags = $prj->getTagIdsByStatus('closed'); $ctags = $prj->getTagIdsByStatus('closed');
if (count($otags) == 0) $otags[] = 0; if (count($otags) == 0) $otags[] = 0;
if (count($ctags) == 0) $ctags[] = 0; if (count($ctags) == 0) $ctags[] = 0;
switch ($match[2]) { switch ($match[3]) {
case 'submit': case 'submit':
$title = sprintf(__('My Submitted %s Issues'), (string) $prj); $titleFormat = __('%s %s Submitted %s Issues');
$f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
break; break;
case 'submitclosed': case 'submitclosed':
$title = sprintf(__('My Closed Submitted %s Issues'), (string) $prj); $titleFormat = __('%s %s Closed Submitted %s Issues');
$f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
break; break;
case 'ownerclosed': case 'ownerclosed':
$title = sprintf(__('My Closed Working %s Issues'), (string) $prj); $titleFormat = __('%s %s Closed Working %s Issues');
$f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
break; break;
default: default:
$title = sprintf(__('My Working %s Issues'), (string) $prj); $titleFormat = __('%s %s Working %s Issues');
$f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
break; break;
} }
$title = sprintf($titleFormat,
$user->first_name,
$user->last_name,
(string) $prj);
// Get stats about the issues // Get stats about the issues
$sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
$nb_submit = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_submit = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
$sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
$nb_owner = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_owner = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
// Closed issues // Closed issues
$sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
$nb_submit_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_submit_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
$sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
$nb_owner_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_owner_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
// Paginator to paginate the issues // Paginator to paginate the issues
@ -359,7 +378,7 @@ class IDF_Views_Issue
'current_user' => $request->user); 'current_user' => $request->user);
$pag->summary = __('This table shows the open issues.'); $pag->summary = __('This table shows the open issues.');
$pag->forced_where = $f_sql; $pag->forced_where = $f_sql;
$pag->action = array('IDF_Views_Issue::myIssues', array($prj->shortname, $match[2])); $pag->action = array('IDF_Views_Issue::userIssues', array($prj->shortname, $match[2]));
$pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted $pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted
$pag->sort_reverse_order = array('modif_dtime'); $pag->sort_reverse_order = array('modif_dtime');
$pag->sort_link_title = true; $pag->sort_link_title = true;
@ -374,9 +393,10 @@ class IDF_Views_Issue
$pag->items_per_page = 10; $pag->items_per_page = 10;
$pag->no_results_text = __('No issues were found.'); $pag->no_results_text = __('No issues were found.');
$pag->setFromRequest($request); $pag->setFromRequest($request);
return Pluf_Shortcuts_RenderToResponse('idf/issues/my-issues.html', return Pluf_Shortcuts_RenderToResponse('idf/issues/userIssues.html',
array('project' => $prj, array('project' => $prj,
'page_title' => $title, 'page_title' => $title,
'login' => $user->login,
'nb_submit' => $nb_submit, 'nb_submit' => $nb_submit,
'nb_owner' => $nb_owner, 'nb_owner' => $nb_owner,
'nb_submit_closed' => $nb_submit_closed, 'nb_submit_closed' => $nb_submit_closed,
@ -429,45 +449,142 @@ class IDF_Views_Issue
public $search_precond = array('IDF_Precondition::accessIssues'); public $search_precond = array('IDF_Precondition::accessIssues');
public function search($request, $match) public function search($request, $match)
{
$query = !isset($request->REQUEST['q']) ? '' : $request->REQUEST['q'];
return $this->doSearch($request, $query, 'open');
}
public $searchStatus_precond = array('IDF_Precondition::accessIssues');
public function searchStatus($request, $match)
{
$query = !isset($request->REQUEST['q']) ? '' : $request->REQUEST['q'];
$status = in_array($match[2], array('open', 'closed')) ? $match[2] : 'open';
return $this->doSearch($request, $query, $status);
}
public $searchLabel_precond = array('IDF_Precondition::accessIssues');
public function searchLabel($request, $match)
{
$query = !isset($request->REQUEST['q']) ? '' : $request->REQUEST['q'];
$tag_id = intval($match[2]);
$status = in_array($match[3], array('open', 'closed')) ? $match[3] : 'open';
return $this->doSearch($request, $query, $status, $tag_id);
}
private function doSearch($request, $query, $status, $tag_id=null)
{ {
$prj = $request->project; $prj = $request->project;
if (!isset($request->REQUEST['q']) or trim($request->REQUEST['q']) == '') { if (trim($query) == '') {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::index', $url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::index', array($prj->shortname));
array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url); return new Pluf_HTTP_Response_Redirect($url);
} }
$q = $request->REQUEST['q'];
$title = sprintf(__('Search Issues - %s'), $q); $tag = null;
$issues = new Pluf_Search_ResultSet(IDF_Search::mySearch($q, $prj, 'IDF_Issue')); if ($tag_id !== null) {
if (count($issues) > 100) { $tag = Pluf_Shortcuts_GetObjectOr404('IDF_Tag', $tag_id);
// no more than 100 results as we do not care
$issues->results = array_slice($issues->results, 0, 100);
} }
$title = sprintf(__('Search issues - %s'), $query);
if ($status === 'closed') {
$title = sprintf(__('Search closed issues - %s'), $query);
}
// using Plufs ResultSet implementation here is inefficient, because
// it makes a SELECT for each item and does not allow for further
// filtering neither, so we just return the ids and filter by them
// and other things in the next round
$results = IDF_Search::mySearch($query, $prj, 'IDF_Issue');
$issue_ids = array(0);
foreach ($results as $result) {
$issue_ids[] = $result['model_id'];
}
$otags = $prj->getTagIdsByStatus($status);
if (count($otags) == 0) $otags[] = 0;
$sql = new Pluf_SQL(
'id IN ('.implode(',', $issue_ids).') '.
'AND status IN ('.implode(', ', $otags).') '.
($tag_id !== null ? 'AND idf_tag_id='.$tag_id.' ' : '')
);
$model = new IDF_Issue();
$issues = $model->getList(array('filter' => $sql->gen(), 'view' => 'join_tags'));
// we unfortunately loose the original sort order,
// so we manually have to apply it here again
$sorted_issues = new ArrayObject();
$filtered_issue_ids = array(0);
foreach ($issue_ids as $issue_id) {
foreach ($issues as $issue) {
if ($issue->id != $issue_id)
continue;
if (array_key_exists($issue_id, $sorted_issues))
continue;
$sorted_issues[$issue_id] = $issue;
$filtered_issue_ids[] = $issue_id;
}
}
$pag = new Pluf_Paginator(); $pag = new Pluf_Paginator();
$pag->items = $issues;
$pag->class = 'recent-issues'; $pag->class = 'recent-issues';
$pag->item_extra_props = array('project_m' => $prj, $pag->items = $sorted_issues;
'shortname' => $prj->shortname, $pag->item_extra_props = array(
'current_user' => $request->user); 'project_m' => $prj,
'shortname' => $prj->shortname,
'current_user' => $request->user
);
$pag->summary = __('This table shows the found issues.'); $pag->summary = __('This table shows the found issues.');
$pag->action = array('IDF_Views_Issue::search', array($prj->shortname), array('q'=> $q));
$pag->extra_classes = array('a-c', '', 'a-c', ''); $pag->extra_classes = array('a-c', '', 'a-c', '');
$list_display = array( $pag->configure(array(
'id' => __('Id'), 'id' => __('Id'),
array('summary', 'IDF_Views_Issue_SummaryAndLabels', __('Summary')), array('summary', 'IDF_Views_Issue_SummaryAndLabels', __('Summary')),
array('status', 'IDF_Views_Issue_ShowStatus', __('Status')), array('status', 'IDF_Views_Issue_ShowStatus', __('Status')),
array('modif_dtime', 'Pluf_Paginator_DateAgo', __('Last Updated')), array('modif_dtime', 'Pluf_Paginator_DateAgo', __('Last Updated')),
); ));
$pag->configure($list_display); // disable paginating
$pag->items_per_page = 100; $pag->items_per_page = PHP_INT_MAX;
$pag->no_results_text = __('No issues were found.'); $pag->no_results_text = __('No issues were found.');
$pag->setFromRequest($request); $pag->setFromRequest($request);
$params = array('page_title' => $title,
'issues' => $pag,
'q' => $q,
);
return Pluf_Shortcuts_RenderToResponse('idf/issues/search.html', $params, $request);
if ($tag_id === null) {
$pag->action = array('IDF_Views_Issue::searchStatus',
array($prj->shortname, $status),
array('q'=> $query),
);
} else {
$pag->action = array('IDF_Views_Issue::searchLabel',
array($prj->shortname, $tag_id, $status),
array('q'=> $query),
);
}
// get stats about the issues
$open = $prj->getIssueCountByStatus('open', $tag, $issue_ids);
$closed = $prj->getIssueCountByStatus('closed', $tag, $issue_ids);
// query the available tags for this search result
$all_tags = $prj->getTagsByIssues($filtered_issue_ids);
$grouped_tags = array();
foreach ($all_tags as $atag) {
// group by class
if (!array_key_exists($atag->class, $grouped_tags)) {
$grouped_tags[$atag->class] = array();
}
$grouped_tags[$atag->class][] = $atag;
}
$params = array(
'page_title' => $title,
'issues' => $pag,
'query' => $query,
'status' => $status,
'open' => $open,
'closed' => $closed,
'tag' => $tag,
'all_tags' => $grouped_tags,
);
return Pluf_Shortcuts_RenderToResponse('idf/issues/search.html', $params, $request);
} }
public $view_precond = array('IDF_Precondition::accessIssues'); public $view_precond = array('IDF_Precondition::accessIssues');
@ -615,6 +732,13 @@ class IDF_Views_Issue
{ {
$prj = $request->project; $prj = $request->project;
$status = $match[2]; $status = $match[2];
if (mb_strtolower($status) == 'open') {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::index',
array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url);
}
$title = sprintf(__('%s Closed Issues'), (string) $prj); $title = sprintf(__('%s Closed Issues'), (string) $prj);
// Get stats about the issues // Get stats about the issues
$open = $prj->getIssueCountByStatus('open'); $open = $prj->getIssueCountByStatus('open');

View File

@ -152,13 +152,11 @@ class IDF_Views_Wiki
$pag->items_per_page = 25; $pag->items_per_page = 25;
$pag->no_results_text = __('No documentation pages were found.'); $pag->no_results_text = __('No documentation pages were found.');
$pag->setFromRequest($request); $pag->setFromRequest($request);
$tags = $prj->getTagCloud('wiki');
return Pluf_Shortcuts_RenderToResponse('idf/wiki/index.html', return Pluf_Shortcuts_RenderToResponse('idf/wiki/index.html',
array( array(
'page_title' => $title, 'page_title' => $title,
'label' => $tag, 'label' => $tag,
'pages' => $pag, 'pages' => $pag,
'tags' => $tags,
'dlabel' => $dtag, 'dlabel' => $dtag,
), ),
$request); $request);

View File

@ -128,6 +128,16 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/search/$#',
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'search'); 'method' => 'search');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/search/status/(\w+)/$#',
'base' => $base,
'model' => 'IDF_Views_Issue',
'method' => 'searchStatus');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/search/label/(\d+)/(\w+)/$#',
'base' => $base,
'model' => 'IDF_Views_Issue',
'method' => 'searchLabel');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/(\d+)/$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/(\d+)/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
@ -153,10 +163,10 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/create/$#',
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'create'); 'method' => 'create');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/my/(\w+)/$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/(.*)/(\w+)/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'myIssues'); 'method' => 'userIssues');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/attachment/(\d+)/(.*)$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/attachment/(\d+)/(.*)$#',
'base' => $base, 'base' => $base,

View File

@ -4,10 +4,10 @@
<div id="sub-tabs"> <div id="sub-tabs">
<a {if $inSummaryIssues}class="active" {/if}href="{url 'IDF_Views_Issue::summary', array($project.shortname)}">{trans 'Summary'}</a> <a {if $inSummaryIssues}class="active" {/if}href="{url 'IDF_Views_Issue::summary', array($project.shortname)}">{trans 'Summary'}</a>
| <a {if $inOpenIssues}class="active" {/if}href="{url 'IDF_Views_Issue::index', array($project.shortname)}">{trans 'Open Issues'}</a> | <a {if $inOpenIssues}class="active" {/if}href="{url 'IDF_Views_Issue::index', array($project.shortname)}">{trans 'Open Issues'}</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 !$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::userIssues', array($project.shortname, $user.login, 'submit')}">{trans 'My Issues'}</a>
| <a {if $inWatchList}class="active" {/if}href="{url 'IDF_Views_Issue::watchList', array($project.shortname, 'open')}">{trans 'My watch list'}</a>{/if} | | <a {if $inWatchList}class="active" {/if}href="{url 'IDF_Views_Issue::watchList', array($project.shortname, 'open')}">{trans 'My watch list'}</a>{/if} |
<form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get"> <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 accesskey="4" type="text" value="{$query}" name="q" size="20" />
<input type="submit" name="s" value="{trans 'Search'}" /> <input type="submit" name="s" value="{trans 'Search'}" />
</form> </form>
{if $inIssue} | {if $inIssue} |

View File

@ -8,16 +8,15 @@
{/block} {/block}
{block context} {block context}
<p><strong>{trans 'Label:'}</strong>
{aurl 'url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'open')}
<a href="{$url}" class="label"><strong>{$label.class}:</strong>{$label.name}</a></p>
{aurl 'open_url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'open')} {aurl 'open_url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'open')}
{aurl 'closed_url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'closed')} {aurl 'closed_url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'closed')}
{blocktrans}<p><strong>Open issues:</strong> <a href="{$open_url}">{$open}</a></p> {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> <p><strong>Closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p>
{/blocktrans}{if $completion} {/blocktrans}
<p><strong>{trans 'Label:'}</strong>
{aurl 'url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'open')}
<a href="{$url}" class="label"><strong>{$label.class}:</strong>{$label.name}</a></p>
{if $completion}
<p><strong>{trans 'Completion:'}</strong> {$completion}</p> <p><strong>{trans 'Completion:'}</strong> {$completion}</p>
{/if} {/if}
{/block} {/block}

View File

@ -13,6 +13,5 @@
{blocktrans}<p><strong>Open issues:</strong> <a href="{$open_url}">{$open}</a></p> {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} <p><strong>Closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p>{/blocktrans}
{assign $cloud_url = 'IDF_Views_Issue::listLabel'} {assign $cloud_url = 'IDF_Views_Issue::listLabel'}
{assign $cloud = 'issues'}
{include 'idf/tags-cloud.html'} {include 'idf/tags-cloud.html'}
{/block} {/block}

View File

@ -8,5 +8,25 @@
{/block} {/block}
{block context} {block context}
<p><strong>{trans 'Found issues:'}</strong> {$issues.nb_items}</p> {aurl 'open_url', 'IDF_Views_Issue::searchStatus', array($project.shortname, 'open'), array('q' => $query)}
{aurl 'closed_url', 'IDF_Views_Issue::searchStatus', array($project.shortname, 'closed'), array('q' => $query)}
{if $tag != null}
{aurl 'open_url', 'IDF_Views_Issue::searchLabel', array($project.shortname, $tag.id, 'open'), array('q' => $query)}
{aurl 'closed_url', 'IDF_Views_Issue::searchLabel', array($project.shortname, $tag.id, 'closed'), array('q' => $query)}
{/if}
{blocktrans}
<p><strong>Found open issues:</strong> <a href="{$open_url}">{$open}</a></p>
<p><strong>Found closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p>{/blocktrans}
{if $tag !== null}
{blocktrans}<p><strong>Label:</strong>
<a href="{$open_url}" class="label"><strong>{$tag.class}:</strong>{$tag.name}</a></p>{/blocktrans}
{else}
{* yes, this is duplicated from tags-cloud.html, but the code there cannot be easily overridden *}
<div id="tagscloud" class="smaller"><dl>{foreach $all_tags as $class => $labels}
<dt class="label">{$class}</dt>
{foreach $labels as $idx => $label}
{aurl 'url', 'IDF_Views_Issue::searchLabel', array($project.shortname, $label.id, $status), array('q'=> $query)}
<dd><a href="{$url}" class="label">{$label.name}{if $idx != count($labels) - 1},{/if}</a></dd>
{/foreach}{/foreach}</dl></p>
{/if}
{/block} {/block}

View File

@ -71,7 +71,12 @@
<tbody> <tbody>
{foreach $ownerStatistics as $key => $value} {foreach $ownerStatistics as $key => $value}
<tr> <tr>
<td class="name">{$key}</td> <td class="name">
{if !empty($value[2])}
{aurl 'url', 'IDF_Views_Issue::userIssues', array($project.shortname, $value[2], 'owner')}
<a href="{$url}">{$key}</a>
{else}{$key}{/if}
</td>
<td class="count">{$value[0]}</td> <td class="count">{$value[0]}</td>
<td class="graph"> <td class="graph">
<table class='graph'> <table class='graph'>

View File

@ -1,5 +1,5 @@
{extends "idf/issues/base.html"} {extends "idf/issues/base.html"}
{block docclass}yui-t2{assign $inMyIssues = true}{/block} {block docclass}yui-t2{if $user.login == $login}{assign $inMyIssues = true}{/if}{/block}
{block body} {block body}
{$issues.render} {$issues.render}
{if !$user.isAnonymous()} {if !$user.isAnonymous()}
@ -8,10 +8,10 @@
{/block} {/block}
{block context} {block context}
{aurl 'owner_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'owner')} {aurl 'owner_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'owner')}
{aurl 'submit_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')} {aurl 'submit_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'submit')}
{aurl 'owner_closed_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'ownerclosed')} {aurl 'owner_closed_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'ownerclosed')}
{aurl 'submit_closed_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submitclosed')} {aurl 'submit_closed_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'submitclosed')}
<p><strong>{trans 'Submitted issues:'}</strong> <a href="{$submit_url}">{$nb_submit}</a> <p><strong>{trans 'Submitted issues:'}</strong> <a href="{$submit_url}">{$nb_submit}</a>
{if $nb_submit_closed}<br /><span class="helptext">{blocktrans $nb_submit_closed}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{plural}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{/blocktrans}</span>{/if}</p> {if $nb_submit_closed}<br /><span class="helptext">{blocktrans $nb_submit_closed}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{plural}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{/blocktrans}</span>{/if}</p>
{if $nb_owner > 0} {if $nb_owner > 0}

View File

@ -17,7 +17,7 @@
{assign $submitter_data = $c.get_submitter_data()} {assign $submitter_data = $c.get_submitter_data()}
<div class="issue-comment{if $i == 0} issue-comment-first{/if}{if $i == ($nc-1)} issue-comment-last{/if}" id="ic{$c.id}"> <div class="issue-comment{if $i == 0} issue-comment-first{/if}{if $i == ($nc-1)} issue-comment-last{/if}" id="ic{$c.id}">
{if $submitter_data.avatar != ''} {if $submitter_data.avatar != ''}
<img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{media}/upload/avatars/{$submitter_data.avatar}" alt=" " /> <img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{upload}/avatars/{$submitter_data.avatar}" alt=" " />
{else} {else}
<img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " /> <img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " />
{/if} {/if}

View File

@ -10,8 +10,8 @@
{if $hasWikiAccess}{hotkey 'Shift+o', 'IDF_Views_Wiki::index', array($project.shortname)}{/if} {if $hasWikiAccess}{hotkey 'Shift+o', 'IDF_Views_Wiki::index', array($project.shortname)}{/if}
{if $hasSourceAccess}{hotkey 'Shift+s', 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}{/if} {if $hasSourceAccess}{hotkey 'Shift+s', 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}{/if}
{if $hasIssuesAccess and !$user.isAnonymous()} {if $hasIssuesAccess and !$user.isAnonymous()}
{hotkey 'Shift+m', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')} {hotkey 'Shift+m', 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'submit')}
{hotkey 'Shift+w', 'IDF_Views_Issue::myIssues', array($project.shortname, 'owner')} {hotkey 'Shift+w', 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'owner')}
{/if}{/if} //--> {/if}{/if} //-->
</script> </script>

View File

@ -4,7 +4,7 @@
<div id="sub-tabs"> <div id="sub-tabs">
<a {if $inOpenReviews}class="active" {/if}href="{url 'IDF_Views_Review::index', array($project.shortname)}">{trans 'Open Reviews'}</a> {* <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} | {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::userIssues', array($project.shortname, $user.login, 'submit')}">{trans 'My Issues'}</a>{/if} |
<form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get"> <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 accesskey="4" type="text" value="{$q}" name="q" size="20" />
<input type="submit" name="s" value="{trans 'Search'}" /> <input type="submit" name="s" value="{trans 'Search'}" />

View File

@ -5,17 +5,4 @@
{if !$user.isAnonymous()} {if !$user.isAnonymous()}
{aurl 'url', 'IDF_Views_Review::create', array($project.shortname)} {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 'Start Code Review'}</a></p>{/if} <p><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/add.png'}" alt="+" align="bottom" /></a> <a href="{$url}">{trans 'Start Code Review'}</a></p>{/if}
{/block} {/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}

View File

@ -109,7 +109,7 @@ to propose more contributions</strong>.
{foreach $comments as $c}{ashowuser 'submitter', $c.get_submitter(), $request}{assign $submitter = $c.get_submitter()}{assign $submitter_data = $c.get_submitter_data()} {foreach $comments as $c}{ashowuser 'submitter', $c.get_submitter(), $request}{assign $submitter = $c.get_submitter()}{assign $submitter_data = $c.get_submitter_data()}
<div class="issue-comment{if $i == 1} issue-comment-first{/if}{if $i == ($nc)} issue-comment-last{/if}" id="ic{$c.id}"> <div class="issue-comment{if $i == 1} issue-comment-first{/if}{if $i == ($nc)} issue-comment-last{/if}" id="ic{$c.id}">
{if $submitter_data.avatar != ''} {if $submitter_data.avatar != ''}
<img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{media}/upload/avatars/{$submitter_data.avatar}" alt=" " /> <img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{upload}/avatars/{$submitter_data.avatar}" alt=" " />
{else} {else}
<img style="float:right; position: relative;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " /> <img style="float:right; position: relative;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " />
{/if} {/if}

View File

@ -1,8 +1,6 @@
{assign $class = ''}{assign $i = 0} <div id="tagscloud" class="smaller"><dl>{foreach $project.getTagCloud($cloud) as $class => $labels}
<div id="tagscloud" class="smaller"><dl>{foreach $project.getTagCloud($cloud) as $label} <dt class="label">{$class}</dt>
{foreach $labels as $idx => $label}
{aurl 'url', $cloud_url, array($project.shortname, $label.id, 'open')} {aurl 'url', $cloud_url, array($project.shortname, $label.id, 'open')}
{if $class != $label.class}<dt class="label">{$label.class}</dt>{assign $i = 0}{/if} <dd><a href="{$url}" class="label">{$label.name}{if $idx != count($labels) - 1},{/if}</a></dd>
<dd><a href="{$url}" class="label">{$label.name},</a></dd> {/foreach}{/foreach}</dl></p>
{assign $class = $label.class}
{assign $i = $i + 1}
{/foreach}</dl></p>

View File

@ -3,7 +3,7 @@
<table class="form" summary=""> <table class="form" summary="">
<tr> <tr>
<th style="text-align: right">{if $user_data.avatar != ''} <th style="text-align: right">{if $user_data.avatar != ''}
<img style="max-height: 60px; max-width: 60px;" src="{media}/upload/avatars/{$user_data.avatar}" alt=" " /> <img style="max-height: 60px; max-width: 60px;" src="{upload}/avatars/{$user_data.avatar}" alt=" " />
{else} {else}
<img src="http://www.gravatar.com/avatar/{$member.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " /> <img src="http://www.gravatar.com/avatar/{$member.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " />
{/if} {/if}

View File

@ -1,5 +1,10 @@
{extends "idf/wiki/base.html"} {extends "idf/wiki/base.html"}
{block extraheader}{if $oldrev}<meta name="ROBOTS" content="NOINDEX" />{/if}{/block}
{block extraheader}
{if $oldrev}<meta name="ROBOTS" content="NOINDEX" />{/if}
<link rel="stylesheet" type="text/css" media="print" href="{media '/idf/css/print-wiki.css'}" />
{/block}
{block docclass}yui-t3{assign $inView=true}{/block} {block docclass}yui-t3{assign $inView=true}{/block}
{block body} {block body}

View File

@ -0,0 +1,32 @@
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
#hd, #ft, #context {
display: none;
}
.yui-t3 #yui-main .yui-b {
margin: 0;
}
#wiki-toc {
float: none;
}