Save a link to the latest activity value in the project model and

render a simple green bar as activity measure in the project list
view.
This commit is contained in:
Thomas Keller 2011-12-24 01:07:25 +01:00
parent 6875e62942
commit 608e7a40e4
8 changed files with 193 additions and 60 deletions

View File

@ -0,0 +1,40 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
function IDF_Migrations_24CurrentProjectActivity_up($params=null)
{
$engine = Pluf::f('db_engine');
$db = Pluf::db();
if ($engine === 'PostgreSQL') {
$db->execute('ALTER TABLE '.$db->pfx.'idf_projects ADD COLUMN current_activity INTEGER');
} else if ($engine === 'MySQL') {
$db->execute('ALTER TABLE '.$db->pfx.'idf_projects ADD COLUMN current_activity MEDIUMINT');
}
}
function IDF_Migrations_24CurrentProjectActivity_down($params=null)
{
$db = Pluf::db();
$db->execute('ALTER TABLE '.$db->pfx.'idf_projects DROP COLUMN current_activity');
}

View File

@ -100,6 +100,26 @@ class IDF_Project extends Pluf_Model
'verbose' => __('private'),
'default' => 0,
),
'current_activity' =>
array(
'type' => 'Pluf_DB_Field_Foreignkey',
'model' => 'IDF_ProjectActivity',
'blank' => true,
'verbose' => __('current project activity'),
),
);
$table = $this->_con->pfx.'idf_projectactivities';
$this->_a['views'] = array(
'join_activities' =>
array(
'join' => 'LEFT JOIN '.$table
.' ON current_activity='.$table.'.id',
'select' => $this->getSelect().', date, value',
'props' => array(
'date' => 'current_activity_date',
'value' => 'current_activity_value'
),
),
);
}

View File

@ -64,4 +64,15 @@ class IDF_ProjectActivity extends Pluf_Model
),
);
}
function postSave($create=false)
{
$prj = $this->get_project();
$sql = new Pluf_SQL('project=%s', array($prj->id));
$latest = Pluf::factory('IDF_ProjectActivity')->getOne(array('filter' => $sql->gen(), 'order' => 'date desc'));
if ($prj->current_activity != $latest->id) {
$prj->current_activity = $latest;
$prj->update();
}
}
}

View File

