Add predefined templates for task descriptions

This commit is contained in:
Frederic Guillot 2017-11-02 15:41:58 -07:00
parent 44ae87ac0e
commit 648dc6bcfb
49 changed files with 655 additions and 17 deletions

View File

@ -6,6 +6,10 @@ Breaking changes:
* Remove feature "Allow everybody to access to this project" (You must define members and groups)
* Composer dependencies are now included in the repository (except development dependencies)
New features:
* Add predefined templates for task descriptions
Improvements:
* You can get an archive of Kanboard by using the download button in Github or the command git archive

View File

@ -0,0 +1,116 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\PageNotFoundException;
/**
* Predefined Task Description Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class PredefinedTaskDescriptionController extends BaseController
{
public function create(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$this->response->html($this->template->render('predefined_task_description/create', array(
'values' => $values,
'errors' => $errors,
'project' => $project,
)));
}
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
list($valid, $errors) = $this->predefinedTaskDescriptionValidator->validate($values);
if ($valid) {
if ($this->predefinedTaskDescriptionModel->create($project['id'], $values['title'], $values['description']) !== false) {
$this->flash->success(t('Template created successfully.'));
} else {
$this->flash->failure(t('Unable to create this template.'));
}
$this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true);
} else {
$this->create($values, $errors);
}
}
public function edit(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$template = $this->predefinedTaskDescriptionModel->getById($project['id'], $this->request->getIntegerParam('id'));
$this->response->html($this->template->render('predefined_task_description/edit', array(
'values' => empty($values) ? $template : $values,
'template' => $template,
'errors' => $errors,
'project' => $project,
)));
}
public function update()
{
$project = $this->getProject();
$template = $this->getTemplate($project);
$values = $this->request->getValues();
list($valid, $errors) = $this->predefinedTaskDescriptionValidator->validate($values);
if ($valid) {
if ($this->predefinedTaskDescriptionModel->update($project['id'], $template['id'], $values['title'], $values['description']) !== false) {
$this->flash->success(t('Template updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this template.'));
}
$this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true);
} else {
$this->edit($values, $errors);
}
}
public function confirm()
{
$project = $this->getProject();
$template = $this->getTemplate($project);
$this->response->html($this->template->render('predefined_task_description/remove', array(
'template' => $template,
'project' => $project,
)));
}
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
$template = $this->getTemplate($project);
if ($this->predefinedTaskDescriptionModel->remove($project['id'], $template['id'])) {
$this->flash->success(t('Template removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this template.'));
}
$this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true);
}
protected function getTemplate(array $project)
{
$template = $this->predefinedTaskDescriptionModel->getById($project['id'], $this->request->getIntegerParam('id'));
if (empty($template)) {
throw new PageNotFoundException();
}
return $template;
}
}

View File

@ -10,13 +10,6 @@ namespace Kanboard\Controller;
*/
class ProjectPredefinedContentController extends BaseController
{
/**
* Edit project
*
* @access public
* @param array $values
* @param array $errors
*/
public function show(array $values = array(), array $errors = array())
{
$project = $this->getProject();
@ -25,15 +18,11 @@ class ProjectPredefinedContentController extends BaseController
'values' => empty($values) ? $project : $values,
'errors' => $errors,
'project' => $project,
'title' => t('Predefined Contents')
'predefined_task_descriptions' => $this->predefinedTaskDescriptionModel->getAll($project['id']),
'title' => t('Predefined Contents'),
)));
}
/**
* Validate and update a project
*
* @access public
*/
public function update()
{
$project = $this->getProject();

View File

@ -105,6 +105,7 @@ use Pimple\Container;
* @property \Kanboard\Model\LinkModel $linkModel
* @property \Kanboard\Model\NotificationModel $notificationModel
* @property \Kanboard\Model\PasswordResetModel $passwordResetModel
* @property \Kanboard\Model\PredefinedTaskDescriptionModel $predefinedTaskDescriptionModel
* @property \Kanboard\Model\ProjectModel $projectModel
* @property \Kanboard\Model\ProjectActivityModel $projectActivityModel
* @property \Kanboard\Model\ProjectDuplicationModel $projectDuplicationModel
@ -179,6 +180,7 @@ use Pimple\Container;
* @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator
* @property \Kanboard\Validator\TaskValidator $taskValidator
* @property \Kanboard\Validator\UserValidator $userValidator
* @property \Kanboard\Validator\PredefinedTaskDescriptionValidator $predefinedTaskDescriptionValidator
* @property \Kanboard\Import\TaskImport $taskImport
* @property \Kanboard\Import\UserImport $userImport
* @property \Kanboard\Export\SubtaskExport $subtaskExport

View File

@ -61,6 +61,30 @@ class TaskHelper extends Base
return $this->helper->form->textEditor('description', $values, $errors, array('tabindex' => 2));
}
public function renderDescriptionTemplateDropdown($projectId)
{
$templates = $this->predefinedTaskDescriptionModel->getAll($projectId);
if (! empty($templates)) {
$html = '<div class="dropdown">';
$html .= '<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-floppy-o fa-fw" aria-hidden="true"></i>'.t('Description Templates').' <i class="fa fa-caret-down" aria-hidden="true"></i></a>';
$html .= '<ul>';
foreach ($templates as $template) {
$html .= '<li>';
$html .= '<a href="#" data-template-target="textarea[name=description]" data-template="'.$this->helper->text->e($template['description']).'" class="js-template">';
$html .= $this->helper->text->e($template['title']);
$html .= '</a>';
$html .= '</li>';
}
$html .= '</ul></div>';
return $html;
}
return '';
}
public function renderTagField(array $project, array $tags = array())
{
$options = $this->tagModel->getAssignableList($project['id']);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
'Enter one subtask by line.' => 'Entrez une sous-tâche par ligne.',
'Predefined Contents' => 'Contenus prédéfini',
'Predefined contents' => 'Contenus prédéfini',
'Predefined Task Description' => 'Modèles de description de tâches',
'Do you really want to remove this template? "%s"' => 'Voulez-vous vraiment supprimer ce modèle ? « %s »',
'Add predefined task description' => 'Ajouter un modèle de description de tâche',
'Predefined Task Descriptions' => 'Modèles de description de tâches',
'Template created successfully.' => 'Modèle créé avec succès.',
'Unable to create this template.' => 'Impossible de créer ce modèle.',
'Template updated successfully.' => 'Modèle mis à jour avec succès.',
'Unable to update this template.' => 'Impossible de mettre à jour ce modèle.',
'Template removed successfully.' => 'Modèle supprimé avec succès.',
'Unable to remove this template.' => 'Impossible de supprimer ce modèle.',
'Description Templates' => 'Modèles de description',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
'Enter one subtask by line.' => '1行に1件のサブタスクを入力',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
'Enter one subtask by line.' => 'Escreva uma subtarefa por linha.',
'Predefined Contents' => 'Conteúdos predefinidos',
'Predefined contents' => 'Conteúdos predefinidos',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
'Enter one subtask by line.' => 'Записывайте по одной подзадаче на строку.',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1351,4 +1351,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -1345,4 +1345,15 @@ return array(
// 'Enter one subtask by line.' => '',
// 'Predefined Contents' => '',
// 'Predefined contents' => '',
// 'Predefined Task Description' => '',
// 'Do you really want to remove this template? "%s"' => '',
// 'Add predefined task description' => '',
// 'Predefined Task Descriptions' => '',
// 'Template created successfully.' => '',
// 'Unable to create this template.' => '',
// 'Template updated successfully.' => '',
// 'Unable to update this template.' => '',
// 'Template removed successfully.' => '',
// 'Unable to remove this template.' => '',
// 'Description Templates' => '',
);

View File

@ -0,0 +1,42 @@
<?php
namespace Kanboard\Model;
use Kanboard\Core\Base;
class PredefinedTaskDescriptionModel extends Base
{
const TABLE = 'predefined_task_descriptions';
public function getAll($projectId)
{
return $this->db->table(self::TABLE)->eq('project_id', $projectId)->findAll();
}
public function getById($projectId, $id)
{
return $this->db->table(self::TABLE)->eq('project_id', $projectId)->eq('id', $id)->findOne();
}
public function create($projectId, $title, $description)
{
return $this->db->table(self::TABLE)->persist(array(
'project_id' => $projectId,
'title' => $title,
'description' => $description,
));
}
public function update($projectId, $id, $title, $description)
{
return $this->db->table(self::TABLE)->eq('project_id', $projectId)->eq('id', $id)->update(array(
'title' => $title,
'description' => $description,
));
}
public function remove($projectId, $id)
{
return $this->db->table(self::TABLE)->eq('project_id', $projectId)->eq('id', $id)->remove();
}
}

View File

@ -8,7 +8,19 @@ use PDO;
use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
const VERSION = 125;
const VERSION = 126;
function version_126(PDO $pdo)
{
$pdo->exec('CREATE TABLE predefined_task_descriptions (
id INT NOT NULL AUTO_INCREMENT,
project_id INT NOT NULL,
title TEXT NOT NULL,
description TEXT NOT NULL,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
PRIMARY KEY(id)
) ENGINE=InnoDB CHARSET=utf8');
}
function version_125(PDO $pdo)
{

View File

@ -8,7 +8,18 @@ use PDO;
use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
const VERSION = 104;
const VERSION = 105;
function version_105(PDO $pdo)
{
$pdo->exec('CREATE TABLE predefined_task_descriptions (
id SERIAL PRIMARY KEY,
project_id INTEGER NOT NULL,
title TEXT NOT NULL,
description TEXT NOT NULL,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
)');
}
function version_104(PDO $pdo)
{

View File

@ -8,7 +8,18 @@ use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
use PDO;
const VERSION = 115;
const VERSION = 116;
function version_116(PDO $pdo)
{
$pdo->exec('CREATE TABLE predefined_task_descriptions (
id INTEGER PRIMARY KEY,
project_id INTEGER NOT NULL,
title TEXT NOT NULL,
description TEXT NOT NULL,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
)');
}
function version_115(PDO $pdo)
{

View File

@ -93,6 +93,7 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('ProjectPermissionController', '*', Role::PROJECT_MANAGER);
$acl->add('ProjectEditController', '*', Role::PROJECT_MANAGER);
$acl->add('ProjectPredefinedContentController', '*', Role::PROJECT_MANAGER);
$acl->add('PredefinedTaskDescriptionController', '*', Role::PROJECT_MANAGER);
$acl->add('ProjectFileController', '*', Role::PROJECT_MEMBER);
$acl->add('ProjectUserOverviewController', '*', Role::PROJECT_MANAGER);
$acl->add('ProjectStatusController', '*', Role::PROJECT_MANAGER);

View File

@ -47,6 +47,7 @@ class ClassProvider implements ServiceProviderInterface
'LinkModel',
'NotificationModel',
'PasswordResetModel',
'PredefinedTaskDescriptionModel',
'ProjectModel',
'ProjectFileModel',
'ProjectActivityModel',
@ -118,6 +119,7 @@ class ClassProvider implements ServiceProviderInterface
'TaskLinkValidator',
'TaskValidator',
'UserValidator',
'PredefinedTaskDescriptionValidator',
),
'Import' => array(
'TaskImport',

View File

@ -0,0 +1,14 @@
<div class="page-header">
<h2><?= t('Predefined Task Description') ?></h2>
</div>
<form method="post" action="<?= $this->url->href('PredefinedTaskDescriptionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->label(t('Title'), 'title') ?>
<?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'tabindex="1"')) ?>
<?= $this->form->label(t('Description'), 'description') ?>
<?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?>
<?= $this->modal->submitButtons() ?>
</form>

View File

@ -0,0 +1,14 @@
<div class="page-header">
<h2><?= t('Predefined Task Description') ?></h2>
</div>
<form method="post" action="<?= $this->url->href('PredefinedTaskDescriptionController', 'update', array('project_id' => $project['id'], 'id' => $template['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->label(t('Title'), 'title') ?>
<?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'tabindex="1"')) ?>
<?= $this->form->label(t('Description'), 'description') ?>
<?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?>
<?= $this->modal->submitButtons() ?>
</form>

