Merge branch 'feature.search-filter' of projects.ceondo.com:indefero into develop

This commit is contained in:
Thomas Keller 2011-10-04 12:46:28 +02:00
commit eb8fd0aa55
10 changed files with 217 additions and 67 deletions

View File

@ -132,7 +132,7 @@ class IDF_Project extends Pluf_Model
} }
return $projects[0]; return $projects[0];
} }
/** /**
* Returns the number of open/closed issues. * Returns the number of open/closed issues.
* *
@ -167,7 +167,7 @@ GROUP BY uid";
$key = ($v['id'] === '-1') ? null : $v['id']; $key = ($v['id'] === '-1') ? null : $v['id'];
$ownerStatistics[$key] = (int)$v['nb']; $ownerStatistics[$key] = (int)$v['nb'];
} }
arsort($ownerStatistics); arsort($ownerStatistics);
return $ownerStatistics; return $ownerStatistics;
@ -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

@ -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

@ -119,9 +119,11 @@ class IDF_Views_Issue
} }
// 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;
@ -431,45 +433,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');

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

@ -117,7 +117,7 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'index'); 'method' => 'index');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/summary/$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/summary/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
@ -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',

View File

@ -7,7 +7,7 @@
{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::myIssues', array($project.shortname, '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

@ -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

@ -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

@ -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>