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.
This commit is contained in:
Thomas Keller 2011-12-24 15:50:41 +01:00
parent 17c6ba97d6
commit f4058ddd69
13 changed files with 242 additions and 31 deletions

View File

@ -70,4 +70,20 @@ class IDF_Forge
} }
return $tags; 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);
}
} }

View File

@ -0,0 +1,47 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright(C) 2008-2011 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
#(at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Configuration of the forge's start page.
*/
class IDF_Form_Admin_ForgeConf extends Pluf_Form
{
public function initFields($extra=array())
{
$this->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,
),
));
}
}

View File

@ -85,6 +85,7 @@ class IDF_Middleware
'issuetext' => 'IDF_Template_IssueComment', 'issuetext' => 'IDF_Template_IssueComment',
'timeline' => 'IDF_Template_TimelineFragment', 'timeline' => 'IDF_Template_TimelineFragment',
'markdown' => 'IDF_Template_Markdown', 'markdown' => 'IDF_Template_Markdown',
'markdown_forge' => 'IDF_Template_MarkdownForge',
'showuser' => 'IDF_Template_ShowUser', 'showuser' => 'IDF_Template_ShowUser',
'ashowuser' => 'IDF_Template_AssignShowUser', 'ashowuser' => 'IDF_Template_AssignShowUser',
'appversion' => 'IDF_Template_AppVersion', 'appversion' => 'IDF_Template_AppVersion',
@ -102,6 +103,7 @@ class IDF_Middleware
function IDF_Middleware_ContextPreProcessor($request) function IDF_Middleware_ContextPreProcessor($request)
{ {
$forge = IDF_Forge::instance();
$c = array(); $c = array();
$c['request'] = $request; $c['request'] = $request;
$c['isAdmin'] = ($request->user->administrator or $request->user->staff); $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['usherConfigured'] = Pluf::f("mtn_usher_conf", null) !== null;
$c['allProjects'] = IDF_Views::getProjects($request->user); $c['allProjects'] = IDF_Views::getProjects($request->user);
$c['customForgePageEnabled'] = $forge->isCustomForgePageEnabled();
return $c; return $c;
} }

View File

@ -0,0 +1,34 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
Pluf::loadFunction('Pluf_Text_MarkDown_parse');
class IDF_Template_MarkdownForge extends Pluf_Template_Tag
{
function start($text)
{
$filter = new IDF_Template_MarkdownPrefilter();
echo $filter->go(Pluf_Text_MarkDown_parse($text));
}
}

View File

