summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Controller/Project.php21
-rw-r--r--app/Locales/de_DE/translations.php4
-rw-r--r--app/Locales/es_ES/translations.php4
-rw-r--r--app/Locales/fi_FI/translations.php4
-rw-r--r--app/Locales/fr_FR/translations.php4
-rw-r--r--app/Locales/pl_PL/translations.php4
-rw-r--r--app/Locales/pt_BR/translations.php4
-rw-r--r--app/Locales/sv_SE/translations.php4
-rw-r--r--app/Locales/zh_CN/translations.php4
-rw-r--r--app/Model/Project.php206
-rw-r--r--app/Templates/project_index.php3
11 files changed, 262 insertions, 0 deletions
diff --git a/app/Controller/Project.php b/app/Controller/Project.php
index 8c21801b..0d430b44 100644
--- a/app/Controller/Project.php
+++ b/app/Controller/Project.php
@@ -13,6 +13,27 @@ use Core\Translator;
*/
class Project extends Base
{
+
+ /**
+ * Clone Project
+ *
+ * @author Antonio Rabelo
+ * @access public
+ */
+ public function duplicate()
+ {
+ $this->checkCSRFParam();
+ $project_id = $this->request->getIntegerParam('project_id');
+
+ if ($project_id && $this->project->duplicate($project_id)) {
+ $this->session->flash(t('Project cloned successfully.'));
+ } else {
+ $this->session->flashError(t('Unable to clone this project.'));
+ }
+
+ $this->response->redirect('?controller=project');
+ }
+
/**
* Task export
*
diff --git a/app/Locales/de_DE/translations.php b/app/Locales/de_DE/translations.php
index 551a7580..73aed160 100644
--- a/app/Locales/de_DE/translations.php
+++ b/app/Locales/de_DE/translations.php
@@ -398,4 +398,8 @@ return array(
'Completion date' => 'Abschlussdatum',
'Webhook URL for task creation' => 'Webhook URL zur Aufgabenerstellung',
'Webhook URL for task modification' => 'Webhook URL zur Aufgabenänderung',
+ // 'Clone' => '',
+ // 'Clone Project' => '',
+ // 'Project cloned successfully.' => '',
+ // 'Unable to clone this project.' => '',
);
diff --git a/app/Locales/es_ES/translations.php b/app/Locales/es_ES/translations.php
index 37b4e231..a8a8024c 100644
--- a/app/Locales/es_ES/translations.php
+++ b/app/Locales/es_ES/translations.php
@@ -397,4 +397,8 @@ return array(
// 'Completion date' => '',
// 'Webhook URL for task creation' => '',
// 'Webhook URL for task modification' => '',
+ // 'Clone' => '',
+ // 'Clone Project' => '',
+ // 'Project cloned successfully.' => '',
+ // 'Unable to clone this project.' => '',
);
diff --git a/app/Locales/fi_FI/translations.php b/app/Locales/fi_FI/translations.php
index caf768fb..801ec5c1 100644
--- a/app/Locales/fi_FI/translations.php
+++ b/app/Locales/fi_FI/translations.php
@@ -397,4 +397,8 @@ return array(
'Completion date' => 'Valmistumispäivä',
'Webhook URL for task creation' => 'Webhook URL tehtävän luomiselle',
'Webhook URL for task modification' => 'Webhook URL tehtävän muokkaamiselle',
+ // 'Clone' => '',
+ // 'Clone Project' => '',
+ // 'Project cloned successfully.' => '',
+ // 'Unable to clone this project.' => '',
);
diff --git a/app/Locales/fr_FR/translations.php b/app/Locales/fr_FR/translations.php
index 72e56111..552060c5 100644
--- a/app/Locales/fr_FR/translations.php
+++ b/app/Locales/fr_FR/translations.php
@@ -395,4 +395,8 @@ return array(
'Completion date' => 'Date de complétion',
'Webhook URL for task creation' => 'URL du webhook pour la création de tâche',
'Webhook URL for task modification' => 'URL du webhook pour la modification de tâche',
+ 'Clone' => 'Clone',
+ 'Clone Project' => 'Cloner le projet',
+ 'Project cloned successfully.' => 'Projet cloné avec succès.',
+ 'Unable to clone this project.' => 'Impossible de cloner ce projet.',
);
diff --git a/app/Locales/pl_PL/translations.php b/app/Locales/pl_PL/translations.php
index c9c6a0e5..41e7f80a 100644
--- a/app/Locales/pl_PL/translations.php
+++ b/app/Locales/pl_PL/translations.php
@@ -398,4 +398,8 @@ return array(
// 'Completion date' => '',
// 'Webhook URL for task creation' => '',
// 'Webhook URL for task modification' => '',
+ // 'Clone' => '',
+ // 'Clone Project' => '',
+ // 'Project cloned successfully.' => '',
+ // 'Unable to clone this project.' => '',
);
diff --git a/app/Locales/pt_BR/translations.php b/app/Locales/pt_BR/translations.php
index 793c30e6..95ea11b5 100644
--- a/app/Locales/pt_BR/translations.php
+++ b/app/Locales/pt_BR/translations.php
@@ -395,4 +395,8 @@ return array(
// 'Completion date' => '',
// 'Webhook URL for task creation' => '',
// 'Webhook URL for task modification' => '',
+ // 'Clone' => '',
+ // 'Clone Project' => '',
+ // 'Project cloned successfully.' => '',
+ // 'Unable to clone this project.' => '',
);
diff --git a/app/Locales/sv_SE/translations.php b/app/Locales/sv_SE/translations.php
index 3cd6525a..68248b72 100644
--- a/app/Locales/sv_SE/translations.php
+++ b/app/Locales/sv_SE/translations.php
@@ -397,4 +397,8 @@ return array(
// 'Completion date' => '',
// 'Webhook URL for task creation' => '',
// 'Webhook URL for task modification' => '',
+ // 'Clone' => '',
+ // 'Clone Project' => '',
+ // 'Project cloned successfully.' => '',
+ // 'Unable to clone this project.' => '',
);
diff --git a/app/Locales/zh_CN/translations.php b/app/Locales/zh_CN/translations.php
index 6f519981..1d759336 100644
--- a/app/Locales/zh_CN/translations.php
+++ b/app/Locales/zh_CN/translations.php
@@ -403,4 +403,8 @@ return array(
// 'Completion date' => '',
// 'Webhook URL for task creation' => '',
// 'Webhook URL for task modification' => '',
+ // 'Clone' => '',
+ // 'Clone Project' => '',
+ // 'Project cloned successfully.' => '',
+ // 'Unable to clone this project.' => '',
);
diff --git a/app/Model/Project.php b/app/Model/Project.php
index 5d3f01b9..f598c96f 100644
--- a/app/Model/Project.php
+++ b/app/Model/Project.php
@@ -378,6 +378,212 @@ class Project extends Base
}
/**
+ * Create a project from another one.
+ *
+ * @author Antonio Rabelo
+ * @param integer $project_id Project Id
+ * @return integer Cloned Project Id
+ */
+ public function createProjectFromAnotherProject($project_id)
+ {
+ // Recover the template project data
+ $project = $this->getById($project_id);
+
+ // Create a Clone project
+ $clone_project = array(
+ 'name' => $project['name'].' ('.t('Clone').')',
+ 'is_active' => true,
+ 'last_modified' => 0,
+ 'token' => Security::generateToken(),
+ );
+
+ // Register the cloned project
+ if (! $this->db->table(self::TABLE)->save($clone_project)) {
+ return false;
+ }
+
+ // Get the cloned project Id
+ return $this->db->getConnection()->getLastId();
+ }
+
+ /**
+ * Copy Board Columns from a project to another one.
+ *
+ * @author Antonio Rabelo
+ * @param integer $project_from Project Template
+ * @return integer $project_to Project that receives the copy
+ * @return boolean
+ */
+ public function copyBoardFromAnotherProject($project_from, $project_to)
+ {
+ $boardModel = new Board($this->db, $this->event);
+ $columns = $this->db->table(Board::TABLE)->eq('project_id', $project_from)->asc('position')->findAllByColumn('title');
+ return $boardModel->create($project_to, $columns);
+ }
+
+ /**
+ * Copy Categories from a project to another one.
+ *
+ * @author Antonio Rabelo
+ * @param integer $project_from Project Template
+ * @return integer $project_to Project that receives the copy
+ * @return boolean
+ */
+ public function copyCategoriesFromAnotherProject($project_from, $project_to)
+ {
+ $categoryModel = new Category($this->db, $this->event);
+ $categoriesTemplate = $categoryModel->getAll($project_from);
+
+ foreach ($categoriesTemplate as $category) {
+
+ unset($category['id']);
+ $category['project_id'] = $project_to;
+
+ if (! $categoryModel->create($category)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Copy User Access from a project to another one.
+ *
+ * @author Antonio Rabelo
+ * @param integer $project_from Project Template
+ * @return integer $project_to Project that receives the copy
+ * @return boolean
+ */
+ public function copyUserAccessFromAnotherProject($project_from, $project_to)
+ {
+ $usersList = $this->getAllowedUsers($project_from);
+
+ foreach ($usersList as $id => $userName) {
+ if (! $this->allowUser($project_to, $id)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Copy Actions and related Actions Parameters from a project to another one.
+ *
+ * @author Antonio Rabelo
+ * @param integer $project_from Project Template
+ * @return integer $project_to Project that receives the copy
+ * @return boolean
+ */
+ public function copyActionsFromAnotherProject($project_from, $project_to)
+ {
+ $actionModel = new Action($this->db, $this->event);
+ $actionTemplate = $actionModel->getAllByProject($project_from);
+
+ foreach ($actionTemplate as $action) {
+
+ unset($action['id']);
+ $action['project_id'] = $project_to;
+ $actionParams = $action['params'];
+ unset($action['params']);
+
+ if (! $this->db->table(Action::TABLE)->save($action)) {
+ return false;
+ }
+
+ $action_clone_id = $this->db->getConnection()->getLastId();
+
+ foreach ($actionParams as $param) {
+ unset($param['id']);
+ $param['value'] = $this->resolveValueParamToClonedAction($param, $project_to);
+ $param['action_id'] = $action_clone_id;
+
+ if (! $this->db->table(Action::TABLE_PARAMS)->save($param)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Resolve type of action value from a project to the respective value in another project.
+ *
+ * @author Antonio Rabelo
+ * @param integer $param A action parameter
+ * @return integer $project_to Project to find the corresponding values
+ * @return mixed The corresponding values from $project_to
+ */
+ private function resolveValueParamToClonedAction($param, $project_to)
+ {
+ switch($param['name']) {
+ case 'project_id':
+ return $project_to;
+ case 'category_id':
+ $categoryModel = new Category($this->db, $this->event);
+ $categoryTemplate = $categoryModel->getById($param['value']);
+ $categoryFromNewProject = $this->db->table(Category::TABLE)->eq('project_id', $project_to)->eq('name', $categoryTemplate['name'])->findOne();
+ return $categoryFromNewProject['id'];
+ case 'column_id':
+ $boardModel = new Board($this->db, $this->event);
+ $boardTemplate = $boardModel->getColumn($param['value']);
+ $boardFromNewProject = $this->db->table(Board::TABLE)->eq('project_id', $project_to)->eq('title', $boardTemplate['title'])->findOne();
+ return $boardFromNewProject['id'];
+ default:
+ return $param['value'];
+ }
+ }
+
+ /**
+ * Clone a project
+ *
+ * @author Antonio Rabelo
+ * @param integer $project_id Project Id
+ * @return integer Cloned Project Id
+ */
+ public function duplicate($project_id)
+ {
+ $this->db->startTransaction();
+
+ // Get the cloned project Id
+ $clone_project_id = $this->createProjectFromAnotherProject($project_id);
+ if (! $clone_project_id) {
+ $this->db->cancelTransaction();
+ return false;
+ }
+
+ // Clone Board
+ if (! $this->copyBoardFromAnotherProject($project_id, $clone_project_id)) {
+ $this->db->cancelTransaction();
+ return false;
+ }
+
+ // Clone Categories
+ if (! $this->copyCategoriesFromAnotherProject($project_id, $clone_project_id)) {
+ $this->db->cancelTransaction();
+ return false;
+ }
+
+ // Clone Allowed Users
+ if (! $this->copyUserAccessFromAnotherProject($project_id, $clone_project_id)) {
+ $this->db->cancelTransaction();
+ return false;
+ }
+
+ // Clone Actions
+ if (! $this->copyActionsFromAnotherProject($project_id, $clone_project_id)) {
+ $this->db->cancelTransaction();
+ return false;
+ }
+
+ $this->db->closeTransaction();
+
+ return (int) $clone_project_id;
+ }
+
+ /**
* Create a project
*
* @access public
diff --git a/app/Templates/project_index.php b/app/Templates/project_index.php
index 097ebd1f..dc71033f 100644
--- a/app/Templates/project_index.php
+++ b/app/Templates/project_index.php
@@ -92,6 +92,9 @@
<li>
<a href="?controller=project&amp;action=export&amp;project_id=<?= $project['id'] ?>"><?= t('Tasks Export') ?></a>
</li>
+ <li>
+ <a href="?controller=project&amp;action=duplicate&amp;project_id=<?= $project['id'].Helper\param_csrf() ?>"><?= t('Clone Project') ?></a>
+ </li>
</ul>
</td>
<?php endif ?>