From aa2868eb17e776371b342184b4f552a40518138a Mon Sep 17 00:00:00 2001
From: Patrick Georgi
Date: Fri, 19 Aug 2011 22:01:55 +0200
Subject: [PATCH 01/99] Add basic framework for web based repository access
/p/$project/source/repo/ is assigned to a method that
takes care of providing repository access.
For now, this results in an exception on all SCMs.
---
src/IDF/Scm.php | 5 +++++
src/IDF/Views/Source.php | 6 ++++++
src/IDF/conf/urls.php | 5 +++++
3 files changed, 16 insertions(+)
diff --git a/src/IDF/Scm.php b/src/IDF/Scm.php
index 5ebb8d9..350b073 100644
--- a/src/IDF/Scm.php
+++ b/src/IDF/Scm.php
@@ -497,5 +497,10 @@ class IDF_Scm
{
return 0;
}
+
+ public function repository($request, $match)
+ {
+ throw new Exception('This repository does not support web based repository access');
+ }
}
diff --git a/src/IDF/Views/Source.php b/src/IDF/Views/Source.php
index 1496e54..1a556a0 100644
--- a/src/IDF/Views/Source.php
+++ b/src/IDF/Views/Source.php
@@ -132,6 +132,12 @@ class IDF_Views_Source
$request);
}
+ public function repository($request, $match)
+ {
+ $scm = IDF_Scm::get($request->project);
+ return $scm->repository($request, $match);
+ }
+
public $treeBase_precond = array('IDF_Precondition::accessSource',
'IDF_Views_Source_Precondition::scmAvailable',
'IDF_Views_Source_Precondition::revisionValid');
diff --git a/src/IDF/conf/urls.php b/src/IDF/conf/urls.php
index 03779c0..d79c998 100644
--- a/src/IDF/conf/urls.php
+++ b/src/IDF/conf/urls.php
@@ -245,6 +245,11 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/source/changesrev/$#',
'model' => 'IDF_Views_Source_Svn',
'method' => 'changelogRev');
+$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/repo/(.*)$#',
+ 'base' => $base,
+ 'model' => 'IDF_Views_Source',
+ 'method' => 'repository');
+
// ---------- WIKI -----------------------------------------
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/$#',
From 34c9d04a35f710dc9512d4f4a960ac2487429891 Mon Sep 17 00:00:00 2001
From: Patrick Georgi
Date: Sat, 20 Aug 2011 11:39:41 +0200
Subject: [PATCH 02/99] Provide http access to git repositories
/p/$project/source/repo for git repos now exposes both
"dumb" and "smart" http protocol access.
---
src/IDF/Scm/Git.php | 78 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/src/IDF/Scm/Git.php b/src/IDF/Scm/Git.php
index 53bb417..e0dadbe 100644
--- a/src/IDF/Scm/Git.php
+++ b/src/IDF/Scm/Git.php
@@ -924,4 +924,82 @@ class IDF_Scm_Git extends IDF_Scm
}
return false;
}
+
+ public function repository($request, $match)
+ {
+ // authenticate: authenticate connection through "extra" password
+ if (isset($_SERVER['HTTP_AUTHORIZATION']) && $_SERVER['HTTP_AUTHORIZATION'] != '')
+ list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':' , base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
+
+ if (isset($_SERVER['PHP_AUTH_USER'])) {
+ $sql = new Pluf_SQL('login=%s', array($_SERVER['PHP_AUTH_USER']));
+ $users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
+ if ((count($users) == 1) && ($users[0]->active)) {
+ $user = $users[0];
+ $realkey = substr(sha1($user->password.Pluf::f('secret_key')), 0, 8);
+ if ($_SERVER['PHP_AUTH_PW'] == $realkey) {
+ $request->user = $user;
+ }
+ }
+ }
+
+ if (IDF_Precondition::accessSource($request) !== true) {
+ $response = new Pluf_HTTP_Response("");
+ $response->status_code = 401;
+ $response->headers['WWW-Authenticate']='Basic realm="git for '.$this->project.'"';
+ return $response;
+ }
+
+ $path = $match[2];
+
+ // update files before delivering them
+ if (($path == 'objects/info/pack') || ($path == 'info/refs')) {
+ $cmd = sprintf(Pluf::f('idf_exec_cmd_prefix', '').
+ 'GIT_DIR=%s '.Pluf::f('git_path', 'git').' update-server-info -f',
+ escapeshellarg($this->repo));
+ self::shell_exec('IDF_Scm_Git::repository', $cmd);
+ }
+
+ // smart HTTP discovery
+ if (($path == 'info/refs') &&
+ (array_key_exists('service', $request->GET))){
+ $service = $request->GET["service"];
+ switch ($service) {
+ case 'git-upload-pack':
+ case 'git-receive-pack':
+ $content = sprintf('%04x',strlen($service)+15).
+ '# service='.$service."\n0000";
+ $content .= self::shell_exec('IDF_Scm_Git::repository',
+ $service.' --stateless-rpc --advertise-refs '.
+ $this->repo);
+ $response = new Pluf_HTTP_Response($content,
+ 'application/x-'.$service.'-advertisement');
+ return $response;
+ default:
+ throw new Exception('unknown service: '.$service);
+ }
+ }
+
+ switch($path) {
+ // smart HTTP RPC
+ case 'git-upload-pack':
+ case 'git-receive-pack':
+ $response = new Pluf_HTTP_Response_CommandPassThru($path.
+ ' --stateless-rpc '.$this->repo,
+ 'application/x-'.$path.'-result');
+ $response->addStdin('php://input');
+ return $response;
+
+ // regular file
+ default:
+ // make sure we're inside the repo hierarchy (ie. no break-out)
+ if (is_file($this->repo.'/'.$path) &&
+ strpos(realpath($this->repo.'/'.$path), $this->repo.'/') == 0) {
+ return new Pluf_HTTP_Response_File($this->repo.'/'.$path,
+ 'application/octet-stream');
+ } else {
+ return new Pluf_HTTP_Response_NotFound($request);
+ }
+ }
+ }
}
From 4ae0019e0f35281c3032d7c2cef3b403e6669edb Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Sat, 24 Sep 2011 00:40:33 +0200
Subject: [PATCH 03/99] Add a sponsored-by flag in NEWS.mdtext.
---
NEWS.mdtext | 3 +++
1 file changed, 3 insertions(+)
diff --git a/NEWS.mdtext b/NEWS.mdtext
index 882a660..3078b76 100644
--- a/NEWS.mdtext
+++ b/NEWS.mdtext
@@ -1,5 +1,8 @@
# InDefero 1.2 - xxx xxx xx xx:xx 2011 UTC
+The development of this version of Indefero has partially been sponsored by the friendly folks
+from Scilab !
+
ATTENTION: You need Pluf [324ae60b](http://projects.ceondo.com/p/pluf/source/commit/324ae60b)
or newer to properly run this version of Indefero!
From 7f610fd2f3f1d33b32e26b0ea1b9250032186fda Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Sun, 25 Sep 2011 01:27:36 +0200
Subject: [PATCH 04/99] Add an option to configure an external URL per project,
e.g. to allow the linking of the home page of the project.
This feature was kindly sponsored by Scilab.
---
logo/external_link.svg | 284 ++++++++++++++++++
src/IDF/Form/Admin/ProjectCreate.php | 14 +-
src/IDF/Form/Admin/ProjectUpdate.php | 24 +-
src/IDF/Form/ProjectConf.php | 44 ++-
src/IDF/Project.php | 20 +-
src/IDF/Views.php | 42 +--
src/IDF/Views/Project.php | 2 +-
src/IDF/templates/idf/admin/summary.html | 6 +
src/IDF/templates/idf/base-full.html | 2 +-
src/IDF/templates/idf/base.html | 2 +-
.../templates/idf/gadmin/projects/create.html | 6 +
.../templates/idf/gadmin/projects/update.html | 6 +
src/IDF/templates/idf/index.html | 4 +
www/media/idf/css/style.css | 6 +
www/media/idf/img/external_link.png | Bin 0 -> 509 bytes
15 files changed, 427 insertions(+), 35 deletions(-)
create mode 100644 logo/external_link.svg
create mode 100644 www/media/idf/img/external_link.png
diff --git a/logo/external_link.svg b/logo/external_link.svg
new file mode 100644
index 0000000..e448789
--- /dev/null
+++ b/logo/external_link.svg
@@ -0,0 +1,284 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/IDF/Form/Admin/ProjectCreate.php b/src/IDF/Form/Admin/ProjectCreate.php
index c39a028..12d27cd 100644
--- a/src/IDF/Form/Admin/ProjectCreate.php
+++ b/src/IDF/Form/Admin/ProjectCreate.php
@@ -72,6 +72,13 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
'widget_attrs' => array('size' => '35'),
));
+ $this->fields['external_project_url'] = new Pluf_Form_Field_Varchar(
+ array('required' => false,
+ 'label' => __('External URL'),
+ 'widget_attrs' => array('size' => '35'),
+ 'initial' => '',
+ ));
+
$this->fields['scm'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Repository type'),
@@ -235,6 +242,11 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
return $shortname;
}
+ public function clean_external_project_url()
+ {
+ return IDF_Form_ProjectConf::checkWebURL($this->cleaned_data['external_project_url']);
+ }
+
public function clean()
{
if ($this->cleaned_data['scm'] != 'svn') {
@@ -298,7 +310,7 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
$conf = new IDF_Conf();
$conf->setProject($project);
$keys = array('scm', 'svn_remote_url', 'svn_username',
- 'svn_password', 'mtn_master_branch');
+ 'svn_password', 'mtn_master_branch', 'external_project_url');
foreach ($keys as $key) {
$this->cleaned_data[$key] = (!empty($this->cleaned_data[$key])) ?
$this->cleaned_data[$key] : '';
diff --git a/src/IDF/Form/Admin/ProjectUpdate.php b/src/IDF/Form/Admin/ProjectUpdate.php
index e600e08..c7cdc07 100644
--- a/src/IDF/Form/Admin/ProjectUpdate.php
+++ b/src/IDF/Form/Admin/ProjectUpdate.php
@@ -53,6 +53,13 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
'widget_attrs' => array('size' => '35'),
));
+ $this->fields['external_project_url'] = new Pluf_Form_Field_Varchar(
+ array('required' => false,
+ 'label' => __('External URL'),
+ 'widget_attrs' => array('size' => '35'),
+ 'initial' => $conf->getVal('external_project_url'),
+ ));
+
if ($this->project->getConf()->getVal('scm') == 'mtn') {
$this->fields['mtn_master_branch'] = new Pluf_Form_Field_Varchar(
array('required' => false,
@@ -115,6 +122,11 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['members']);
}
+ public function clean_external_project_url()
+ {
+ return IDF_Form_ProjectConf::checkWebURL($this->cleaned_data['external_project_url']);
+ }
+
public function save($commit=true)
{
if (!$this->isValid()) {
@@ -127,10 +139,16 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
$this->project->shortdesc = $this->cleaned_data['shortdesc'];
$this->project->update();
- $keys = array('mtn_master_branch');
+ $conf = $this->project->getConf();
+ $keys = array('mtn_master_branch', 'external_project_url');
foreach ($keys as $key) {
- if (!empty($this->cleaned_data[$key])) {
- $this->project->getConf()->setVal($key, $this->cleaned_data[$key]);
+ if (array_key_exists($key, $this->cleaned_data)) {
+ if (!empty($this->cleaned_data[$key])) {
+ $conf->setVal($key, $this->cleaned_data[$key]);
+ }
+ else {
+ $conf->delVal($key);
+ }
}
}
}
diff --git a/src/IDF/Form/ProjectConf.php b/src/IDF/Form/ProjectConf.php
index 482320e..74257c2 100644
--- a/src/IDF/Form/ProjectConf.php
+++ b/src/IDF/Form/ProjectConf.php
@@ -32,6 +32,7 @@ class IDF_Form_ProjectConf extends Pluf_Form
public function initFields($extra=array())
{
$this->project = $extra['project'];
+ $conf = $this->project->getConf();
// Basic part
$this->fields['name'] = new Pluf_Form_Field_Varchar(array('required' => true,
@@ -51,6 +52,11 @@ class IDF_Form_ProjectConf extends Pluf_Form
),
'widget' => 'Pluf_Form_Widget_TextareaInput',
));
+ $this->fields['external_project_url'] = new Pluf_Form_Field_Varchar(array('required' => false,
+ 'label' => __('External URL'),
+ 'widget_attrs' => array('size' => '68'),
+ 'initial' => $conf->getVal('external_project_url'),
+ ));
// Logo part
$upload_path = Pluf::f('upload_path', false);
@@ -118,20 +124,48 @@ class IDF_Form_ProjectConf extends Pluf_Form
return $this->cleaned_data['logo'];
}
+ public function clean_external_project_url()
+ {
+ return self::checkWebURL($this->cleaned_data['external_project_url']);
+ }
+
+ public static function checkWebURL($url)
+ {
+ $url = trim($url);
+ if (empty($url)) {
+ return '';
+ }
+
+ $parsed = parse_url($url);
+ if ($parsed === false || !array_key_exists('scheme', $parsed) ||
+ ($parsed['scheme'] != 'http' && $parsed['scheme'] != 'https')) {
+ throw new Pluf_Form_Invalid(__('The entered URL is invalid. Only http and https URLs are allowed.'));
+ }
+
+ return $url;
+ }
+
public function save($commit=true)
{
- $conf = $this->project->getConf();
-
// Basic part
$this->project->name = $this->cleaned_data['name'];
$this->project->shortdesc = $this->cleaned_data['shortdesc'];
$this->project->description = $this->cleaned_data['description'];
$this->project->update();
- // Logo part
- if ($this->cleaned_data['logo'] !== "") {
- $conf->setVal('logo', $this->cleaned_data['logo']);
+ $conf = $this->project->getConf();
+ $keys = array('logo', 'external_project_url');
+ foreach ($keys as $key) {
+ if (array_key_exists($key, $this->cleaned_data)) {
+ if (!empty($this->cleaned_data[$key])) {
+ $conf->setVal($key, $this->cleaned_data[$key]);
+ }
+ else {
+ $conf->delVal($key);
+ }
+ }
}
+
if ($this->cleaned_data['logo_remove'] === true) {
@unlink(Pluf::f('upload_path') . '/' . $this->project->shortname . $conf->getVal('logo'));
$conf->delVal('logo');
diff --git a/src/IDF/Project.php b/src/IDF/Project.php
index 66a070e..598f4a3 100644
--- a/src/IDF/Project.php
+++ b/src/IDF/Project.php
@@ -132,7 +132,7 @@ class IDF_Project extends Pluf_Model
}
return $projects[0];
}
-
+
/**
* Returns the number of open/closed issues.
*
@@ -167,7 +167,7 @@ GROUP BY uid";
$key = ($v['id'] === '-1') ? null : $v['id'];
$ownerStatistics[$key] = (int)$v['nb'];
}
-
+
arsort($ownerStatistics);
return $ownerStatistics;
@@ -552,6 +552,22 @@ GROUP BY uid";
return $this->_pconf;
}
+ /**
+ * Magic overload that falls back to the values of the internal configuration
+ * if no getter / caller matched
+ *
+ * @param string $key
+ */
+ public function __get($key)
+ {
+ try {
+ return parent::__get($key);
+ }
+ catch (Exception $e) {
+ return $this->getConf()->getVal($key);
+ }
+ }
+
/**
* Get simple statistics about the project.
*
diff --git a/src/IDF/Views.php b/src/IDF/Views.php
index beca428..70bbdc3 100644
--- a/src/IDF/Views.php
+++ b/src/IDF/Views.php
@@ -40,10 +40,10 @@ class IDF_Views
public function index($request, $match, $api=false)
{
$projects = self::getProjects($request->user);
- $stats = self::getProjectsStatistics ($projects);
-
+ $stats = self::getProjectsStatistics($projects);
+
if ($api == true) return $projects;
- return Pluf_Shortcuts_RenderToResponse('idf/index.html',
+ return Pluf_Shortcuts_RenderToResponse('idf/index.html',
array('page_title' => __('Projects'),
'projects' => $projects,
'stats' => new Pluf_Template_ContextVars($stats)),
@@ -55,7 +55,7 @@ class IDF_Views
*/
public function login($request, $match)
{
- if (isset($request->POST['action'])
+ if (isset($request->POST['action'])
and $request->POST['action'] == 'new-user') {
$login = (isset($request->POST['login'])) ? $request->POST['login'] : '';
$url = Pluf_HTTP_URL_urlForView('IDF_Views::register', array(),
@@ -91,7 +91,7 @@ class IDF_Views
$params = array('request'=>$request);
if ($request->method == 'POST') {
$form = new IDF_Form_Register(array_merge(
- (array)$request->POST,
+ (array)$request->POST,
(array)$request->FILES
), $params);
if ($form->isValid()) {
@@ -108,7 +108,7 @@ class IDF_Views
$context = new Pluf_Template_Context(array());
$tmpl = new Pluf_Template('idf/terms.html');
$terms = Pluf_Template::markSafe($tmpl->render($context));
- return Pluf_Shortcuts_RenderToResponse('idf/register/index.html',
+ return Pluf_Shortcuts_RenderToResponse('idf/register/index.html',
array('page_title' => $title,
'form' => $form,
'terms' => $terms),
@@ -133,7 +133,7 @@ class IDF_Views
} else {
$form = new IDF_Form_RegisterInputKey();
}
- return Pluf_Shortcuts_RenderToResponse('idf/register/inputkey.html',
+ return Pluf_Shortcuts_RenderToResponse('idf/register/inputkey.html',
array('page_title' => $title,
'form' => $form),
$request);
@@ -168,7 +168,7 @@ class IDF_Views
$request->session->clear();
$request->session->setData('login_time', gmdate('Y-m-d H:i:s'));
$user->last_login = gmdate('Y-m-d H:i:s');
- $user->update();
+ $user->update();
$request->user->setMessage(__('Welcome! You can now participate in the life of your project of choice.'));
$url = Pluf_HTTP_URL_urlForView('IDF_Views::index');
return new Pluf_HTTP_Response_Redirect($url);
@@ -176,7 +176,7 @@ class IDF_Views
} else {
$form = new IDF_Form_RegisterConfirmation(null, $extra);
}
- return Pluf_Shortcuts_RenderToResponse('idf/register/confirmation.html',
+ return Pluf_Shortcuts_RenderToResponse('idf/register/confirmation.html',
array('page_title' => $title,
'new_user' => $user,
'form' => $form),
@@ -213,7 +213,7 @@ class IDF_Views
/**
* If the key is valid, provide a nice form to reset the password
- * and automatically login the user.
+ * and automatically login the user.
*
* This is also firing the password change event for the plugins.
*/
@@ -238,7 +238,7 @@ class IDF_Views
$request->session->clear();
$request->session->setData('login_time', gmdate('Y-m-d H:i:s'));
$user->last_login = gmdate('Y-m-d H:i:s');
- $user->update();
+ $user->update();
$request->user->setMessage(__('Welcome back! Next time, you can use your broswer options to remember the password.'));
$url = Pluf_HTTP_URL_urlForView('IDF_Views::index');
return new Pluf_HTTP_Response_Redirect($url);
@@ -246,12 +246,12 @@ class IDF_Views
} else {
$form = new IDF_Form_PasswordReset(null, $extra);
}
- return Pluf_Shortcuts_RenderToResponse('idf/user/passrecovery.html',
+ return Pluf_Shortcuts_RenderToResponse('idf/user/passrecovery.html',
array('page_title' => $title,
'new_user' => $user,
'form' => $form),
$request);
-
+
}
/**
@@ -270,7 +270,7 @@ class IDF_Views
} else {
$form = new IDF_Form_PasswordInputKey();
}
- return Pluf_Shortcuts_RenderToResponse('idf/user/passrecovery-inputkey.html',
+ return Pluf_Shortcuts_RenderToResponse('idf/user/passrecovery-inputkey.html',
array('page_title' => $title,
'form' => $form),
$request);
@@ -283,7 +283,7 @@ class IDF_Views
{
$title = __('Here to Help You!');
$projects = self::getProjects($request->user);
- return Pluf_Shortcuts_RenderToResponse('idf/faq.html',
+ return Pluf_Shortcuts_RenderToResponse('idf/faq.html',
array(
'page_title' => $title,
'projects' => $projects,
@@ -299,7 +299,7 @@ class IDF_Views
{
$title = __('InDefero API (Application Programming Interface)');
$projects = self::getProjects($request->user);
- return Pluf_Shortcuts_RenderToResponse('idf/faq-api.html',
+ return Pluf_Shortcuts_RenderToResponse('idf/faq-api.html',
array(
'page_title' => $title,
'projects' => $projects,
@@ -335,7 +335,7 @@ class IDF_Views
);
$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 = sprintf('%s=%s', $db->qn('private'), $false);
if ($rows->count() > 0) {
$ids = array();
@@ -347,7 +347,7 @@ class IDF_Views
return Pluf::factory('IDF_Project')->getList(array('filter' => $sql,
'order' => 'name ASC'));
}
-
+
/**
* Returns statistics on a list of projects.
*
@@ -362,7 +362,7 @@ class IDF_Views
'issues' => 0,
'docpages' => 0,
'commits' => 0);
-
+
// Count for each projects
foreach ($projects as $p) {
$pstats = $p->getStats ();
@@ -372,7 +372,7 @@ class IDF_Views
$forgestats['docpages'] += $pstats['docpages'];
$forgestats['commits'] += $pstats['commits'];
}
-
+
// Count projects
$forgestats['projects'] = count($projects);
@@ -380,7 +380,7 @@ class IDF_Views
$sql = new Pluf_SQL('first_name != %s', array('---'));
$forgestats['members'] = Pluf::factory('Pluf_User')
->getCount(array('filter' => $sql->gen()));
-
+
return $forgestats;
}
}
diff --git a/src/IDF/Views/Project.php b/src/IDF/Views/Project.php
index 11c08f4..70b2efa 100644
--- a/src/IDF/Views/Project.php
+++ b/src/IDF/Views/Project.php
@@ -305,7 +305,7 @@ class IDF_Views_Project
return new Pluf_HTTP_Response_Redirect($url);
}
} else {
- $form = new IDF_Form_ProjectConf($prj->getData(), $extra);
+ $form = new IDF_Form_ProjectConf(null, $extra);
}
$logo = $prj->getConf()->getVal('logo');
diff --git a/src/IDF/templates/idf/admin/summary.html b/src/IDF/templates/idf/admin/summary.html
index 5a44503..c75391d 100644
--- a/src/IDF/templates/idf/admin/summary.html
+++ b/src/IDF/templates/idf/admin/summary.html
@@ -25,6 +25,12 @@
+{$form.f.external_project_url.labelTag}:
+{if $form.f.external_project_url.errors}{$form.f.external_project_url.fieldErrors}{/if}
+{$form.f.external_project_url|unsafe}
+
+
+
{$form.f.description.labelTag}:
{if $form.f.description.errors}{$form.f.description.fieldErrors}{/if}
{$form.f.description|unsafe}
diff --git a/src/IDF/templates/idf/base-full.html b/src/IDF/templates/idf/base-full.html
index 3f1c447..ff0d1e4 100644
--- a/src/IDF/templates/idf/base-full.html
+++ b/src/IDF/templates/idf/base-full.html
@@ -37,7 +37,7 @@
- {if $project}
{$project} {if $project.private} {/if}{$p} {/if}
+ {if $project}
{$project} {if $project.private} {/if}{$p}{assign $url = $project.external_project_url}{if $url != ''} {/if} {/if}
{include 'idf/main-menu.html'}
+{$form.f.external_project_url.labelTag}:
+{if $form.f.external_project_url.errors}{$form.f.external_project_url.fieldErrors}{/if}
+{$form.f.external_project_url|unsafe}
+
+
+
{$form.f.scm.labelTag}:
{if $form.f.scm.errors}{$form.f.scm.fieldErrors}{/if}
{$form.f.scm|unsafe}
diff --git a/src/IDF/templates/idf/gadmin/projects/update.html b/src/IDF/templates/idf/gadmin/projects/update.html
index 132336a..0a64e56 100644
--- a/src/IDF/templates/idf/gadmin/projects/update.html
+++ b/src/IDF/templates/idf/gadmin/projects/update.html
@@ -25,6 +25,12 @@
{$form.f.shortdesc.help_text}
+
+{$form.f.external_project_url.labelTag}:
+{if $form.f.external_project_url.errors}{$form.f.external_project_url.fieldErrors}{/if}
+{$form.f.external_project_url|unsafe}
+
+
{if $project.getConf().getVal('scm') == 'mtn'}
{$form.f.mtn_master_branch.labelTag}:
diff --git a/src/IDF/templates/idf/index.html b/src/IDF/templates/idf/index.html
index 14d0d7b..fba3614 100644
--- a/src/IDF/templates/idf/index.html
+++ b/src/IDF/templates/idf/index.html
@@ -25,6 +25,10 @@
{$p}
+ {assign $url = $p.external_project_url}
+ {if $url != ''}
+
+ {/if}
{if $p.private} - {trans 'Private project'}{/if}
{$p.shortdesc}
diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css
index 4b94eb2..831a0f6 100644
--- a/www/media/idf/css/style.css
+++ b/www/media/idf/css/style.css
@@ -1105,6 +1105,12 @@ div.p-list-private {
position: relative;
}
+a.external-link {
+ background: url(../img/external_link.png) no-repeat right center;
+ padding-right: 15px;
+ text-decoration: none;
+}
+
/*
* Issue summary
*/
diff --git a/www/media/idf/img/external_link.png b/www/media/idf/img/external_link.png
new file mode 100644
index 0000000000000000000000000000000000000000..ef6ca95becc06d8f688888d90578085cc6c00387
GIT binary patch
literal 509
zcmV
5r00004b3#c}2nYxW
zd_>>GYbWX8r8;$u
z4$-B+i=^%u_ahxxJloHc4NhXuAjY=t`
z9O5_*zVAl>hEfWp)Xd325U{?!#pC1CRIMR|KnO7t0C0PIPqDZ`z21oU=JC^^Ci_
zhlr=O2Eg-twzqeAdy{N#mVO3YTwHN^S&vOJ9*^1C+2{KDhDzmd8t_-0p4NDIdBylI
zU}0gNgM+>41eP`Zq*`l&Aefw2R+czFuVUL7gy(s>)oM{F6k?~f+s`a7uK`dh6*)dW
z!Z1F7a$BnEv3Zw{a*p;bQ;^XBir~6&$*RJ`0JNu00000NkvXXu0mjfERo%W
literal 0
HcmV?d00001
From dc50e9b3162dccf251d24679ae0301e20e5ac0d0 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Sun, 25 Sep 2011 02:02:34 +0200
Subject: [PATCH 05/99] Spelling.
---
src/IDF/locale/de/idf.po | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/IDF/locale/de/idf.po b/src/IDF/locale/de/idf.po
index 0df27e3..42f412d 100644
--- a/src/IDF/locale/de/idf.po
+++ b/src/IDF/locale/de/idf.po
@@ -1039,7 +1039,7 @@ msgid ""
"value.\n"
msgstr ""
"\n"
-"Anletung:
\n"
+"Anleitung:
\n"
"Füge einen Statuswert pro Zeile in der gewünschten Reihenfolge hinzu."
"p>\n"
"
Füge optional nach einem Istgleich-Zeichen (=) eine kurze Beschreibung "
From b29acd71cb32521c63d9ce94539c586b89d43c68 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Sun, 25 Sep 2011 02:03:35 +0200
Subject: [PATCH 06/99] Add an option to specify a webhook URL for updates in
the downloads section and tweak the help texts for the original source web
hook a bit. Also remove the superfluous inline help code from the SourceConf
form that was actually not used.
This feature was sponsored by Scilab.
---
src/IDF/Commit.php | 2 +-
src/IDF/Form/SourceConf.php | 3 --
src/IDF/Form/UploadConf.php | 10 +++++-
src/IDF/Project.php | 4 +--
src/IDF/Views/Project.php | 12 +++++---
src/IDF/templates/idf/admin/downloads.html | 36 +++++++++++++++++++++-
src/IDF/templates/idf/admin/source.html | 17 +++++-----
7 files changed, 63 insertions(+), 21 deletions(-)
diff --git a/src/IDF/Commit.php b/src/IDF/Commit.php
index 0e724ad..6be1a95 100644
--- a/src/IDF/Commit.php
+++ b/src/IDF/Commit.php
@@ -297,7 +297,7 @@ class IDF_Commit extends Pluf_Model
'creation_date' => $this->creation_dtime,
),
'project_id' => $project->id,
- 'authkey' => $project->getPostCommitHookKey(),
+ 'authkey' => $project->getWebHookKey(),
'url' => $url,
);
$item = new IDF_Queue();
diff --git a/src/IDF/Form/SourceConf.php b/src/IDF/Form/SourceConf.php
index df2a6cc..5a9a183 100644
--- a/src/IDF/Form/SourceConf.php
+++ b/src/IDF/Form/SourceConf.php
@@ -49,13 +49,10 @@ class IDF_Form_SourceConf extends Pluf_Form
'widget' => 'Pluf_Form_Widget_PasswordInput',
));
}
- Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
- $url = Pluf_HTTP_URL_urlForView('idf_faq').'#webhooks';
$this->fields['webhook_url'] = new Pluf_Form_Field_Url(
array('required' => false,
'label' => __('Webhook URL'),
'initial' => $this->conf->getVal('webhook_url', ''),
- 'help_text' => sprintf(__('Learn more about the post-commit web hooks .'), $url),
'widget_attrs' => array('size' => 35),
));
diff --git a/src/IDF/Form/UploadConf.php b/src/IDF/Form/UploadConf.php
index f6b4c7d..eec6788 100644
--- a/src/IDF/Form/UploadConf.php
+++ b/src/IDF/Form/UploadConf.php
@@ -60,10 +60,18 @@ Deprecated = Most users should NOT download this';
$this->fields['labels_download_one_max'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Each download may have at most one label with each of these classes'),
- 'initial' => self::init_one_max,
+ 'initial' => self::init_one_max,
'widget_attrs' => array('size' => 60),
));
+ $this->conf = $extra['conf'];
+ $this->fields['upload_webhook_url'] = new Pluf_Form_Field_Url(
+ array('required' => false,
+ 'label' => __('Webhook URL'),
+ 'initial' => $this->conf->getVal('upload_webhook_url', ''),
+ 'widget_attrs' => array('size' => 60),
+ ));
+
}
}
diff --git a/src/IDF/Project.php b/src/IDF/Project.php
index 598f4a3..ab25cf5 100644
--- a/src/IDF/Project.php
+++ b/src/IDF/Project.php
@@ -494,12 +494,12 @@ GROUP BY uid";
}
/**
- * Get the post commit hook key.
+ * Get the web hook key.
*
* The goal is to get something predictable but from which one
* cannot reverse find the secret key.
*/
- public function getPostCommitHookKey()
+ public function getWebHookKey()
{
return md5($this->id.sha1(Pluf::f('secret_key')).$this->shortname);
}
diff --git a/src/IDF/Views/Project.php b/src/IDF/Views/Project.php
index 70b2efa..54c52ee 100644
--- a/src/IDF/Views/Project.php
+++ b/src/IDF/Views/Project.php
@@ -375,8 +375,11 @@ class IDF_Views_Project
$title = sprintf(__('%s Downloads Configuration'), (string) $prj);
$conf = new IDF_Conf();
$conf->setProject($prj);
+ $extra = array(
+ 'conf' => $conf,
+ );
if ($request->method == 'POST') {
- $form = new IDF_Form_UploadConf($request->POST);
+ $form = new IDF_Form_UploadConf($request->POST, $extra);
if ($form->isValid()) {
foreach ($form->cleaned_data as $key=>$val) {
$conf->setVal($key, $val);
@@ -388,7 +391,7 @@ class IDF_Views_Project
}
} else {
$params = array();
- $keys = array('labels_download_predefined', 'labels_download_one_max');
+ $keys = array('labels_download_predefined', 'labels_download_one_max', 'upload_webhook_url');
foreach ($keys as $key) {
$_val = $conf->getVal($key, false);
if ($_val !== false) {
@@ -398,12 +401,13 @@ class IDF_Views_Project
if (count($params) == 0) {
$params = null; //Nothing in the db, so new form.
}
- $form = new IDF_Form_UploadConf($params);
+ $form = new IDF_Form_UploadConf($params, $extra);
}
return Pluf_Shortcuts_RenderToResponse('idf/admin/downloads.html',
array(
'page_title' => $title,
'form' => $form,
+ 'hookkey' => $prj->getWebHookKey(),
),
$request);
}
@@ -598,7 +602,7 @@ class IDF_Views_Project
'repository_size' => $prj->getRepositorySize(),
'page_title' => $title,
'form' => $form,
- 'hookkey' => $prj->getPostCommitHookKey(),
+ 'hookkey' => $prj->getWebHookKey(),
),
$request);
}
diff --git a/src/IDF/templates/idf/admin/downloads.html b/src/IDF/templates/idf/admin/downloads.html
index c22be5e..a614484 100644
--- a/src/IDF/templates/idf/admin/downloads.html
+++ b/src/IDF/templates/idf/admin/downloads.html
@@ -1,5 +1,5 @@
{extends "idf/admin/base.html"}
-{block docclass}yui-t1{assign $inDownloads = true}{/block}
+{block docclass}yui-t3{assign $inDownloads = true}{/block}
{block body}
+{include 'idf/project/js-autocomplete.html'}{/block}
{/block}
+
{block context}
{blocktrans}
diff --git a/src/IDF/templates/idf/project/js-autocomplete.html b/src/IDF/templates/idf/project/js-autocomplete.html
new file mode 100644
index 0000000..1799f16
--- /dev/null
+++ b/src/IDF/templates/idf/project/js-autocomplete.html
@@ -0,0 +1,25 @@
+
+
+
From 4d7d99c0bbac4fa181b4365f87e8f2b28fbaa4d0 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Sun, 11 Dec 2011 01:44:29 +0100
Subject: [PATCH 64/99] Neither the logo fields / controls nor the external
project url field is mandatory, so don't render it bold.
---
src/IDF/templates/idf/admin/summary.html | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/IDF/templates/idf/admin/summary.html b/src/IDF/templates/idf/admin/summary.html
index 2caf786..095b004 100644
--- a/src/IDF/templates/idf/admin/summary.html
+++ b/src/IDF/templates/idf/admin/summary.html
@@ -25,7 +25,7 @@
-{$form.f.external_project_url.labelTag}:
+{$form.f.external_project_url.labelTag}:
{if $form.f.external_project_url.errors}{$form.f.external_project_url.fieldErrors}{/if}
{$form.f.external_project_url|unsafe}
@@ -48,7 +48,7 @@
-{trans 'Current logo'}:
+{trans 'Current logo'}:
{if $logo}
@@ -58,14 +58,14 @@
-{$form.f.logo.labelTag}:
+{$form.f.logo.labelTag}:
{if $form.f.logo.errors}{$form.f.logo.fieldErrors}{/if}
{$form.f.logo|unsafe}
{if $logo}
-{$form.f.logo_remove.labelTag}:
+{$form.f.logo_remove.labelTag}:
{if $form.f.logo_remove.errors}{$form.f.logo_remove.fieldErrors}{/if}
{$form.f.logo_remove|unsafe}
From 4dd89942700d56539a9ec35171760a4a7419c67c Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Tue, 13 Dec 2011 00:12:23 +0100
Subject: [PATCH 65/99] Clear previous floating for new paragraphs.
---
www/media/idf/css/style.css | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css
index 5454e0c..8058ee0 100644
--- a/www/media/idf/css/style.css
+++ b/www/media/idf/css/style.css
@@ -799,6 +799,10 @@ table.download {
/**
* Wiki
*/
+#wiki-content p {
+ clear: both;
+}
+
p.desc {
background-color: #eeeeec;
-moz-border-radius: 3px;
From a83634c1662f8498e5f1b63301330265c5c5f9e2 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Tue, 13 Dec 2011 00:18:49 +0100
Subject: [PATCH 66/99] Render information about the old revision
to-be-deleted, not about the current one.
---
src/IDF/Views/Wiki.php | 2 --
src/IDF/templates/idf/wiki/deleteResourceRev.html | 10 +++++-----
2 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/IDF/Views/Wiki.php b/src/IDF/Views/Wiki.php
index d394d43..8b6d7df 100644
--- a/src/IDF/Views/Wiki.php
+++ b/src/IDF/Views/Wiki.php
@@ -452,7 +452,6 @@ class IDF_Views_Wiki
}
$title = sprintf(__('Delete Old Revision of %s'), $resource->title);
- $revision = $resource->get_current_revision();
$false = Pluf_DB_BooleanToDb(false, $resource->getDbConnection());
$revs = $resource->get_revisions_list(array('order' => 'creation_dtime DESC',
'filter' => 'is_head='.$false));
@@ -461,7 +460,6 @@ class IDF_Views_Wiki
'page_title' => $title,
'resource' => $resource,
'oldrev' => $oldrev,
- 'rev' => $revision,
'revs' => $revs,
),
$request);
diff --git a/src/IDF/templates/idf/wiki/deleteResourceRev.html b/src/IDF/templates/idf/wiki/deleteResourceRev.html
index 8523192..aac8ed7 100644
--- a/src/IDF/templates/idf/wiki/deleteResourceRev.html
+++ b/src/IDF/templates/idf/wiki/deleteResourceRev.html
@@ -21,16 +21,16 @@
{$resource.summary}
-{assign $preview = $rev.renderRaw()}
+{assign $preview = $oldrev.renderRaw()}
{if $preview == ''}
{assign $preview = __('Unable to render preview for this MIME type.')}
{/if}
{$preview|unsafe}
{/block}
@@ -38,8 +38,8 @@
{block context}
{assign $submitter = $resource.get_submitter()}
{trans 'Created:'} {$resource.creation_dtime|dateago} {blocktrans}by {$submitter}{/blocktrans}
-{if $rev.creation_dtime != $resource.creation_dtime}{assign $submitter = $rev.get_submitter()}
-{trans 'Updated:'} {$rev.creation_dtime|dateago} {blocktrans}by {$submitter}{/blocktrans}
{/if}
+{if $oldrev.creation_dtime != $resource.creation_dtime}{assign $submitter = $oldrev.get_submitter()}
+{trans 'Updated:'} {$oldrev.creation_dtime|dateago} {blocktrans}by {$submitter}{/blocktrans}
{/if}
{if $revs.count() > 0}
{trans 'Old Revisions'}
{foreach $revs as $old}
From 6a20b36700b81680acfd928f54d3092f4d182625 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Tue, 13 Dec 2011 00:32:11 +0100
Subject: [PATCH 67/99] Instead of breaking for every paragraph (which makes
our toc look really broken, break after the first element after a resource
container.
This also means that resource containers cannot be "chained" together
to all float left or right, but we have to draw a line somewhere.
---
www/media/idf/css/style.css | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css
index 8058ee0..e3fc6de 100644
--- a/www/media/idf/css/style.css
+++ b/www/media/idf/css/style.css
@@ -799,10 +799,6 @@ table.download {
/**
* Wiki
*/
-#wiki-content p {
- clear: both;
-}
-
p.desc {
background-color: #eeeeec;
-moz-border-radius: 3px;
@@ -937,6 +933,10 @@ ol > li {
padding: 5px;
}
+.resource-container:after {
+ clear: both;
+}
+
.resource-container .preview {
margin-bottom: 5px;
}
From 52d638e02773ebab7c3a05fa55b49a413bf04aad Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Tue, 13 Dec 2011 00:36:57 +0100
Subject: [PATCH 68/99] Next try.
---
www/media/idf/css/style.css | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css
index 8058ee0..5260c69 100644
--- a/www/media/idf/css/style.css
+++ b/www/media/idf/css/style.css
@@ -799,10 +799,6 @@ table.download {
/**
* Wiki
*/
-#wiki-content p {
- clear: both;
-}
-
p.desc {
background-color: #eeeeec;
-moz-border-radius: 3px;
@@ -937,6 +933,10 @@ ol > li {
padding: 5px;
}
+.resource-container + * {
+ clear: both;
+}
+
.resource-container .preview {
margin-bottom: 5px;
}
From 5ffe66cbfbdc5f30372a9bcc3f6ca4f679bc1520 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Tue, 20 Dec 2011 23:26:29 +0100
Subject: [PATCH 69/99] Add resource revisions to the timeline and add proper
templates and rendering for resources and resource revisions (this is needed
later on for the taxonomy). Directly link to individual resource / page
revisions from the timeline as well.
---
src/IDF/Wiki/Page.php | 2 +-
src/IDF/Wiki/PageRevision.php | 38 ++++++------
src/IDF/Wiki/Resource.php | 10 ++--
src/IDF/Wiki/ResourceRevision.php | 58 ++++++++++++++++++-
...feedfragment.xml => feedfragment-page.xml} | 2 +-
.../idf/wiki/feedfragment-resource.xml | 11 ++++
6 files changed, 90 insertions(+), 31 deletions(-)
rename src/IDF/templates/idf/wiki/{feedfragment.xml => feedfragment-page.xml} (97%)
create mode 100644 src/IDF/templates/idf/wiki/feedfragment-resource.xml
diff --git a/src/IDF/Wiki/Page.php b/src/IDF/Wiki/Page.php
index cc7f5e9..0c4a35c 100644
--- a/src/IDF/Wiki/Page.php
+++ b/src/IDF/Wiki/Page.php
@@ -218,7 +218,7 @@ class IDF_Wiki_Page extends Pluf_Model
'create' => true,
'date' => $date)
);
- $tmpl = new Pluf_Template('idf/wiki/feedfragment.xml');
+ $tmpl = new Pluf_Template('idf/wiki/feedfragment-page.xml');
return $tmpl->render($context);
}
}
diff --git a/src/IDF/Wiki/PageRevision.php b/src/IDF/Wiki/PageRevision.php
index ae05d5d..12e9695 100644
--- a/src/IDF/Wiki/PageRevision.php
+++ b/src/IDF/Wiki/PageRevision.php
@@ -137,7 +137,7 @@ class IDF_Wiki_PageRevision extends Pluf_Model
function postSave($create=false)
{
- $prj = $this->get_wikipage()->get_project();
+ $page = $this->get_wikipage();
if ($create) {
// Check if more than one revision for this page. We do
@@ -148,7 +148,7 @@ class IDF_Wiki_PageRevision extends Pluf_Model
$sql = new Pluf_SQL('wikipage=%s', array($this->wikipage));
$rev = Pluf::factory('IDF_Wiki_PageRevision')->getList(array('filter'=>$sql->gen()));
if ($rev->count() > 1) {
- IDF_Timeline::insert($this, $prj, $this->get_submitter());
+ IDF_Timeline::insert($this, $page->get_project(), $this->get_submitter());
foreach ($rev as $r) {
if ($r->id != $this->id and $r->is_head) {
$r->is_head = false;
@@ -156,11 +156,11 @@ class IDF_Wiki_PageRevision extends Pluf_Model
}
}
}
- $page = $this->get_wikipage();
- $page->update(); // Will update the modification timestamp.
- IDF_Search::index($page);
}
+ IDF_Search::index($page);
+ $page->update(); // Will update the modification timestamp.
+
// remember the resource revisions used in this page revision
if ($this->is_head) {
preg_match_all('#\[\[!([A-Za-z0-9\-]+)[^\]]*\]\]#im', $this->content, $matches, PREG_PATTERN_ORDER);
@@ -183,9 +183,11 @@ class IDF_Wiki_PageRevision extends Pluf_Model
public function timelineFragment($request)
{
$page = $this->get_wikipage();
- $url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
- array($request->project->shortname,
- $page->title));
+ $url = Pluf::f('url_base')
+ .Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
+ array($request->project->shortname,
+ $page->title),
+ array('rev' => $this->id));
$out = "\n".''.
Pluf_esc(Pluf_Template_dateAgo($this->creation_dtime, 'without')).
' ';
@@ -220,18 +222,12 @@ class IDF_Wiki_PageRevision extends Pluf_Model
public function feedFragment($request)
{
$page = $this->get_wikipage();
- if (!$this->is_head) {
- $url = Pluf::f('url_base')
- .Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
- array($request->project->shortname,
- $page->title),
- array('rev' => $this->id));
- } else {
- $url = Pluf::f('url_base')
- .Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
- array($request->project->shortname,
- $page->title));
- }
+ $url = Pluf::f('url_base')
+ .Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
+ array($request->project->shortname,
+ $page->title),
+ array('rev' => $this->id));
+
$title = sprintf(__('%1$s: Documentation page %2$s updated - %3$s'),
$request->project->name,
$page->title, $page->summary);
@@ -245,7 +241,7 @@ class IDF_Wiki_PageRevision extends Pluf_Model
'create' => false,
'date' => $date)
);
- $tmpl = new Pluf_Template('idf/wiki/feedfragment.xml');
+ $tmpl = new Pluf_Template('idf/wiki/feedfragment-page.xml');
return $tmpl->render($context);
}
diff --git a/src/IDF/Wiki/Resource.php b/src/IDF/Wiki/Resource.php
index 3220a3c..487cf1a 100644
--- a/src/IDF/Wiki/Resource.php
+++ b/src/IDF/Wiki/Resource.php
@@ -175,7 +175,7 @@ class IDF_Wiki_Resource extends Pluf_Model
$user = $stag->start($this->get_submitter(), $request, '', false);
$out .= sprintf(__('%2$s , %3$s'), $url, Pluf_esc($this->title), Pluf_esc($this->summary)).' ';
$out .= "\n".' ';
+'.sprintf(__('Creation of
resource %2$s , by %3$s'), $url, Pluf_esc($this->title), $user).'
';
return Pluf_Template::markSafe($out);
}
@@ -185,7 +185,7 @@ class IDF_Wiki_Resource extends Pluf_Model
.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource',
array($request->project->shortname,
$this->title));
- $title = sprintf(__('%1$s: Documentation page %2$s added - %3$s'),
+ $title = sprintf(__('%1$s: Documentation resource %2$s added - %3$s'),
$request->project->name,
$this->title, $this->summary);
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
@@ -193,12 +193,12 @@ class IDF_Wiki_Resource extends Pluf_Model
$request,
array('url' => $url,
'title' => $title,
- 'page' => $this,
+ 'resource' => $this,
'rev' => $this->get_current_revision(),
'create' => true,
'date' => $date)
- );
- $tmpl = new Pluf_Template('idf/wiki/feedfragment.xml');
+ );
+ $tmpl = new Pluf_Template('idf/wiki/feedfragment-resource.xml');
return $tmpl->render($context);
}
}
diff --git a/src/IDF/Wiki/ResourceRevision.php b/src/IDF/Wiki/ResourceRevision.php
index 0ec8fc0..ee1a3ea 100644
--- a/src/IDF/Wiki/ResourceRevision.php
+++ b/src/IDF/Wiki/ResourceRevision.php
@@ -135,6 +135,7 @@ class IDF_Wiki_ResourceRevision extends Pluf_Model
}
@unlink($this->getFilePath());
+ IDF_Timeline::remove($this);
}
function preSave($create=false)
@@ -147,10 +148,13 @@ class IDF_Wiki_ResourceRevision extends Pluf_Model
function postSave($create=false)
{
+ $resource = $this->get_wikiresource();
+
if ($create) {
$sql = new Pluf_SQL('wikiresource=%s', array($this->wikiresource));
$rev = Pluf::factory('IDF_Wiki_ResourceRevision')->getList(array('filter'=>$sql->gen()));
if ($rev->count() > 1) {
+ IDF_Timeline::insert($this, $resource->get_project(), $this->get_submitter());
foreach ($rev as $r) {
if ($r->id != $this->id and $r->is_head) {
$r->is_head = false;
@@ -158,10 +162,10 @@ class IDF_Wiki_ResourceRevision extends Pluf_Model
}
}
}
- // update the modification timestamp
- $resource = $this->get_wikiresource();
- $resource->update();
}
+
+ // update the modification timestamp
+ $resource->update();
}
function getFilePath()
@@ -285,4 +289,52 @@ class IDF_Wiki_ResourceRevision extends Pluf_Model
return '';
}
+
+
+ public function timelineFragment($request)
+ {
+ $resource = $this->get_wikiresource();
+ $url = Pluf::f('url_base')
+ .Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource',
+ array($request->project->shortname,
+ $resource->title),
+ array('rev' => $this->id));
+
+ $out = "\n".''.
+ Pluf_esc(Pluf_Template_dateAgo($this->creation_dtime, 'without')).
+ ' ';
+ $stag = new IDF_Template_ShowUser();
+ $user = $stag->start($this->get_submitter(), $request, '', false);
+ $out .= sprintf(__('%2$s , %3$s'), $url, Pluf_esc($resource->title), Pluf_esc($this->summary));
+ $out .= ' ';
+ $out .= "\n".'';
+ return Pluf_Template::markSafe($out);
+ }
+
+ public function feedFragment($request)
+ {
+ $resource = $this->get_wikiresource();
+ $url = Pluf::f('url_base')
+ .Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource',
+ array($request->project->shortname,
+ $resource->title),
+ array('rev' => $this->id));
+
+ $title = sprintf(__('%1$s: Documentation resource %2$s updated - %3$s'),
+ $request->project->name,
+ $resource->title, $resource->summary);
+ $date = Pluf_Date::gmDateToGmString($this->creation_dtime);
+ $context = new Pluf_Template_Context_Request(
+ $request,
+ array('url' => $url,
+ 'title' => $title,
+ 'resource' => $resource,
+ 'rev' => $this,
+ 'create' => false,
+ 'date' => $date)
+ );
+ $tmpl = new Pluf_Template('idf/wiki/feedfragment-resource.xml');
+ return $tmpl->render($context);
+ }
}
diff --git a/src/IDF/templates/idf/wiki/feedfragment.xml b/src/IDF/templates/idf/wiki/feedfragment-page.xml
similarity index 97%
rename from src/IDF/templates/idf/wiki/feedfragment.xml
rename to src/IDF/templates/idf/wiki/feedfragment-page.xml
index d017437..6c0e625 100644
--- a/src/IDF/templates/idf/wiki/feedfragment.xml
+++ b/src/IDF/templates/idf/wiki/feedfragment-page.xml
@@ -1,4 +1,4 @@
-
+
{$title}
{$url}:{$rev.id}
diff --git a/src/IDF/templates/idf/wiki/feedfragment-resource.xml b/src/IDF/templates/idf/wiki/feedfragment-resource.xml
new file mode 100644
index 0000000..2fddefb
--- /dev/null
+++ b/src/IDF/templates/idf/wiki/feedfragment-resource.xml
@@ -0,0 +1,11 @@
+
+ {$title}
+
+ {$url}:{$rev.id}
+ {$date}
+ {$rev.get_submitter()}
+
+{if !$create}
{trans 'Changes:'} {$rev.summary}
+{else}{trans 'Initial creation'}{/if}
+
+
From 16573daee0837e5cc0ba51bd352c6a79a36a8b60 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Tue, 20 Dec 2011 23:39:40 +0100
Subject: [PATCH 70/99] Migrating the timeline entries according to the new
model names was previously forgotten.
---
src/IDF/Migrations/21WikiPageRevisionName.php | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/IDF/Migrations/21WikiPageRevisionName.php b/src/IDF/Migrations/21WikiPageRevisionName.php
index aca13d1..a9ab466 100644
--- a/src/IDF/Migrations/21WikiPageRevisionName.php
+++ b/src/IDF/Migrations/21WikiPageRevisionName.php
@@ -36,6 +36,8 @@ function IDF_Migrations_21WikiPageRevisionName_up($params=null)
}
$db->execute('ALTER TABLE '.$db->pfx.'idf_wikirevisions RENAME TO '.$db->pfx.'idf_wikipagerevs');
+ $db->execute("UPDATE ".$db->pfx."idf_timeline SET model_class='IDF_Wiki_Page' WHERE model_class LIKE 'IDF_WikiPage'");
+ $db->execute("UPDATE ".$db->pfx."idf_timeline SET model_class='IDF_Wiki_PageRevision' WHERE model_class LIKE 'IDF_WikiRevision'");
}
function IDF_Migrations_21WikiPageRevisionName_down($params=null)
@@ -53,4 +55,6 @@ function IDF_Migrations_21WikiPageRevisionName_down($params=null)
}
$db->execute('ALTER TABLE '.$db->pfx.'idf_wikipagerevs RENAME TO '.$db->pfx.'idf_wikirevisions');
+ $db->execute("UPDATE ".$db->pfx."idf_timeline SET model_class='IDF_WikiPage' WHERE model_class LIKE 'IDF_Wiki_Page'");
+ $db->execute("UPDATE ".$db->pfx."idf_timeline SET model_class='IDF_WikiRevision' WHERE model_class LIKE 'IDF_Wiki_PageRevision'");
}
From c07aee6287dab92fb29fc838a5fafb5a80c2fb04 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Fri, 23 Dec 2011 01:03:07 +0100
Subject: [PATCH 71/99] Implement a basic, configurable project activity
taxonomy.
---
scripts/activitycron.php | 41 ++++++
src/IDF/ActivityTaxonomy.php | 156 +++++++++++++++++++++++
src/IDF/Migrations/23ProjectActivity.php | 42 ++++++
src/IDF/Migrations/Backup.php | 2 +
src/IDF/Migrations/Install.php | 2 +
src/IDF/ProjectActivity.php | 67 ++++++++++
src/IDF/conf/idf.php-dist | 29 +++++
src/IDF/relations.php | 1 +
8 files changed, 340 insertions(+)
create mode 100644 scripts/activitycron.php
create mode 100644 src/IDF/ActivityTaxonomy.php
create mode 100644 src/IDF/Migrations/23ProjectActivity.php
create mode 100644 src/IDF/ProjectActivity.php
diff --git a/scripts/activitycron.php b/scripts/activitycron.php
new file mode 100644
index 0000000..3a7717e
--- /dev/null
+++ b/scripts/activitycron.php
@@ -0,0 +1,41 @@
+ 1) {
+ $date = new DateTime($_SERVER['argv'][1]);
+}
+
+echo 'recalculating project activity for '.$date->format('Y-m-d')."\n";
+IDF_ActivityTaxonomy::recalculateTaxnomies($date);
+
diff --git a/src/IDF/ActivityTaxonomy.php b/src/IDF/ActivityTaxonomy.php
new file mode 100644
index 0000000..2feee34
--- /dev/null
+++ b/src/IDF/ActivityTaxonomy.php
@@ -0,0 +1,156 @@
+ $weight) {
+ $sectionWeights[$section] = $weight / (float) $allWeights;
+ }
+
+ //
+ // determine the date boundaries
+ //
+ $lookback = Pluf::f('activity_lookback', 0);
+ if ($lookback < 1) {
+ throw new LogicException('lookback must be greater or equal to 1');
+ }
+ $dateCopy = new DateTime();
+ $dateCopy->setTimestamp($date->getTimestamp());
+ $dateBoundaries = array(
+ $dateCopy->format('Y-m-d 23:59:59'),
+ $dateCopy->sub(new DateInterval('P'.$lookback.'D'))->format('Y-m-d 00:00:00')
+ );
+
+ //
+ // now recalculate the values for all projects
+ //
+ $projects = Pluf::factory('IDF_Project')->getList();
+ foreach ($projects as $project) {
+ self::recalculateTaxonomy($date, $project, $dateBoundaries, $sectionWeights);
+ }
+ }
+
+ private static function recalculateTaxonomy(DateTime $date, IDF_Project $project, array $dateBoundaries, array $sectionWeights)
+ {
+ $conf = new IDF_Conf();
+ $conf->setProject($project);
+
+ $sectionClasses = array(
+ 'source' => array('IDF_Commit'),
+ 'issues' => array('IDF_Issue'),
+ 'wiki' => array('IDF_Wiki_Page', 'IDF_Wiki_Resource'),
+ 'review' => array('IDF_Review'),
+ 'downloads' => array('IDF_Upload')
+ );
+
+ $value = 0;
+ foreach ($sectionWeights as $section => $weight) {
+ // skip closed / non-existant sections
+ if ($conf->getVal($section.'_access_rights', 'none') === 'none')
+ continue;
+
+ if (!array_key_exists($section, $sectionClasses))
+ continue;
+
+ $sectionValue = self::calculateActivityValue(
+ $dateBoundaries, $sectionClasses[$section], $project->id);
+ $value = ((1 - $weight) * $value) + ($weight * $sectionValue);
+ }
+
+ echo "project {$project->name} has an activity value of $value\n";
+
+ $sql = new Pluf_SQL('project=%s AND date=%s', array($project->id, $date->format('Y-m-d')));
+ $activity = Pluf::factory('IDF_ProjectActivity')->getOne(array('filter' => $sql->gen()));
+
+ if ($activity == null) {
+ $activity = new IDF_ProjectActivity();
+ $activity->project = $project;
+ $activity->date = $date->format('Y-m-d');
+ $activity->value = $value;
+ $activity->create();
+ } else {
+ $activity->value = $value;
+ $activity->update();
+ }
+ }
+
+ private static function calculateActivityValue(array $dateBoundaries, array $classes, $projectId)
+ {
+ $allCount = self::countActivityFor($dateBoundaries, $classes);
+ if ($allCount == 0) return 0;
+ $prjCount = self::countActivityFor($dateBoundaries, $classes, $projectId);
+ return $prjCount / (float) $allCount;
+ }
+
+ private static function countActivityFor(array $dateBoundaries, array $classes, $projectId = null)
+ {
+ static $cache = array();
+ $argIdent = md5(serialize(func_get_args()));
+ if (array_key_exists($argIdent, $cache)) {
+ return $cache[$argIdent];
+ }
+
+ $cache[$argIdent] = 0;
+ list($higher, $lower) = $dateBoundaries;
+ $sql = new Pluf_SQL('model_class IN ("'.implode('","', $classes).'") '.
+ 'AND creation_dtime >= %s AND creation_dtime <= %s',
+ array($lower, $higher));
+
+ if ($projectId !== null) {
+ $sql->SAnd(new Pluf_SQL('project=%s', array($projectId)));
+ }
+
+ $cache[$argIdent] = Pluf::factory('IDF_Timeline')->getCount(array('filter' => $sql->gen()));
+
+ return $cache[$argIdent];
+ }
+}
\ No newline at end of file
diff --git a/src/IDF/Migrations/23ProjectActivity.php b/src/IDF/Migrations/23ProjectActivity.php
new file mode 100644
index 0000000..bf4b2a7
--- /dev/null
+++ b/src/IDF/Migrations/23ProjectActivity.php
@@ -0,0 +1,42 @@
+model = new IDF_ProjectActivity();
+ $schema->createTables();
+}
+
+function IDF_Migrations_23ProjectActivity_down($params=null)
+{
+ $db = Pluf::db();
+ $schema = new Pluf_DB_Schema($db);
+ $schema->model = new IDF_ProjectActivity();
+ $schema->dropTables();
+}
diff --git a/src/IDF/Migrations/Backup.php b/src/IDF/Migrations/Backup.php
index 60f6f78..7d4083a 100644
--- a/src/IDF/Migrations/Backup.php
+++ b/src/IDF/Migrations/Backup.php
@@ -34,6 +34,7 @@ function IDF_Migrations_Backup_run($folder, $name=null)
{
$models = array(
'IDF_Project',
+ 'IDF_ProjectActivity',
'IDF_Tag',
'IDF_Issue',
'IDF_IssueComment',
@@ -83,6 +84,7 @@ function IDF_Migrations_Backup_restore($folder, $name)
{
$models = array(
'IDF_Project',
+ 'IDF_ProjectActivity',
'IDF_Tag',
'IDF_Issue',
'IDF_IssueComment',
diff --git a/src/IDF/Migrations/Install.php b/src/IDF/Migrations/Install.php
index cefdb30..74709b3 100644
--- a/src/IDF/Migrations/Install.php
+++ b/src/IDF/Migrations/Install.php
@@ -31,6 +31,7 @@ function IDF_Migrations_Install_setup($params=null)
{
$models = array(
'IDF_Project',
+ 'IDF_ProjectActivity',
'IDF_Tag',
'IDF_Issue',
'IDF_IssueComment',
@@ -112,6 +113,7 @@ function IDF_Migrations_Install_teardown($params=null)
'IDF_Issue',
'IDF_Tag',
'IDF_Commit',
+ 'IDF_ProjectActivity',
'IDF_Project',
'IDF_EmailAddress',
'IDF_IssueRelation',
diff --git a/src/IDF/ProjectActivity.php b/src/IDF/ProjectActivity.php
new file mode 100644
index 0000000..d994a6a
--- /dev/null
+++ b/src/IDF/ProjectActivity.php
@@ -0,0 +1,67 @@
+_a['table'] = 'idf_projectactivities';
+ $this->_a['model'] = __CLASS__;
+ $this->_a['cols'] = array(
+ // It is mandatory to have an "id" column.
+ 'id' =>
+ array(
+ 'type' => 'Pluf_DB_Field_Sequence',
+ 'blank' => true,
+ ),
+ 'project' =>
+ array(
+ 'type' => 'Pluf_DB_Field_Foreignkey',
+ 'model' => 'IDF_Project',
+ 'blank' => false,
+ 'verbose' => __('project'),
+ 'relate_name' => 'activities',
+ ),
+ 'date' =>
+ array(
+ 'type' => 'Pluf_DB_Field_Datetime',
+ 'blank' => false,
+ 'verbose' => __('date'),
+ ),
+ 'value' =>
+ array(
+ 'type' => 'Pluf_DB_Field_Float',
+ 'blank' => false,
+ 'verbose' => __('value'),
+ 'default' => 0,
+ ),
+ );
+ }
+}
diff --git a/src/IDF/conf/idf.php-dist b/src/IDF/conf/idf.php-dist
index a03aa1a..97bd441 100644
--- a/src/IDF/conf/idf.php-dist
+++ b/src/IDF/conf/idf.php-dist
@@ -515,5 +515,34 @@ $cfg['idf_strong_key_check'] = false;
# should really change the other end of your web hooks!
$cfg['webhook_processing'] = 'compat';
+# If IDF recalculates the activity index of the forge's projects, it does so
+# by looking at the created and updated items in a particular tab / section
+# for each project.
+#
+# You can now edit the weights that are applied to the calculation for each
+# section in order to give other things more precendence. For example, if you
+# do not use the documentation part to a great extent in most of your projects,
+# you can weight this section lower and get an overall better activity value.
+#
+# If a section is removed, then activity in this section is neglected during
+# the calculation. The same is true in case a section is disabled in the
+# project administration.
+$cfg['activity_section_weights'] = array(
+ 'source' => 4,
+ 'issues' => 2,
+ 'wiki' => 2,
+ 'downloads' => 1,
+ 'review' => 1,
+);
+
+# Here you can define the timespan in days how long the activity calculation
+# process should look into the history to get meaningful activity values for
+# each project.
+#
+# If you have many low-profile projects in your forge, i.e. projects that only
+# record very little activity, then it might be a good idea to bump this value
+# high enough to show a proper activity index for those projects as well.
+$cfg['activity_lookback'] = 7;
+
return $cfg;
diff --git a/src/IDF/relations.php b/src/IDF/relations.php
index 221bd81..b8ae4e3 100644
--- a/src/IDF/relations.php
+++ b/src/IDF/relations.php
@@ -22,6 +22,7 @@
# ***** END LICENSE BLOCK ***** */
$m = array();
+$m['IDF_ProjectActivity'] = array('relate_to' => array('IDF_Project'));
$m['IDF_Tag'] = array('relate_to' => array('IDF_Project'),
'relate_to_many' => array('IDF_Project'));
$m['IDF_Issue'] = array('relate_to' => array('IDF_Project', 'Pluf_User', 'IDF_Tag'),
From 6875e62942dbe582611890071e110536f34b60dd Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Fri, 23 Dec 2011 01:06:13 +0100
Subject: [PATCH 72/99] Add some more documentary to the cronjob.
---
scripts/activitycron.php | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/scripts/activitycron.php b/scripts/activitycron.php
index 3a7717e..43f91d9 100644
--- a/scripts/activitycron.php
+++ b/scripts/activitycron.php
@@ -25,6 +25,13 @@
* This script recalculates the "project activity" for all of the
* forge's projects for the given date.
* If no date is given, yesterday's date is used.
+ *
+ * This script should run once a day. You can configure its behaviour
+ * with $cfg['activity_section_weights'] and $cfg['activity_lookback'].
+ *
+ * If the script runs more than once with the same date argument,
+ * previously recorded project activity values are replaced with the
+ * newly created ones.
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
From da6df31ee9841a0724852d5a1d6aacf608c4538d Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Fri, 23 Dec 2011 17:37:29 +0100
Subject: [PATCH 73/99] Escape the path to the monotone binary in case it
contains whitespaces.
---
src/IDF/Scm/Monotone/Stdio.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/IDF/Scm/Monotone/Stdio.php b/src/IDF/Scm/Monotone/Stdio.php
index 5409764..a9e6c7b 100644
--- a/src/IDF/Scm/Monotone/Stdio.php
+++ b/src/IDF/Scm/Monotone/Stdio.php
@@ -119,7 +119,7 @@ class IDF_Scm_Monotone_Stdio implements IDF_Scm_Monotone_IStdio
$remote_db_access = Pluf::f('mtn_db_access', 'remote') == 'remote';
$cmd = Pluf::f('idf_exec_cmd_prefix', '') .
- Pluf::f('mtn_path', 'mtn') . ' ';
+ escapeshellarg(Pluf::f('mtn_path', 'mtn')) . ' ';
$opts = Pluf::f('mtn_opts', array());
foreach ($opts as $opt) {
From 608e7a40e4477296b72135752e7be83c5dc60b8c Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Sat, 24 Dec 2011 01:07:25 +0100
Subject: [PATCH 74/99] 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.
---
.../Migrations/24CurrentProjectActivity.php | 40 +++++++++++
src/IDF/Project.php | 22 +++++-
src/IDF/ProjectActivity.php | 11 +++
src/IDF/Views.php | 70 ++++++++++++-------
src/IDF/Views/Api.php | 11 ++-
src/IDF/conf/urls.php | 5 ++
.../idf/{index.html => listProjects.html} | 41 +++++++----
www/media/idf/css/style.css | 53 +++++++++++---
8 files changed, 193 insertions(+), 60 deletions(-)
create mode 100644 src/IDF/Migrations/24CurrentProjectActivity.php
rename src/IDF/templates/idf/{index.html => listProjects.html} (64%)
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
@@ -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'
+ ));
}
/**
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/listProjects.html
similarity index 64%
rename from src/IDF/templates/idf/index.html
rename to src/IDF/templates/idf/listProjects.html
index fba3614..52cb37b 100644
--- a/src/IDF/templates/idf/index.html
+++ b/src/IDF/templates/idf/listProjects.html
@@ -10,30 +10,41 @@
{trans 'Create Project'}
{/if}
{else}
{foreach $projects as $p}
-
-
-
-
- {if $p.private}
-
- {/if}
-
+
+
+
+
+ {if $p.private}
+
+ {/if}
+ {if $p.current_activity_value}
+
+ {/if}
+
{$p}
{assign $url = $p.external_project_url}
{if $url != ''}
{/if}
- {if $p.private} - {trans 'Private project'}{/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}
-
{$p.shortdesc}
-
{/foreach}
{/if}
{/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 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 {
From 6e305eb5418a8690b9b6f5de2a0b457954376a88 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Sat, 24 Dec 2011 02:45:01 +0100
Subject: [PATCH 75/99] 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.
---
src/IDF/Forge.php | 19 ++++++++
src/IDF/Project.php | 10 +++--
src/IDF/Tag.php | 12 +++++
src/IDF/Views.php | 45 ++++++++++++++-----
src/IDF/conf/urls.php | 2 +-
src/IDF/templates/idf/issues/search.html | 4 +-
src/IDF/templates/idf/listProjects.html | 56 ++++++++++++++++++------
src/IDF/templates/idf/tags-cloud.html | 4 +-
www/media/idf/css/style.css | 28 +++++++++---
9 files changed, 138 insertions(+), 42 deletions(-)
diff --git a/src/IDF/Forge.php b/src/IDF/Forge.php
index 97ce9c1..fc4d430 100644
--- a/src/IDF/Forge.php
+++ b/src/IDF/Forge.php
@@ -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;
+ }
}
\ No newline at end of file
diff --git a/src/IDF/Project.php b/src/IDF/Project.php
index fc8a340..8cafd2c 100644
--- a/src/IDF/Project.php
+++ b/src/IDF/Project.php
@@ -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'
diff --git a/src/IDF/Tag.php b/src/IDF/Tag.php
index 24441a6..b9fa25d 100644
--- a/src/IDF/Tag.php
+++ b/src/IDF/Tag.php
@@ -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(
diff --git a/src/IDF/Views.php b/src/IDF/Views.php
index bd1569f..3eaa01c 100644
--- a/src/IDF/Views.php
+++ b/src/IDF/Views.php
@@ -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')
diff --git a/src/IDF/conf/urls.php b/src/IDF/conf/urls.php
index 1755ae6..5995e5b 100644
--- a/src/IDF/conf/urls.php
+++ b/src/IDF/conf/urls.php
@@ -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');
diff --git a/src/IDF/templates/idf/issues/search.html b/src/IDF/templates/idf/issues/search.html
index 4c0b720..4ffce24 100644
--- a/src/IDF/templates/idf/issues/search.html
+++ b/src/IDF/templates/idf/issues/search.html
@@ -22,11 +22,11 @@
{$tag.class}: {$tag.name}
{/blocktrans}
{else}
{* yes, this is duplicated from tags-cloud.html, but the code there cannot be easily overridden *}
-
From f4058ddd69be63318098b64cf722e472b694493b Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Sat, 24 Dec 2011 15:50:41 +0100
Subject: [PATCH 79/99] Implement a simple form to save custom markdown-enabled
content in the forge's admin area that is displayed instead of the default
project list. Sanitize the URLs that we're using and make a redirect to the
listProjects page when no custom forge page is enabled.
---
src/IDF/Forge.php | 16 ++++++
src/IDF/Form/Admin/ForgeConf.php | 47 ++++++++++++++++
src/IDF/Middleware.php | 3 ++
src/IDF/Template/MarkdownForge.php | 34 ++++++++++++
src/IDF/Views.php | 35 +++++++++---
src/IDF/Views/Admin.php | 38 +++++++++----
src/IDF/conf/urls.php | 12 ++++-
src/IDF/templates/idf/gadmin/base.html | 1 +
src/IDF/templates/idf/gadmin/forge/base.html | 5 ++
src/IDF/templates/idf/gadmin/forge/index.html | 54 +++++++++++++++++++
.../idf/{gadmin/home.html => index.html} | 10 ++--
src/IDF/templates/idf/listProjects.html | 12 ++---
src/IDF/templates/idf/main-menu.html | 6 ++-
13 files changed, 242 insertions(+), 31 deletions(-)
create mode 100644 src/IDF/Form/Admin/ForgeConf.php
create mode 100644 src/IDF/Template/MarkdownForge.php
create mode 100644 src/IDF/templates/idf/gadmin/forge/base.html
create mode 100644 src/IDF/templates/idf/gadmin/forge/index.html
rename src/IDF/templates/idf/{gadmin/home.html => index.html} (56%)
diff --git a/src/IDF/Forge.php b/src/IDF/Forge.php
index fc4d430..832323d 100644
--- a/src/IDF/Forge.php
+++ b/src/IDF/Forge.php
@@ -70,4 +70,20 @@ class IDF_Forge
}
return $tags;
}
+
+ public function setCustomForgePageEnabled($enabled) {
+ $this->conf->setVal('custom_forge_page_enabled', $enabled);
+ }
+
+ public function isCustomForgePageEnabled($default = false) {
+ return $this->conf->getVal('custom_forge_page_enabled', $default);
+ }
+
+ public function getCustomForgePageContent($default = '') {
+ return $this->conf->getVal('custom_forge_page_content', $default);
+ }
+
+ public function setCustomForgePageContent($content) {
+ $this->conf->setVal('custom_forge_page_content', $content);
+ }
}
\ No newline at end of file
diff --git a/src/IDF/Form/Admin/ForgeConf.php b/src/IDF/Form/Admin/ForgeConf.php
new file mode 100644
index 0000000..669ac67
--- /dev/null
+++ b/src/IDF/Form/Admin/ForgeConf.php
@@ -0,0 +1,47 @@
+fields['enabled'] = new Pluf_Form_Field_Boolean(
+ array('required' => false,
+ 'label' => __('Custom forge page enabled'),
+ 'widget' => 'Pluf_Form_Widget_CheckboxInput',
+ ));
+ $this->fields['content'] = new Pluf_Form_Field_Varchar(
+ array('required' => true,
+ 'label' => __('Content'),
+ 'widget' => 'Pluf_Form_Widget_TextareaInput',
+ 'widget_attrs' => array(
+ 'cols' => 68,
+ 'rows' => 26,
+ ),
+ ));
+ }
+}
diff --git a/src/IDF/Middleware.php b/src/IDF/Middleware.php
index 5e44abe..c59217b 100644
--- a/src/IDF/Middleware.php
+++ b/src/IDF/Middleware.php
@@ -85,6 +85,7 @@ class IDF_Middleware
'issuetext' => 'IDF_Template_IssueComment',
'timeline' => 'IDF_Template_TimelineFragment',
'markdown' => 'IDF_Template_Markdown',
+ 'markdown_forge' => 'IDF_Template_MarkdownForge',
'showuser' => 'IDF_Template_ShowUser',
'ashowuser' => 'IDF_Template_AssignShowUser',
'appversion' => 'IDF_Template_AppVersion',
@@ -102,6 +103,7 @@ class IDF_Middleware
function IDF_Middleware_ContextPreProcessor($request)
{
+ $forge = IDF_Forge::instance();
$c = array();
$c['request'] = $request;
$c['isAdmin'] = ($request->user->administrator or $request->user->staff);
@@ -115,6 +117,7 @@ function IDF_Middleware_ContextPreProcessor($request)
}
$c['usherConfigured'] = Pluf::f("mtn_usher_conf", null) !== null;
$c['allProjects'] = IDF_Views::getProjects($request->user);
+ $c['customForgePageEnabled'] = $forge->isCustomForgePageEnabled();
return $c;
}
diff --git a/src/IDF/Template/MarkdownForge.php b/src/IDF/Template/MarkdownForge.php
new file mode 100644
index 0000000..58516b8
--- /dev/null
+++ b/src/IDF/Template/MarkdownForge.php
@@ -0,0 +1,34 @@
+go(Pluf_Text_MarkDown_parse($text));
+ }
+}
+
diff --git a/src/IDF/Views.php b/src/IDF/Views.php
index 3eaa01c..ecc526f 100644
--- a/src/IDF/Views.php
+++ b/src/IDF/Views.php
@@ -36,19 +36,40 @@ class IDF_Views
*/
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
- $match = array('', 'all', 'name');
- return $this->listProjects($request, $match);
+ $forge = IDF_Forge::instance();
+ if (!$forge->isCustomForgePageEnabled()) {
+ $url = Pluf_HTTP_URL_urlForView('IDF_Views::listProjects');
+ return new Pluf_HTTP_Response_Redirect($url);
+ }
+
+ return Pluf_Shortcuts_RenderToResponse('idf/index.html',
+ array('page_title' => __('Welcome'),
+ 'content' => $forge->getCustomForgePageContent(),
+ ),
+ $request);
}
/**
- * List all the projects managed by InDefero.
+ * List all projects unfiltered
*
- * Only the public projects are listed or the private with correct
- * rights.
+ * @param unknown_type $request
+ * @param unknown_type $match
+ * @return Pluf_HTTP_Response
*/
public function listProjects($request, $match)
+ {
+ $match = array('', 'all', 'name');
+ return $this->listProjectsByLabel($request, $match);
+ }
+
+ /**
+ * List projects, optionally filtered by label
+ *
+ * @param unknown_type $request
+ * @param unknown_type $match
+ * @return Pluf_HTTP_Response
+ */
+ public function listProjectsByLabel($request, $match)
{
list(, $tagId, $order) = $match;
diff --git a/src/IDF/Views/Admin.php b/src/IDF/Views/Admin.php
index 8176fd5..7e78698 100644
--- a/src/IDF/Views/Admin.php
+++ b/src/IDF/Views/Admin.php
@@ -32,20 +32,40 @@ Pluf::loadFunction('Pluf_Shortcuts_GetFormForModel');
class IDF_Views_Admin
{
/**
- * Home page of the administration.
- *
- * It should provide an overview of the forge status.
+ * Start page of the administration.
*/
- public $home_precond = array('Pluf_Precondition::staffRequired');
- public function home($request, $match)
+ public $forge_precond = array('Pluf_Precondition::staffRequired');
+ public function forge($request, $match)
{
$title = __('Forge Management');
- return Pluf_Shortcuts_RenderToResponse('idf/gadmin/home.html',
+ $forge = IDF_Forge::instance();
+ if ($request->method == 'POST') {
+ $form = new IDF_Form_Admin_ForgeConf($request->POST);
+ if ($form->isValid()) {
+ $forge->setCustomForgePageEnabled($form->cleaned_data['enabled']);
+ $forge->setCustomForgePageContent($form->cleaned_data['content']);
+ $request->user->setMessage(__('The forge configuration has been saved.'));
+ $url = Pluf_HTTP_URL_urlForView('IDF_Views_Admin::forge');
+ return new Pluf_HTTP_Response_Redirect($url);
+ }
+ } else {
+ $params = array();
+ $params['enabled'] = $forge->isCustomForgePageEnabled();
+ if (($content = $forge->getCustomForgePageContent(false)) !== false) {
+ $params['content'] = $content;
+ }
+ if (count($params) == 0) {
+ $params = null; //Nothing in the db, so new form.
+ }
+ $form = new IDF_Form_Admin_ForgeConf($params);
+ }
+ return Pluf_Shortcuts_RenderToResponse('idf/gadmin/forge/index.html',
array(
- 'page_title' => $title,
- ),
+ 'page_title' => $title,
+ 'form' => $form,
+ ),
$request);
- }
+ }
/**
* Projects overview.
diff --git a/src/IDF/conf/urls.php b/src/IDF/conf/urls.php
index 5995e5b..eac28ae 100644
--- a/src/IDF/conf/urls.php
+++ b/src/IDF/conf/urls.php
@@ -29,11 +29,16 @@ $ctl[] = array('regex' => '#^/$#',
'model' => 'IDF_Views',
'method' => 'index');
-$ctl[] = array('regex' => '#^/label/(\w+)/(\w+)/$#',
+$ctl[] = array('regex' => '#^/projects/$#',
'base' => $base,
'model' => 'IDF_Views',
'method' => 'listProjects');
+$ctl[] = array('regex' => '#^/projects/label/(\w+)/(\w+)/$#',
+ 'base' => $base,
+ 'model' => 'IDF_Views',
+ 'method' => 'listProjectsByLabel');
+
$ctl[] = array('regex' => '#^/login/$#',
'base' => $base,
'model' => 'IDF_Views',
@@ -473,6 +478,11 @@ $ctl[] = array('regex' => '#^/api/$#',
// ---------- FORGE ADMIN --------------------------------
+$ctl[] = array('regex' => '#^/admin/forge/$#',
+ 'base' => $base,
+ 'model' => 'IDF_Views_Admin',
+ 'method' => 'forge');
+
$ctl[] = array('regex' => '#^/admin/projects/$#',
'base' => $base,
'model' => 'IDF_Views_Admin',
diff --git a/src/IDF/templates/idf/gadmin/base.html b/src/IDF/templates/idf/gadmin/base.html
index 20249ee..6a6ab92 100644
--- a/src/IDF/templates/idf/gadmin/base.html
+++ b/src/IDF/templates/idf/gadmin/base.html
@@ -39,6 +39,7 @@
{include 'idf/main-menu.html'}