diff --git a/src/IDF/Migrations/24CurrentProjectActivity.php b/src/IDF/Migrations/24CurrentProjectActivity.php new file mode 100644 index 0000000..78483b1 --- /dev/null +++ b/src/IDF/Migrations/24CurrentProjectActivity.php @@ -0,0 +1,40 @@ +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'); +} diff --git a/src/IDF/Project.php b/src/IDF/Project.php index ef1d592..fc8a340 100644 --- a/src/IDF/Project.php +++ b/src/IDF/Project.php @@ -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' + ), + ), + ); } diff --git a/src/IDF/ProjectActivity.php b/src/IDF/ProjectActivity.php index d994a6a..8b067de 100644 --- a/src/IDF/ProjectActivity.php +++ b/src/IDF/ProjectActivity.php @@ -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(); + } + } } diff --git a/src/IDF/Views.php b/src/IDF/Views.php index 65f016f..bd1569f 100644 --- a/src/IDF/Views.php +++ b/src/IDF/Views.php @@ -32,18 +32,28 @@ 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' + )); } /** diff --git a/src/IDF/Views/Api.php b/src/IDF/Views/Api.php index ba78dec..c0e488d 100644 --- a/src/IDF/Views/Api.php +++ b/src/IDF/Views/Api.php @@ -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; diff --git a/src/IDF/conf/urls.php b/src/IDF/conf/urls.php index 16f1892..1755ae6 100644 --- a/src/IDF/conf/urls.php +++ b/src/IDF/conf/urls.php @@ -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', diff --git a/src/IDF/templates/idf/index.html b/src/IDF/templates/idf/index.html deleted file mode 100644 index fba3614..0000000 --- a/src/IDF/templates/idf/index.html +++ /dev/null @@ -1,54 +0,0 @@ -{extends "idf/base-simple.html"} -{block docclass}yui-t2{/block} -{block tabhome} class="active"{/block} -{block subtabs}{trans 'Projects'}{/block} -{block body} -{if $projects.count() == 0} -

{trans 'No projects managed with InDefero were found.'}

-{if $isAdmin} -{aurl 'url', 'IDF_Views_Admin::projectCreate'} -

+ {trans 'Create Project'}

{/if} -{else} -{foreach $projects as $p} -
- - {trans 'Project logo'} - - {if $p.private} -
- - {trans 'Private project'} - -
- {/if} -
-
-

- {$p} - {assign $url = $p.external_project_url} - {if $url != ''} -   - {/if} - {if $p.private} - {trans 'Private project'}{/if} -

-

{$p.shortdesc}

-
-
-{/foreach} -{/if} -{/block} -{block context} -
-

{trans 'Forge statistics'}

- - - - - - - - -
{trans 'Projects:'}{$stats.projects}
{trans 'Members:'}{$stats.members}
{trans 'Issues:'}{$stats.issues}
{trans 'Commits:'}{$stats.commits}
{trans 'Documentations:'}{$stats.docpages}
{trans 'Downloads:'}{$stats.downloads}
{trans 'Code reviews:'}{$stats.reviews}
-
-{/block} -{block foot}
Powered by InDefero,
a Céondo Ltd initiative.
{/block} diff --git a/src/IDF/templates/idf/listProjects.html b/src/IDF/templates/idf/listProjects.html new file mode 100644 index 0000000..52cb37b --- /dev/null +++ b/src/IDF/templates/idf/listProjects.html @@ -0,0 +1,65 @@ +{extends "idf/base-simple.html"} +{block docclass}yui-t2{/block} +{block tabhome} class="active"{/block} +{block subtabs}{trans 'Projects'}{/block} +{block body} +{if $projects.count() == 0} +

{trans 'No projects managed with InDefero were found.'}

+{if $isAdmin} +{aurl 'url', 'IDF_Views_Admin::projectCreate'} +

+ {trans 'Create Project'}

{/if} +{else} +{foreach $projects as $p} +
+ +

+ {$p} + {assign $url = $p.external_project_url} + {if $url != ''} +   + {/if} + {if $p.private} - {trans 'Private project'}{/if} +

+

{$p.shortdesc}

+

{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} + {$tag} + {/foreach} + {/if} +

+
+{/foreach} +{/if} +{/block} +{block context} +
+

{trans 'Forge statistics'}

+ + + + + + + + +
{trans 'Projects:'}{$stats.projects}
{trans 'Members:'}{$stats.members}
{trans 'Issues:'}{$stats.issues}
{trans 'Commits:'}{$stats.commits}
{trans 'Documentations:'}{$stats.docpages}
{trans 'Downloads:'}{$stats.downloads}
{trans 'Code reviews:'}{$stats.reviews}
+
+{/block} +{block foot}
Powered by InDefero,
a Céondo Ltd initiative.
{/block} diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css index 5260c69..7e6f860 100644 --- a/www/media/idf/css/style.css +++ b/www/media/idf/css/style.css @@ -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 p { - margin: 0px; +div.p-list-prj div.logo img { + max-width: 32px; + max-height: 32px; } -div.p-list-private { - bottom: 16px; +div.p-list-prj div.logo .private { + top: 18px; right: -3px; - position: relative; + 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: 0; + margin-left: 42px; +} + +div.p-list-prj .smaller { + font-size: 85%; } a.external-link {