@ -36,19 +36,40 @@ class IDF_Views
*/ */
public function index($request, $match) public function index($request, $match)
{ {
// TODO: add a switch here later on to determine whether the project list $forge = IDF_Forge::instance();
// or a custom start page should be displayed if (!$forge->isCustomForgePageEnabled()) {
$match = array('', 'all', 'name'); $url = Pluf_HTTP_URL_urlForView('IDF_Views::listProjects');
return $this->listProjects($request, $match); 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 * @param unknown_type $request
* rights. * @param unknown_type $match
* @return Pluf_HTTP_Response
*/ */
public function listProjects($request, $match) 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; list(, $tagId, $order) = $match;

View File

@ -32,20 +32,40 @@ Pluf::loadFunction('Pluf_Shortcuts_GetFormForModel');
class IDF_Views_Admin class IDF_Views_Admin
{ {
/** /**
* Home page of the administration. * Start page of the administration.
*
* It should provide an overview of the forge status.
*/ */
public $home_precond = array('Pluf_Precondition::staffRequired'); public $forge_precond = array('Pluf_Precondition::staffRequired');
public function home($request, $match) public function forge($request, $match)
{ {
$title = __('Forge Management'); $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( array(
'page_title' => $title, 'page_title' => $title,
), 'form' => $form,
),
$request); $request);
} }
/** /**
* Projects overview. * Projects overview.

View File

@ -29,11 +29,16 @@ $ctl[] = array('regex' => '#^/$#',
'model' => 'IDF_Views', 'model' => 'IDF_Views',
'method' => 'index'); 'method' => 'index');
$ctl[] = array('regex' => '#^/label/(\w+)/(\w+)/$#', $ctl[] = array('regex' => '#^/projects/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views', 'model' => 'IDF_Views',
'method' => 'listProjects'); 'method' => 'listProjects');
$ctl[] = array('regex' => '#^/projects/label/(\w+)/(\w+)/$#',
'base' => $base,
'model' => 'IDF_Views',
'method' => 'listProjectsByLabel');
$ctl[] = array('regex' => '#^/login/$#', $ctl[] = array('regex' => '#^/login/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views', 'model' => 'IDF_Views',
@ -473,6 +478,11 @@ $ctl[] = array('regex' => '#^/api/$#',
// ---------- FORGE ADMIN -------------------------------- // ---------- FORGE ADMIN --------------------------------
$ctl[] = array('regex' => '#^/admin/forge/$#',
'base' => $base,
'model' => 'IDF_Views_Admin',
'method' => 'forge');
$ctl[] = array('regex' => '#^/admin/projects/$#', $ctl[] = array('regex' => '#^/admin/projects/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Admin', 'model' => 'IDF_Views_Admin',

View File

@ -39,6 +39,7 @@
{include 'idf/main-menu.html'} {include 'idf/main-menu.html'}
<div id="header"> <div id="header">
<div id="main-tabs"> <div id="main-tabs">
<a href="{url 'IDF_Views_Admin::forge'}"{block tabforge}{/block}>{trans 'Forge'}</a>
<a href="{url 'IDF_Views_Admin::projects'}"{block tabprojects}{/block}>{trans 'Projects'}</a> <a href="{url 'IDF_Views_Admin::projects'}"{block tabprojects}{/block}>{trans 'Projects'}</a>
<a href="{url 'IDF_Views_Admin::users'}"{block tabusers}{/block}>{trans 'People'}</a> <a href="{url 'IDF_Views_Admin::users'}"{block tabusers}{/block}>{trans 'People'}</a>
{if $usherConfigured} {if $usherConfigured}

View File

@ -0,0 +1,5 @@
{extends "idf/gadmin/base.html"}
{block tabforge} class="active"{/block}
{block subtabs}
<a {if $inIndex}class="active" {/if}href="{url 'IDF_Views_Admin::forge'}">{trans 'Frontpage'}</a>
{/block}

View File

@ -0,0 +1,54 @@
{extends "idf/gadmin/forge/base.html"}
{block docclass}yui-t3{assign $inIndex=true}{/block}
{block body}
<form method="post" action=".">
<table class="form" summary="">
<colgroup>
<col width="20" />
<col width="*" />
</colgroup>
<tr>
<td>{$form.f.enabled|unsafe}</td>
<td>
{$form.f.enabled.labelTag}
{if $form.f.enabled.errors}{$form.f.enabled.fieldErrors}{/if}
</td>
</tr>
<tr>
<td colspan="2">
{if $form.f.content.errors}{$form.f.content.fieldErrors}{/if}
{$form.f.content|unsafe}
</td>
<tr>
<td colspan="2">
<input type="submit" value="{trans 'Save Changes'}" name="submit" />
</td>
</tr>
</table>
</form>
<script type="text/javascript" charset="utf-8">
// <!-- {literal}
$(document).ready(function() {
var handler = function() {
if ($(this).is(':checked')) {
$('#id_content').removeAttr('readonly');
} else {
$('#id_content').attr('readonly', 'readonly');
}
};
$('#id_enabled').bind('click', handler);
handler.call($('#id_enabled'));
});
// {/literal} -->
</script>
{/block}
{block context}
<div class="issue-submit-info">
<p>{blocktrans}You can define a custom forge start page that is displayed instead of the standard project listing.{/blocktrans}</p>
</div>
{/block}

View File

@ -1,14 +1,14 @@
{extends "idf/gadmin/base.html"} {extends "idf/base-simple.html"}
{block docclass}yui-t2{/block} {block docclass}yui-t2{/block}
{block tabhome} class="active"{/block} {block tabhome} class="active"{/block}
{block subtabs} {block subtabs}
{trans 'Welcome'} <a href="{url 'IDF_Views::index'}" class="active">{trans 'Home'}</a> |
<a href="{url 'IDF_Views::listProjects', array('all', 'name')}" class="active">{trans 'Projects'}</a>
{/block} {/block}
{block body} {block body}
<p>{blocktrans}You have here access to the administration of the forge.{/blocktrans}</p> {markdown_forge $content}
{/block} {/block}
{block context}
{/block} {block context}{/block}
{block foot}<div id="branding">Powered by <a href="http://www.indefero.net" title="InDefero, bug tracking and more">InDefero</a>,<br />a <a href="http://www.ceondo.com">Céondo Ltd</a> initiative.</div>{/block} {block foot}<div id="branding">Powered by <a href="http://www.indefero.net" title="InDefero, bug tracking and more">InDefero</a>,<br />a <a href="http://www.ceondo.com">Céondo Ltd</a> initiative.</div>{/block}

View File

@ -1,7 +1,5 @@
{extends "idf/base-simple.html"} {extends "idf/base-simple.html"}
{block docclass}yui-t2{/block} {block docclass}yui-t2{/block}
{block tabhome} class="active"{/block}
{block subtabs}<a href="{url 'IDF_Views::index'}" class="active">{trans 'Projects'}</a>{/block}
{block body} {block body}
{if $projects.count() == 0} {if $projects.count() == 0}
<p>{trans 'No projects managed with InDefero were found.'}</p> <p>{trans 'No projects managed with InDefero were found.'}</p>
@ -41,7 +39,7 @@
{if count($tags) == 0}{trans 'n/a'}{else} {if count($tags) == 0}{trans 'n/a'}{else}
{foreach $p.get_tags_list() as $idx => $label} {foreach $p.get_tags_list() as $idx => $label}
{if $idx != 0}, {/if} {if $idx != 0}, {/if}
<a class="label" href="{url 'IDF_Views::listProjects', array($label->id, $order)}">{$label}</a> <a class="label" href="{url 'IDF_Views::listProjectsByLabel', array($label->id, $order)}">{$label}</a>
{/foreach} {/foreach}
{/if} {/if}
</p> </p>
@ -58,11 +56,11 @@
<dl class="tagscloud smaller">{foreach $projectLabels as $class => $labels} <dl class="tagscloud smaller">{foreach $projectLabels as $class => $labels}
<dt class="label">{$class}</dt> <dt class="label">{$class}</dt>
{foreach $labels as $idx => $label} {foreach $labels as $idx => $label}
<dd><a href="{url 'IDF_Views::listProjects', array($label->id, $order)}" class="label" title="{blocktrans $label.project_count}1 project{plural}{$label.project_count} projects{/blocktrans}">{$label.name}{if $idx != count($labels) - 1},{/if}</a></dd> <dd><a href="{url 'IDF_Views::listProjectsByLabel', array($label->id, $order)}" class="label" title="{blocktrans $label.project_count}1 project{plural}{$label.project_count} projects{/blocktrans}">{$label.name}{if $idx != count($labels) - 1},{/if}</a></dd>
{/foreach} {/foreach}
{/foreach}</dl> {/foreach}</dl>
{if $tag} {if $tag}
<p class="smaller"><a href="{url 'IDF_Views::listProjects', array('all', $order)}"> <p class="smaller"><a href="{url 'IDF_Views::listProjectsByLabel', array('all', $order)}">
{blocktrans}Remove filter for {$tag}{/blocktrans}</a></p> {blocktrans}Remove filter for {$tag}{/blocktrans}</a></p>
{/if} {/if}
{/if} {/if}
@ -72,10 +70,10 @@
{assign $labelPart = 'all'} {assign $labelPart = 'all'}
{if $tag}{assign $labelPart = $tag->id}{/if} {if $tag}{assign $labelPart = $tag->id}{/if}
<p class="smaller"> <p class="smaller">
{if $order != 'name'}<a href="{url 'IDF_Views::listProjects', array($labelPart, 'name')}">{/if} {if $order != 'name'}<a href="{url 'IDF_Views::listProjectsByLabel', array($labelPart, 'name')}">{/if}
{trans 'By name'}{if $order != 'name'}</a>{/if} {trans 'By name'}{if $order != 'name'}</a>{/if}
&ndash; &ndash;
{if $order != 'activity'}<a href="{url 'IDF_Views::listProjects', array($labelPart, 'activity')}">{/if} {if $order != 'activity'}<a href="{url 'IDF_Views::listProjectsByLabel', array($labelPart, 'activity')}">{/if}
{trans 'By activity'}{if $order != 'activity'}</a>{/if} {trans 'By activity'}{if $order != 'activity'}</a>{/if}
</p> </p>
<br /> <br />

View File

@ -5,12 +5,14 @@
<li>{blocktrans}Welcome, <strong><a class="userw" href="{$url}">{$user}</a></strong>.{/blocktrans} <li>{blocktrans}Welcome, <strong><a class="userw" href="{$url}">{$user}</a></strong>.{/blocktrans}
<a href="{url 'IDF_Views::logout'}">{trans 'Sign Out'}</a></li>{else}<li> <a href="{url 'IDF_Views::logout'}">{trans 'Sign Out'}</a></li>{else}<li>
<a href="{url 'IDF_Views::login'}">{trans 'Sign in or create your account'}</a></li> <a href="{url 'IDF_Views::login'}">{trans 'Sign in or create your account'}</a></li>
{/if}<li id="project-list"><a href="{url 'IDF_Views::index'}">{trans 'Project List'} &#x25be;</a> {/if}{if $customForgePageEnabled}
<li><a href="{url 'IDF_Views::index'}">{trans 'Home'}</a></li>
{/if}<li id="project-list"><a href="{url 'IDF_Views::listProjects'}">{trans 'Project List'} &#x25be;</a>
{if $allProjects.count() != 0} {if $allProjects.count() != 0}
<ul>{foreach $allProjects as $p} <ul>{foreach $allProjects as $p}
<li><a href="{url 'IDF_Views_Project::home', array($p.shortname)}"><img class="logo" src="{url 'IDF_Views_Project::logo', array($p.shortname)}" alt="{trans 'Project logo'}" />{if $p.private}<img class="lock" src="{media '/idf/img/lock.png'}" alt="{trans 'Private project'}" />{/if}{$p}</a></li> <li><a href="{url 'IDF_Views_Project::home', array($p.shortname)}"><img class="logo" src="{url 'IDF_Views_Project::logo', array($p.shortname)}" alt="{trans 'Project logo'}" />{if $p.private}<img class="lock" src="{media '/idf/img/lock.png'}" alt="{trans 'Private project'}" />{/if}{$p}</a></li>
{/foreach}</ul> {/foreach}</ul>
{/if}</li>{if $isAdmin}<li><a href="{url 'IDF_Views_Admin::projects'}">{trans 'Forge Management'}</a></li>{/if}<li> {/if}</li>{if $isAdmin}<li><a href="{url 'IDF_Views_Admin::forge'}">{trans 'Forge Management'}</a></li>{/if}<li>
<a href="{url 'IDF_Views::faq'}" title="{trans 'Help and accessibility features'}">{trans 'Help'}</a></li> <a href="{url 'IDF_Views::faq'}" title="{trans 'Help and accessibility features'}">{trans 'Help'}</a></li>
</ul> </ul>