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 save($commit=true) + public function clean_external_project_url() { - $conf = $this->project->getConf(); + 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) + { // 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; @@ -553,6 +553,22 @@ GROUP BY uid"; } /** + * 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. * * This returns an associative array with number of tickets, 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}{trans 'Private project'}{/if}{$p}

{/if} + {if $project}

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

{/if} {include 'idf/main-menu.html'}