@ -31,19 +31,29 @@ Pluf::loadFunction('Pluf_Shortcuts_GetFormForModel');
*/
class IDF_Views
{
/**
* The index view.
*/
public function index($request, $match)
{
// TODO: add a switch here later on to determine whether the project list
// or a custom start page should be displayed
return $this->listProjects($request, $match);
}
/**
* List all the projects managed by InDefero.
*
* Only the public projects are listed or the private with correct
* rights.
*/
public function index($request, $match, $api=false)
public function listProjects($request, $match, $api=false)
{
$projects = self::getProjects($request->user);
$stats = self::getProjectsStatistics($projects);
if ($api == true) return $projects;
return Pluf_Shortcuts_RenderToResponse('idf/index.html',
return Pluf_Shortcuts_RenderToResponse('idf/listProjects.html',
array('page_title' => __('Projects'),
'projects' => $projects,
'stats' => new Pluf_Template_ContextVars($stats)),
@ -334,34 +344,40 @@ class IDF_Views
{
$db =& Pluf::db();
$false = Pluf_DB_BooleanToDb(false, $db);
if ($user->isAnonymous()) {
$sql = sprintf('%s=%s', $db->qn('private'), $false);
return Pluf::factory('IDF_Project')->getList(array('filter'=> $sql,
'order' => 'name ASC'));
}
if ($user->administrator) {
return Pluf::factory('IDF_Project')->getList(array('order' => 'name ASC'));
}
// grab the list of projects where the user is admin, member
// or authorized
$sql = new Pluf_SQL(1);
if ($user->isAnonymous())
{
$authSql = new Pluf_SQL('private=%s', $false);
$sql->SAnd($authSql);
} else
if (!$user->administrator) {
// grab the list of projects where the user is admin,
// member or authorized
$perms = array(
Pluf_Permission::getFromString('IDF.project-member'),
Pluf_Permission::getFromString('IDF.project-owner'),
Pluf_Permission::getFromString('IDF.project-authorized-user')
);
$sql = new Pluf_SQL("model_class='IDF_Project' AND owner_class='Pluf_User' AND owner_id=%s AND negative=".$false, $user->id);
$rows = Pluf::factory('Pluf_RowPermission')->getList(array('filter' => $sql->gen()));
$permSql = new Pluf_SQL("model_class='IDF_Project' AND owner_class='Pluf_User' AND owner_id=%s AND negative=".$false, $user->id);
$rows = Pluf::factory('Pluf_RowPermission')->getList(array('filter' => $permSql->gen()));
$sql = sprintf('%s=%s', $db->qn('private'), $false);
$authSql = new Pluf_SQL('private=%s', $false);
if ($rows->count() > 0) {
$ids = array();
foreach ($rows as $row) {
$ids[] = $row->model_id;
}
$sql .= sprintf(' OR id IN (%s)', implode(', ', $ids));
$authSql->SOr(new Pluf_SQL(sprintf('id IN (%s)', implode(', ', $ids))));
}
return Pluf::factory('IDF_Project')->getList(array('filter' => $sql,
'order' => 'name ASC'));
$sql->SAnd($authSql);
}
return Pluf::factory('IDF_Project')->getList(array(
'filter'=> $sql->gen(),
'view' => 'join_activities',
'order' => 'name ASC'
));
}
/**

View File

@ -93,8 +93,7 @@ class IDF_Views_Api
public function projectIndex($request, $match)
{
$view = new IDF_Views();
$projects = $view->index($request, $match, true);
$projects = IDF_Views::getProjects($request->user);
$data = array();
foreach ($projects as $p) {

View File

@ -29,6 +29,11 @@ $ctl[] = array('regex' => '#^/$#',
'model' => 'IDF_Views',
'method' => 'index');
$ctl[] = array('regex' => '#^/label/(\d+)/$#',
'base' => $base,
'model' => 'IDF_Views',
'method' => 'listProjects');
$ctl[] = array('regex' => '#^/login/$#',
'base' => $base,
'model' => 'IDF_Views',

View File

@ -10,30 +10,41 @@
<p><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/add.png'}" alt="+" align="bottom" /></a> <a href="{$url}">{trans 'Create Project'}</a></p>{/if}
{else}
{foreach $projects as $p}
<div class="p-list-img">
<div class="p-list-prj">
<div class="logo">
<a href="{url 'IDF_Views_Project::home', array($p.shortname)}">
<img src="{url 'IDF_Views_Project::logo', array($p.shortname)}" alt="{trans 'Project logo'}" />
</a>
{if $p.private}
<div class="p-list-private">
<div class="private">
<a href="{url 'IDF_Views_Project::home', array($p.shortname)}">
<img style="float:right" src="{media '/idf/img/lock.png'}" alt="{trans 'Private project'}" />
</a>
</div>
{/if}
{if $p.current_activity_value}
<div class="activity" title="{trans 'Project activity'}"><div class="bar" style="width: {$p.current_activity_value * 100}%"></div></div>
{/if}
</div>
<div class="p-list-prj">
<p>
<a href="{url 'IDF_Views_Project::home', array($p.shortname)}"><strong>{$p}</strong></a>
{assign $url = $p.external_project_url}
{if $url != ''}
<a href="{$url}" target="_blank" class="external-link" title="{trans 'External link to project'}" />&nbsp;</a>
{/if}
{if $p.private} - {trans 'Private project'}{/if}
{if $p.private} - <span class="smaller">{trans 'Private project'}</span>{/if}
</p>
<p class="smaller">{$p.shortdesc}</p>
<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}
{if $idx != 0}, {/if}
<a class="label" href="{url 'IDF_Views::listProjects', array($tag->id)}">{$tag}</a>
{/foreach}
{/if}
</p>
<p>{$p.shortdesc}</p>
</div>
<div style="clear: both"></div>
{/foreach}
{/if}
{/block}

View File

@ -1214,27 +1214,58 @@ span.scm-action.property-changed {
}
/*
* Project list on index
* Project list
*/
div.p-list-img {
div.p-list-prj {
width: 24em;
min-height: 5em;
float: left;
height: 32px;
margin-top: .5em;
margin: 0 1em 0.5em 0;
}
div.p-list-prj {
div.p-list-prj div.logo {
float: left;
margin: .5em 0 .5em .8em;
width: 32px;
height: 32px;
position: relative;
}
div.p-list-prj div.logo img {
max-width: 32px;
max-height: 32px;
}
div.p-list-prj div.logo .private {
top: 18px;
right: -3px;
position: absolute;
}
div.p-list-prj div.logo .activity {
height: 4px;
width: 32px;
margin-top: 5px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
background: #E6E6E6;
}
div.p-list-prj div.logo .activity .bar {
background: #A5E26A;
height: 100%;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
}
div.p-list-prj p {
margin: 0px;
margin: 0;
margin-left: 42px;
}
div.p-list-private {
bottom: 16px;
right: -3px;
position: relative;
div.p-list-prj .smaller {
font-size: 85%;
}
a.external-link {