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:
parent
6875e62942
commit
608e7a40e4
40
src/IDF/Migrations/24CurrentProjectActivity.php
Normal file
40
src/IDF/Migrations/24CurrentProjectActivity.php
Normal 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');
|
||||
}
|
@ -100,7 +100,27 @@ 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'
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
$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()));
|
||||
$sql = new Pluf_SQL(1);
|
||||
|
||||
$sql = sprintf('%s=%s', $db->qn('private'), $false);
|
||||
if ($rows->count() > 0) {
|
||||
$ids = array();
|
||||
foreach ($rows as $row) {
|
||||
$ids[] = $row->model_id;
|
||||
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')
|
||||
);
|
||||
$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()));
|
||||
|
||||
$authSql = new Pluf_SQL('private=%s', $false);
|
||||
if ($rows->count() > 0) {
|
||||
$ids = array();
|
||||
foreach ($rows as $row) {
|
||||
$ids[] = $row->model_id;
|
||||
}
|
||||
$authSql->SOr(new Pluf_SQL(sprintf('id IN (%s)', implode(', ', $ids))));
|
||||
}
|
||||
$sql .= sprintf(' OR id IN (%s)', implode(', ', $ids));
|
||||
$sql->SAnd($authSql);
|
||||
}
|
||||
return Pluf::factory('IDF_Project')->getList(array('filter' => $sql,
|
||||
'order' => 'name ASC'));
|
||||
|
||||
return Pluf::factory('IDF_Project')->getList(array(
|
||||
'filter'=> $sql->gen(),
|
||||
'view' => 'join_activities',
|
||||
'order' => 'name ASC'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,7 +29,7 @@
|
||||
* JSON instead of HTML.
|
||||
*
|
||||
* A special precondition is used to set the $request->user from the
|
||||
* _login, _hash and _salt parameters.
|
||||
* _login, _hash and _salt parameters.
|
||||
*/
|
||||
class IDF_Views_Api
|
||||
{
|
||||
@ -90,17 +90,16 @@ class IDF_Views_Api
|
||||
* List all the projects
|
||||
*/
|
||||
public $projectIndex_precond = array('IDF_Precondition::apiSetUser');
|
||||
|
||||
|
||||
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) {
|
||||
$data[] = array("shortname" => $p->shortname, "name" => $p->name, "shortdesc" => $p->shortdesc, "private" => $p->private);
|
||||
}
|
||||
|
||||
|
||||
$out = array();
|
||||
$out['message'] = 'success';
|
||||
$out['projects'] = $data;
|
||||
|
@ -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',
|
||||
|
@ -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">
|
||||
<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">
|
||||
<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}
|
||||
</div>
|
||||
<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="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>
|
||||
<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'}" /> </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}
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user