View File

@ -0,0 +1,15 @@
<div class="page-header">
<h2><?= t('Predefined Task Description') ?></h2>
</div>
<div class="confirm">
<p class="alert alert-info">
<?= t('Do you really want to remove this template? "%s"', $template['title']) ?>
</p>
<?= $this->modal->confirmButtons(
'PredefinedTaskDescriptionController',
'remove',
array('project_id' => $project['id'], 'id' => $template['id'])
) ?>
</div>

View File

@ -1,6 +1,39 @@
<div class="page-header">
<h2><?= t('Predefined Contents') ?></h2>
<ul>
<li>
<?= $this->modal->medium('plus', t('Add predefined task description'), 'PredefinedTaskDescriptionController', 'create', array('project_id' => $project['id'])) ?>
</li>
</ul>
</div>
<?php if (! empty($predefined_task_descriptions)): ?>
<h3><?= t('Predefined Task Descriptions') ?></h3>
<table>
<?php foreach ($predefined_task_descriptions as $template): ?>
<tr>
<td>
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
<?= $this->modal->medium('edit', t('Edit'), 'PredefinedTaskDescriptionController', 'edit', array('project_id' => $project['id'], 'id' => $template['id'])) ?>
</li>
<li>
<?= $this->modal->confirm('trash-o', t('Remove'), 'PredefinedTaskDescriptionController', 'confirm', array('project_id' => $project['id'], 'id' => $template['id'])) ?>
</li>
</ul>
</div>
<?= $this->text->e($template['title']) ?>
<span class="tooltip" title="<?= $this->text->markdownAttribute($template['description']) ?>">
<i class="fa fa-info-circle"></i>
</span>
</td>
</tr>
<?php endforeach ?>
</table>
<?php endif ?>
<form method="post" action="<?= $this->url->href('ProjectPredefinedContentController', 'update', array('project_id' => $project['id'], 'redirect' => 'edit')) ?>" autocomplete="off">
<?= $this->form->csrf() ?>

