Add filtering and sorting capabilities to the project list page and
also render the project activity with a small bar graph below the logo image. Sanitize the tagcloud css.
This commit is contained in:
parent
608e7a40e4
commit
6e305eb541
@ -51,4 +51,23 @@ class IDF_Forge
|
||||
public function setProjectLabels($labels) {
|
||||
$this->conf->setVal('project_labels', $labels);
|
||||
}
|
||||
|
||||
public function getProjectLabelsWithCounts() {
|
||||
$sql = new Pluf_SQL('project=0');
|
||||
$tagList = Pluf::factory('IDF_Tag')->getList(array(
|
||||
'filter' => $sql->gen(),
|
||||
'view' => 'join_projects',
|
||||
'order' => 'class ASC, lcname ASC'
|
||||
));
|
||||
|
||||
$tags = array();
|
||||
foreach ($tagList as $tag) {
|
||||
// group by class
|
||||
if (!array_key_exists($tag->class, $tags)) {
|
||||
$tags[$tag->class] = array();
|
||||
}
|
||||
$tags[$tag->class][] = $tag;
|
||||
}
|
||||
return $tags;
|
||||
}
|
||||
}
|
@ -108,13 +108,15 @@ class IDF_Project extends Pluf_Model
|
||||
'verbose' => __('current project activity'),
|
||||
),
|
||||
);
|
||||
$table = $this->_con->pfx.'idf_projectactivities';
|
||||
$activityTable = $this->_con->pfx.'idf_projectactivities';
|
||||
$tagTable = $this->_con->pfx.'idf_project_idf_tag_assoc';
|
||||
$this->_a['views'] = array(
|
||||
'join_activities' =>
|
||||
'join_activities_and_tags' =>
|
||||
array(
|
||||
'join' => 'LEFT JOIN '.$table
|
||||
.' ON current_activity='.$table.'.id',
|
||||
'join' => 'LEFT JOIN '.$activityTable.' ON current_activity='.$activityTable.'.id '
|
||||
.'LEFT JOIN '.$tagTable.' ON idf_project_id='.$this->getSqlTable().'.id',
|
||||
'select' => $this->getSelect().', date, value',
|
||||
'group' => $this->getSqlTable().'.id',
|
||||
'props' => array(
|
||||
'date' => 'current_activity_date',
|
||||
'value' => 'current_activity_value'
|
||||
|
@ -75,6 +75,18 @@ class IDF_Tag extends Pluf_Model
|
||||
),
|
||||
);
|
||||
|
||||
$table = $this->_con->pfx.'idf_project_idf_tag_assoc';
|
||||
$this->_a['views'] = array(
|
||||
'join_projects' =>
|
||||
array(
|
||||
'join' => 'LEFT JOIN '.$table
|
||||
.' ON idf_tag_id=id',
|
||||
'select' => $this->getSelect().',COUNT(idf_project_id) as project_count',
|
||||
'group' => 'idf_tag_id',
|
||||
'props' => array('project_count' => 'project_count'),
|
||||
),
|
||||
);
|
||||
|
||||
$this->_a['idx'] = array(
|
||||
'lcname_idx' =>
|
||||
array(
|
||||
|
@ -38,6 +38,7 @@ class IDF_Views
|
||||
{
|
||||
// TODO: add a switch here later on to determine whether the project list
|
||||
// or a custom start page should be displayed
|
||||
$match = array('', 'all', 'name');
|
||||
return $this->listProjects($request, $match);
|
||||
}
|
||||
|
||||
@ -47,15 +48,30 @@ class IDF_Views
|
||||
* Only the public projects are listed or the private with correct
|
||||
* rights.
|
||||
*/
|
||||
public function listProjects($request, $match, $api=false)
|
||||
public function listProjects($request, $match)
|
||||
{
|
||||
$projects = self::getProjects($request->user);
|
||||
$stats = self::getProjectsStatistics($projects);
|
||||
list(, $tagId, $order) = $match;
|
||||
|
||||
$tag = false;
|
||||
if ($tagId !== 'all') {
|
||||
$tag = Pluf::factory('IDF_Tag')->get($match[1]);
|
||||
// ignore non-global tags
|
||||
if ($tag !== false && $tag->project > 0) {
|
||||
$tag = false;
|
||||
}
|
||||
}
|
||||
$order = in_array($order, array('name', 'activity')) ? $order : 'name';
|
||||
|
||||
$projects = self::getProjects($request->user, $tag, $order);
|
||||
$stats = self::getProjectsStatistics($projects);
|
||||
$projectLabels = IDF_Forge::instance()->getProjectLabelsWithCounts();
|
||||
|
||||
if ($api == true) return $projects;
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/listProjects.html',
|
||||
array('page_title' => __('Projects'),
|
||||
'projects' => $projects,
|
||||
'projectLabels' => $projectLabels,
|
||||
'tag' => $tag,
|
||||
'order' => $order,
|
||||
'stats' => new Pluf_Template_ContextVars($stats)),
|
||||
$request);
|
||||
}
|
||||
@ -335,16 +351,20 @@ class IDF_Views
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of projects accessible for the user.
|
||||
* Returns a list of projects accessible for the user and optionally filtered by tag.
|
||||
*
|
||||
* @param Pluf_User
|
||||
* @param IDF_Tag
|
||||
* @return ArrayObject IDF_Project
|
||||
*/
|
||||
public static function getProjects($user)
|
||||
public static function getProjects($user, $tag = false, $order = 'name')
|
||||
{
|
||||
$db =& Pluf::db();
|
||||
$false = Pluf_DB_BooleanToDb(false, $db);
|
||||
$sql = new Pluf_SQL(1);
|
||||
if ($tag !== false) {
|
||||
$sql->SAnd(new Pluf_SQL('idf_tag_id=%s', $tag->id));
|
||||
}
|
||||
|
||||
if ($user->isAnonymous())
|
||||
{
|
||||
@ -373,10 +393,14 @@ class IDF_Views
|
||||
$sql->SAnd($authSql);
|
||||
}
|
||||
|
||||
$orderTypes = array(
|
||||
'name' => 'name ASC',
|
||||
'activity' => 'value DESC, name ASC',
|
||||
);
|
||||
return Pluf::factory('IDF_Project')->getList(array(
|
||||
'filter'=> $sql->gen(),
|
||||
'view' => 'join_activities',
|
||||
'order' => 'name ASC'
|
||||
'view' => 'join_activities_and_tags',
|
||||
'order' => $orderTypes[$order],
|
||||
));
|
||||
}
|
||||
|
||||
@ -397,7 +421,7 @@ class IDF_Views
|
||||
|
||||
// Count for each projects
|
||||
foreach ($projects as $p) {
|
||||
$pstats = $p->getStats ();
|
||||
$pstats = $p->getStats();
|
||||
$forgestats['downloads'] += $pstats['downloads'];
|
||||
$forgestats['reviews'] += $pstats['reviews'];
|
||||
$forgestats['issues'] += $pstats['issues'];
|
||||
@ -405,9 +429,6 @@ class IDF_Views
|
||||
$forgestats['commits'] += $pstats['commits'];
|
||||
}
|
||||
|
||||
// Count projects
|
||||
$forgestats['projects'] = count($projects);
|
||||
|
||||
// Count members
|
||||
$sql = new Pluf_SQL('first_name != %s', array('---'));
|
||||
$forgestats['members'] = Pluf::factory('Pluf_User')
|
||||
|
@ -29,7 +29,7 @@ $ctl[] = array('regex' => '#^/$#',
|
||||
'model' => 'IDF_Views',
|
||||
'method' => 'index');
|
||||
|
||||
$ctl[] = array('regex' => '#^/label/(\d+)/$#',
|
||||
$ctl[] = array('regex' => '#^/label/(\w+)/(\w+)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views',
|
||||
'method' => 'listProjects');
|
||||
|
@ -22,11 +22,11 @@
|
||||
<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}
|
||||
<dl class="tagscloud smaller">{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>
|
||||
{/foreach}{/foreach}</dl>
|
||||
{/if}
|
||||
{/block}
|
||||
|
@ -38,9 +38,9 @@
|
||||
<p class="smaller">{trans 'Labels:'}
|
||||
{assign $tags = $p.get_tags_list()}
|
||||
{if count($tags) == 0}{trans 'n/a'}{else}
|
||||
{foreach $p.get_tags_list() as $idx => $tag}
|
||||
{foreach $p.get_tags_list() as $idx => $label}
|
||||
{if $idx != 0}, {/if}
|
||||
<a class="label" href="{url 'IDF_Views::listProjects', array($tag->id)}">{$tag}</a>
|
||||
<a class="label" href="{url 'IDF_Views::listProjects', array($label->id, $order)}">{$label}</a>
|
||||
{/foreach}
|
||||
{/if}
|
||||
</p>
|
||||
@ -48,18 +48,46 @@
|
||||
{/foreach}
|
||||
{/if}
|
||||
{/block}
|
||||
|
||||
{block context}
|
||||
<div id="stats" class="issue-submit-info">
|
||||
<h3 class="a-c">{trans 'Forge statistics'}</h3>
|
||||
<table>
|
||||
<tr><td class="right">{trans 'Projects:'}</td><td>{$stats.projects}</td></tr>
|
||||
<tr><td class="right">{trans 'Members:'}</td><td>{$stats.members}</td></tr>
|
||||
<tr><td class="right">{trans 'Issues:'}</td><td>{$stats.issues}</td></tr>
|
||||
<tr><td class="right">{trans 'Commits:'}</td><td>{$stats.commits}</td></tr>
|
||||
<tr><td class="right">{trans 'Documentations:'}</td><td>{$stats.docpages}</td></tr>
|
||||
<tr><td class="right">{trans 'Downloads:'}</td><td>{$stats.downloads}</td></tr>
|
||||
<tr><td class="right">{trans 'Code reviews:'}</td><td>{$stats.reviews}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<strong>{trans 'Filter projects by label'}</strong>
|
||||
{if count($projectLabels) == 0}
|
||||
<p class="smaller">{trans 'No projects with labels found.'}</p>
|
||||
{else}
|
||||
<dl class="tagscloud smaller">{foreach $projectLabels as $class => $labels}
|
||||
<dt class="label">{$class}</dt>
|
||||
{foreach $labels as $idx => $label}
|
||||
<dd><a href="{url 'IDF_Views::listProjects', array($label->id, $order)}" class="label" title="{blocktrans $label.project_count}1 project{plural}{$label.project_count} projects{/blocktrans}">{$label.name}{if $idx != count($labels) - 1},{/if}</a></dd>
|
||||
{/foreach}
|
||||
{/foreach}</dl>
|
||||
{if $tag}
|
||||
<p class="smaller"><a href="{url 'IDF_Views::listProjects', array('all', $order)}">
|
||||
{blocktrans}Remove filter for {$tag}{/blocktrans}</a></p>
|
||||
{/if}
|
||||
{/if}
|
||||
<br />
|
||||
|
||||
<strong>{trans 'Order'}</strong>
|
||||
{assign $labelPart = 'all'}
|
||||
{if $tag}{assign $labelPart = $tag->id}{/if}
|
||||
<p class="smaller">
|
||||
{if $order != 'name'}<a href="{url 'IDF_Views::listProjects', array($labelPart, 'name')}">{/if}
|
||||
{trans 'By name'}{if $order != 'name'}</a>{/if}
|
||||
–
|
||||
{if $order != 'activity'}<a href="{url 'IDF_Views::listProjects', array($labelPart, 'activity')}">{/if}
|
||||
{trans 'By activity'}{if $order != 'activity'}</a>{/if}
|
||||
</p>
|
||||
<br />
|
||||
|
||||
<strong>{trans 'Filtered project stats'}</strong>
|
||||
<dl class="statistics smaller">
|
||||
<dt>{trans 'Members:'}</dt><dd>{$stats.members}</dd>
|
||||
<dt>{trans 'Issues:'}</dt><dd>{$stats.issues}</dd>
|
||||
<dt>{trans 'Commits:'}</dt><dd>{$stats.commits}</dd>
|
||||
<dt>{trans 'Documentations:'}</dt><dd>{$stats.docpages}</dd>
|
||||
<dt>{trans 'Downloads:'}</dt><dd>{$stats.downloads}</dd>
|
||||
<dt>{trans 'Code reviews:'}</dt><dd>{$stats.reviews}</dd>
|
||||
</dl>
|
||||
{/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}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div id="tagscloud" class="smaller"><dl>{foreach $project.getTagCloud($cloud) as $class => $labels}
|
||||
<dl class="tagscloud smaller"><dl>{foreach $project.getTagCloud($cloud) as $class => $labels}
|
||||
<dt class="label">{$class}</dt>
|
||||
{foreach $labels as $idx => $label}
|
||||
{aurl 'url', $cloud_url, array($project.shortname, $label.id, 'open')}
|
||||
<dd><a href="{$url}" class="label">{$label.name}{if $idx != count($labels) - 1},{/if}</a></dd>
|
||||
{/foreach}{/foreach}</dl></p>
|
||||
{/foreach}{/foreach}</dl>
|
||||
|
@ -213,23 +213,37 @@ span.px-header-title a, span.px-header-title a:link, span.px-header-title a:visi
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue
|
||||
*/
|
||||
#tagscloud dl {
|
||||
margin: 0;
|
||||
dl.tagscloud,
|
||||
dl.statistics {
|
||||
margin: 0;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#tagscloud dt {
|
||||
dl.tagscloud dt {
|
||||
margin-top: .5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#tagscloud dd {
|
||||
dl.tagscloud dd {
|
||||
margin: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
dl.statistics dt {
|
||||
margin-top: .5em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
dl.statistics dd {
|
||||
margin: 0;
|
||||
float: right;
|
||||
margin-top: -1.2em;
|
||||
margin-right: 2em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue
|
||||
*/
|
||||
a.issue-c {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user