View File

@ -8,6 +8,7 @@
<div class="task-form-main-column">
<?= $this->task->renderTitleField($values, $errors) ?>
<?= $this->task->renderDescriptionField($values, $errors) ?>
<?= $this->task->renderDescriptionTemplateDropdown($project['id']) ?>
<?= $this->task->renderTagField($project) ?>
<?= $this->hook->render('template:task:form:first-column', array('values' => $values, 'errors' => $errors)) ?>

View File

@ -0,0 +1,22 @@
<?php
namespace Kanboard\Validator;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
class PredefinedTaskDescriptionValidator extends BaseValidator
{
public function validate(array $values)
{
$v = new Validator($values, array(
new Validators\Required('title', t('This value is required')),
new Validators\Required('description', t('This value is required')),
));
return array(
$v->execute(),
$v->getErrors()
);
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
KB.onClick('.js-template', function (e) {
var template = KB.dom(e.target).data('template');
var target = KB.dom(e.target).data('templateTarget');
var targetField = KB.find(target);
if (targetField) {
targetField.build().value = template;
}
_KB.controllers.Dropdown.close();
});

View File

@ -203,6 +203,7 @@ return array(
'Kanboard\\Controller\\OAuthController' => $baseDir . '/app/Controller/OAuthController.php',
'Kanboard\\Controller\\PasswordResetController' => $baseDir . '/app/Controller/PasswordResetController.php',
'Kanboard\\Controller\\PluginController' => $baseDir . '/app/Controller/PluginController.php',
'Kanboard\\Controller\\PredefinedTaskDescriptionController' => $baseDir . '/app/Controller/PredefinedTaskDescriptionController.php',
'Kanboard\\Controller\\ProjectActionDuplicationController' => $baseDir . '/app/Controller/ProjectActionDuplicationController.php',
'Kanboard\\Controller\\ProjectCreationController' => $baseDir . '/app/Controller/ProjectCreationController.php',
'Kanboard\\Controller\\ProjectEditController' => $baseDir . '/app/Controller/ProjectEditController.php',
@ -525,6 +526,7 @@ return array(
'Kanboard\\Model\\NotificationModel' => $baseDir . '/app/Model/NotificationModel.php',
'Kanboard\\Model\\NotificationTypeModel' => $baseDir . '/app/Model/NotificationTypeModel.php',
'Kanboard\\Model\\PasswordResetModel' => $baseDir . '/app/Model/PasswordResetModel.php',
'Kanboard\\Model\\PredefinedTaskDescriptionModel' => $baseDir . '/app/Model/PredefinedTaskDescriptionModel.php',
'Kanboard\\Model\\ProjectActivityModel' => $baseDir . '/app/Model/ProjectActivityModel.php',
'Kanboard\\Model\\ProjectDailyColumnStatsModel' => $baseDir . '/app/Model/ProjectDailyColumnStatsModel.php',
'Kanboard\\Model\\ProjectDailyStatsModel' => $baseDir . '/app/Model/ProjectDailyStatsModel.php',
@ -638,6 +640,7 @@ return array(
'Kanboard\\Validator\\GroupValidator' => $baseDir . '/app/Validator/GroupValidator.php',
'Kanboard\\Validator\\LinkValidator' => $baseDir . '/app/Validator/LinkValidator.php',
'Kanboard\\Validator\\PasswordResetValidator' => $baseDir . '/app/Validator/PasswordResetValidator.php',
'Kanboard\\Validator\\PredefinedTaskDescriptionValidator' => $baseDir . '/app/Validator/PredefinedTaskDescriptionValidator.php',
'Kanboard\\Validator\\ProjectRoleValidator' => $baseDir . '/app/Validator/ProjectRoleValidator.php',
'Kanboard\\Validator\\ProjectValidator' => $baseDir . '/app/Validator/ProjectValidator.php',
'Kanboard\\Validator\\SubtaskValidator' => $baseDir . '/app/Validator/SubtaskValidator.php',

View File

@ -339,6 +339,7 @@ class ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b
'Kanboard\\Controller\\OAuthController' => __DIR__ . '/../..' . '/app/Controller/OAuthController.php',
'Kanboard\\Controller\\PasswordResetController' => __DIR__ . '/../..' . '/app/Controller/PasswordResetController.php',
'Kanboard\\Controller\\PluginController' => __DIR__ . '/../..' . '/app/Controller/PluginController.php',
'Kanboard\\Controller\\PredefinedTaskDescriptionController' => __DIR__ . '/../..' . '/app/Controller/PredefinedTaskDescriptionController.php',
'Kanboard\\Controller\\ProjectActionDuplicationController' => __DIR__ . '/../..' . '/app/Controller/ProjectActionDuplicationController.php',
'Kanboard\\Controller\\ProjectCreationController' => __DIR__ . '/../..' . '/app/Controller/ProjectCreationController.php',
'Kanboard\\Controller\\ProjectEditController' => __DIR__ . '/../..' . '/app/Controller/ProjectEditController.php',
@ -661,6 +662,7 @@ class ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b
'Kanboard\\Model\\NotificationModel' => __DIR__ . '/../..' . '/app/Model/NotificationModel.php',
'Kanboard\\Model\\NotificationTypeModel' => __DIR__ . '/../..' . '/app/Model/NotificationTypeModel.php',
'Kanboard\\Model\\PasswordResetModel' => __DIR__ . '/../..' . '/app/Model/PasswordResetModel.php',
'Kanboard\\Model\\PredefinedTaskDescriptionModel' => __DIR__ . '/../..' . '/app/Model/PredefinedTaskDescriptionModel.php',
'Kanboard\\Model\\ProjectActivityModel' => __DIR__ . '/../..' . '/app/Model/ProjectActivityModel.php',
'Kanboard\\Model\\ProjectDailyColumnStatsModel' => __DIR__ . '/../..' . '/app/Model/ProjectDailyColumnStatsModel.php',
'Kanboard\\Model\\ProjectDailyStatsModel' => __DIR__ . '/../..' . '/app/Model/ProjectDailyStatsModel.php',
@ -774,6 +776,7 @@ class ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b
'Kanboard\\Validator\\GroupValidator' => __DIR__ . '/../..' . '/app/Validator/GroupValidator.php',
'Kanboard\\Validator\\LinkValidator' => __DIR__ . '/../..' . '/app/Validator/LinkValidator.php',
'Kanboard\\Validator\\PasswordResetValidator' => __DIR__ . '/../..' . '/app/Validator/PasswordResetValidator.php',
'Kanboard\\Validator\\PredefinedTaskDescriptionValidator' => __DIR__ . '/../..' . '/app/Validator/PredefinedTaskDescriptionValidator.php',
'Kanboard\\Validator\\ProjectRoleValidator' => __DIR__ . '/../..' . '/app/Validator/ProjectRoleValidator.php',
'Kanboard\\Validator\\ProjectValidator' => __DIR__ . '/../..' . '/app/Validator/ProjectValidator.php',
'Kanboard\\Validator\\SubtaskValidator' => __DIR__ . '/../..' . '/app/Validator/SubtaskValidator.php',