ソースを参照

init - code uploaded

sirjeff 1 ヶ月 前
コミット
62f64ebf9e

+ 0 - 0
.gitignore


+ 193 - 0
Controller/TemplateController.php

@@ -0,0 +1,193 @@
+<?php namespace Kanboard\Plugin\OMITemplateModder\Controller;
+
+use Kanboard\Controller\BaseController;
+
+/**
+ * TemplateController Controller
+ *
+ * @package  Kanboard\Plugin\OMITemplateModder
+ * @author   Dwayne @ OMI NZ
+ */
+class TemplateController extends BaseController {
+  
+  /**
+  * Show all the available templates.
+  */
+  public function show() {
+    $appTemplates = $this->getCoreTemplates();
+    $pluginTemplates = $this->getPluginTemplates();
+    
+    $templates = $this->getEditableTemplates();
+    
+    $missingTemplates = array_diff($appTemplates, $pluginTemplates);
+    $extraTemplates = array_diff($pluginTemplates, $appTemplates);
+    $modifiedStatus = $this->getModifiedStatus();
+    
+    $this->response->html($this->helper->layout->config("OMITemplateModder:config/index", [
+      "title" => t("OMI - Notification Template Modder"),
+      "templates" => $templates,
+      "missing_templates" => $missingTemplates,
+      "extra_templates" => $extraTemplates,
+      "modified_status" => $modifiedStatus,
+    ]));
+  }
+  
+  /**
+  * Get a list of editable templates from the plugin's directory.
+  *
+  * @return array
+  */
+  private function getEditableTemplates() {
+    $templates = [];
+    $templateDir = ETM_PLUGIN_ROOT_DIR . "/Template/notification";
+    
+    foreach (glob($templateDir . "/*.php") as $filename) {
+      $templateName = basename($filename, ".php");
+      $templates[] = "notification/" . $templateName;
+    }
+    
+    return $templates;
+  }
+  
+  /**
+  * Get a list of core notification templates.
+  *
+  * @return array
+  */
+  private function getCoreTemplates() {
+    $templates = [];
+    // Use the new custom constant defined in Plugin.php
+    $templateDir = KB_APP_DIR . "/Template/notification";
+    
+    foreach (glob($templateDir . "/*.php") as $filename) {
+      $templates[] = basename($filename, ".php");
+    }
+    
+    return $templates;
+  }
+  
+  /**
+  * Get a list of plugin notification templates.
+  *
+  * @return array
+  */
+  private function getPluginTemplates() {
+    $templates = [];
+    $templateDir = ETM_PLUGIN_ROOT_DIR . "/Template/notification";
+    
+    foreach (glob($templateDir . "/*.php") as $filename) {
+      $templates[] = basename($filename, ".php");
+    }
+    
+    return $templates;
+  }
+  
+  /**
+  * Compares core and plugin templates to check for modifications.
+  *
+  * @return array
+  */
+  private function getModifiedStatus() {
+    $modified = [];
+    $pluginTemplateDir = ETM_PLUGIN_ROOT_DIR . '/Template/notification';
+    $coreTemplateDir = KB_APP_DIR . '/Template/notification';
+  
+    foreach (glob($pluginTemplateDir . '/*.php') as $filename) {
+        $templateName = basename($filename);
+        $pluginFile = $pluginTemplateDir . '/' . $templateName;
+        $coreFile = $coreTemplateDir . '/' . $templateName;
+  
+        // Only compare if the core file exists
+        if (file_exists($coreFile)) {
+            $pluginContent = file_get_contents($pluginFile);
+            $coreContent = file_get_contents($coreFile);
+            
+            // Compare the file contents
+            if ($pluginContent !== $coreContent) {
+                $modified[$templateName] = true;
+            }
+        }
+    }
+    return $modified;
+  }
+  
+  /**
+   * Show the form to edit a template
+   */
+  public function edit() {
+    $templateIndex = $this->request->getIntegerParam("template_index");
+    $templates = $this->getEditableTemplates();
+    
+    if (!isset($templates[$templateIndex])) {
+      $this->flash->failure(t("Template not found!"));
+      return $this->response->redirect($this->helper->url->to("TemplateController", "show", ["plugin" => "OMITemplateModder"]));
+    }
+    
+    $templateName = $templates[$templateIndex];
+    $templatePath = $this->getTemplatePath($templateName);
+    
+    if (!file_exists($templatePath)) {
+      $this->flash->failure(t("Template file not found!"));
+      return $this->response->redirect($this->helper->url->to("TemplateController", "show", ["plugin" => "OMITemplateModder"]));
+    }
+    
+    $templateContent = file_get_contents($templatePath);
+    
+    // Use the layout helper to render the page with the full Kanboard layout.
+    $this->response->html($this->helper->layout->config("OMITemplateModder:config/edit", [
+      "title" => t("OMI - Notification Template Modder"),
+      "subtitle" => t("Edit Template: %s", $templateName),
+      "templateName" => $templateName,
+      "templateContent" => htmlspecialchars($templateContent),
+      "templateIndex" => $templateIndex // Pass the index for the form action
+    ]));
+  }
+  
+  /**
+   * Save the edited template
+   */
+  public function save() {
+    
+    $templateIndex = $this->request->getIntegerParam("template_index");
+    $templates = $this->getEditableTemplates();
+    
+    if (!isset($templates[$templateIndex])) {
+      $this->flash->failure(t("Template not found! Check your Kanboard version is compatible with this plugin."));
+      return $this->response->redirect($this->helper->url->to("TemplateController", "show", ["plugin" => "OMITemplateModder"]));
+    }
+    
+    $templateName = $templates[$templateIndex];
+    $templateContent = $this->request->getValue("content");
+    $templatePath = $this->getTemplatePath($templateName);
+    
+    //check if the realpath of the file to be saved begins with the realpath of the allowed directory.
+    $allowedDir = realpath(\ETM_PLUGIN_ROOT_DIR . "/Template/notification");
+    $realTemplatePath = realpath($templatePath);
+    
+    if ($realTemplatePath === false || strpos($realTemplatePath, $allowedDir) !== 0) {
+      $this->flash->failure(t("Invalid template path."));
+      return $this->response->redirect($this->helper->url->to("TemplateController", "show", ["plugin" => "OMITemplateModder"]));
+    }
+    
+    // Try to save the file
+    if (file_put_contents($templatePath, $templateContent) !== false) {
+      $this->flash->success(t("Template saved successfully."));
+    } else {
+      $this->flash->failure(t("Unable to save template. Please check file permissions and directory '_DIR_' set up."));
+    }
+    
+    return $this->response->redirect($this->helper->url->to("TemplateController", "edit", ["plugin" => "OMITemplateModder", "template_index" => $templateIndex]));
+  }
+  
+  /**
+   * Get the full path to a template file
+   *
+   * @param string $templateName
+   * @return string
+   */
+  private function getTemplatePath($templateName) {
+    return \ETM_PLUGIN_ROOT_DIR . "/Template/" . $templateName . ".php";
+  }
+}
+#-
+#plugins/OMITemplateModder/Controller/TemplateController.php

+ 20 - 5
LICENSE

@@ -1,8 +1,23 @@
-MIT License
-Copyright (c) <year> <copyright holders>
+The MIT License (MIT)
 
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+Copyright (c) 2025 Dwayne Pivac @ OMI NZ
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
 
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 67 - 0
Plugin.php

@@ -0,0 +1,67 @@
+<?php namespace Kanboard\Plugin\OMITemplateModder;
+
+use Kanboard\Core\Plugin\Base;
+use Kanboard\Core\Security\Role;
+use Kanboard\Core\Translator;
+
+// Define a local constant for the plugin root directory.
+define("ETM_PLUGIN_ROOT_DIR", __DIR__);
+
+// Define a constant for the core Kanboard application directory.
+// This is a robust alternative to KANBOARD_APP_DIR if it's not available.
+define("KB_APP_DIR", dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . "app");
+
+/**
+ * Plugin Initialisation
+ *
+ * @package  Kanboard\Plugin\OMITemplateModder
+ * @author   Dwayne @ OMI NZ
+ */
+class Plugin extends Base {
+  
+  /**
+   * Initialise the plugin
+   */
+  public function initialize() {
+    // Add a new sub-menu item under Application settings
+    $this->hook->on('template:config:sidebar', ['template' => 'OMITemplateModder:config/sidebar']);
+    
+    // Register the new route for the plugin configuration page
+    $this->route->addRoute("/settings/template_modder", "TemplateController", "show", "OMITemplateModder");
+    $this->route->addRoute("/settings/template_modder/edit/:template_index", "TemplateController", "edit", "OMITemplateModder");
+    $this->route->addRoute("/settings/template_modder/save/:template_index", "TemplateController", "save", "OMITemplateModder");
+    
+     // loop through all notification templates and registers them (that exist in this plugin dir!)
+     $templateDir = ETM_PLUGIN_ROOT_DIR . "/Template/notification";
+     foreach (glob($templateDir . "/*.php") as $filename) {
+       $templateName = basename($filename, ".php");
+       $this->template->setTemplateOverride("notification/" . $templateName, "EmailTemplateModder:notification/" . $templateName);
+     }
+  }
+  
+  public function getPluginName() {
+    return "OMI Notification Template Modder";
+  }
+  
+  public function getPluginDescription() {
+    return t("View and modify Kanboard notification templates which include email templates. Built by dwayne Pivac @ OMI Ltd.");
+  }
+  
+  public function getPluginAuthor() {
+    return "OMI";
+  }
+  
+  public function getPluginVersion() {
+    return "1.0.0";
+  }
+  
+  public function getPluginHomepage() {
+    return "https://vcs.nz/ominz/kb_notification_tpl_modr";
+  }
+  
+  public function getCompatibleVersion() {
+    return ">=1.2.11";
+  }
+}
+#-
+#plugins/OMITemplateModder/Plugin.php

+ 69 - 2
README.md

@@ -1,3 +1,70 @@
-# kb_notification_tpl_modr
+# OMI Notification Template Modder for Kanboard
+
+'Notification Template Modder' plugin for Kanboard, to allow modification of all 'notification' templates which includes email templates.
+
+## Author
+* Dwayne Pivac @ OMI NZ
+* License MIT
+
+## Requirements
+
+* Kanboard 1.2 
+* PHP 8.2.0
+  
+It is possible this works with other versions of Kanboard and PHP, but these are the version this was built and tested on.  
+
+## How to install this plugin
+
+All Kanboard plugins are found in the `plugins` directory. In general you just put your plugin there and that's about it!
+
+### CLI
+
+In a terminal window ( or command for Windoze ) browse/cd to your Kanboard `plugins` directory and issue this command  
+
+#### git clone
+
+`git clone https://vcs.nz/ominz/OMITemplateModder.git`  
+
+#### wget
+
+##### zip ( Windoze )
+
+`wget https://vcs.nz/ominz/OMITemplateModder/archive/main.zip`   
+
+`unzip main.zip`  
+
+`del main.zip`  
+
+##### tar ( Nix )
+
+`wget https://vcs.nz/ominz/OMITemplateModder/archive/main.tar.gz`  
+
+`tar -xzf main.tar.gz`  
+
+`rm main.tar.gz`  
+
+### GUI
+
+Download either zip file :  
+* [https://vcs.nz/ominz/OMITemplateModder/archive/main.zip](https://vcs.nz/ominz/OMITemplateModder/archive/main.zip)
+* [https://vcs.nz/ominz/OMITemplateModder/archive/main.tar.gz](https://vcs.nz/ominz/OMITemplateModder/archive/main.tar.gz)
+
+and uncompress/unzip/untar into your Kanboard `plugins` directory  
+
+## How to use
+
+You will see a new link appear under /settings : "Notification Templates"  
+
+Follow this link and you should see a list of available templates that you can modify.  
+This plugin will alert you to any templates missing or any extras. This may occur if the Kanboard version was different to the one this plugin was built on (1.2).  
+Easy to fix, just make sure the files in your Kanboard `app/Template/notification` directory match the ones in the plugin `Template/notification` directory.  
+
+This plugin will also show a red asterisk on the template files that are different ( have been modified ) to the ones in the app.  
+
+## Maintainers
+
+* Dwayne &lt;dwayne at omi dot nz&gt;
+* OMI Ltd. &lt;adm at omi dot nz&gt;
+
+
 
-'Notification Template Modder' plugin for Kanboard, to allow modification of all 'notification' templates

+ 26 - 0
Template/config/edit.php

@@ -0,0 +1,26 @@
+<div class="page-header">
+  <h2><?= $title ?></h2>
+  <h3><?= $subtitle ?></h3>
+</div>
+
+<form method="post" action="<?= $this->url->href('TemplateController', 'save', ['plugin' => 'OMITemplateModder', 'template_index' => $templateIndex]) ?>">
+  <?= $this->form->csrf() ?>
+  
+  <div class="form-group">
+    <label for="form-content">
+      <?= t('Template Content') ?>
+      
+    </label>
+    <textarea name="content" id="form-content" class="form-textarea" rows="20" style="width:100%; font-family: monospace;"><?= $templateContent ?></textarea>
+  </div>
+  
+  <div class="form-actions">
+    <button type="submit" class="btn btn-blue">
+      <?= t("Save") ?>
+      
+    </button>
+    <?= $this->url->link(t("Cancel"), "TemplateController", "show", ["plugin" => "OMITemplateModder"], false, "btn") ?>
+    
+  </div>
+</form>
+

+ 40 - 0
Template/config/index.php

@@ -0,0 +1,40 @@
+<div class="page-header">
+  <h2><?= $title ?></h2>
+</div>
+
+<?php /* Display notices about missing/extra templates */ ?>
+<?php if (!empty($missing_templates)): ?>
+<div class="alert alert-info">
+  <h4 style="margin-top: 0;"><?= t("Missing Templates") ?></h4>
+  <p><?= t('The following core notification templates are not available in your plugin. To use them, copy the files from "%s" to "%s".', 'app/Template/notification/', 'plugins/OMITemplateModder/Template/notification/') ?></p>
+  <ul style="list-style-type: disc; margin-left: 20px;">
+    <?php foreach ($missing_templates as $template): ?><li><?= $template ?>.php</li><?php endforeach ?>
+  </ul>
+</div>
+<?php endif ?>
+
+<?php if (!empty($extra_templates)): ?>
+<div class="alert alert-warning">
+  <h4 style="margin-top: 0;"><?= t("Extra Templates") ?></h4>
+  <p><?= t('The following templates exist in your plugin but not in the core application. They will be ignored during the override process.') ?></p>
+  <ul style="list-style-type: disc; margin-left: 20px;">
+    <?php foreach ($extra_templates as $template): ?><li><?= $template ?>.php</li><?php endforeach ?>
+  </ul>
+</div>
+<?php endif ?>
+
+<ul class="listing"><?php foreach ($templates as $index => $template): ?>
+  
+  <li>
+    <?php
+      $templateName = basename($template);
+      $isModified = isset($modified_status[$templateName . ".php"]);
+    ?><?= $this->url->link($template, 'TemplateController', 'edit', ['plugin' => 'OMITemplateModder', 'template_index' => $index]) ?>
+    <?php if ($isModified): ?><span style="color:red;font-weight:bold;">*</span><?php endif ?>
+  
+  </li>
+<?php endforeach ?></ul>
+
+<p style="margin-top:2em;">
+  <i>To add or remove templates, simply copy or delete the corresponding files from this plugin's Templates/notification directory. They will automatically be registered for use.</i>
+</p>

+ 4 - 0
Template/config/sidebar.php

@@ -0,0 +1,4 @@
+<li <?= $this->app->checkMenuSelection("TemplateController") ?>>
+          <?= $this->url->link(t("Notification Templates"), "TemplateController", "show", ["plugin" => "OMITemplateModder"]) ?>
+          
+        </li>

+ 15 - 0
Template/notification/comment_create.php

@@ -0,0 +1,15 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<?php if (! empty($comment['username'])): ?>
+    <h3><?= t('New comment posted by %s', $comment['name'] ?: $comment['username']) ?></h3>
+<?php else: ?>
+    <h3><?= t('New comment') ?></h3>
+<?php endif ?>
+
+<?= $this->text->markdown($comment['comment'], true) ?>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 11 - 0
Template/notification/comment_delete.php

@@ -0,0 +1,11 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<h3><?= t('Comment removed') ?></h3>
+
+<?= $this->text->markdown($comment['comment'], true) ?>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 11 - 0
Template/notification/comment_update.php

@@ -0,0 +1,11 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<h3><?= t('Comment updated') ?></h3>
+
+<?= $this->text->markdown($comment['comment'], true) ?>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 11 - 0
Template/notification/comment_user_mention.php

@@ -0,0 +1,11 @@
+<html>
+<body>
+<h2><?= t('You were mentioned in a comment on the task #%d', $task['id']) ?></h2>
+
+<p><?= $this->text->e($task['title']) ?></p>
+
+<?= $this->text->markdown($comment['comment'], true) ?>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 9 - 0
Template/notification/footer.php

@@ -0,0 +1,9 @@
+<hr/>
+OMI Tasks
+
+<?php if ($this->app->config('application_url') != ''): ?>
+    <?php if (isset($task['id'])): ?>
+        - <?= $this->url->absoluteLink(t('view the task on Kanboard'), 'TaskViewController', 'show', array('task_id' => $task['id'])) ?>
+    <?php endif ?>
+    - <?= $this->url->absoluteLink(t('view the board on Kanboard'), 'BoardViewController', 'show', array('project_id' => $task['project_id'])) ?>
+<?php endif ?>

+ 21 - 0
Template/notification/subtask_create.php

@@ -0,0 +1,21 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<h3><?= t('New sub-task') ?></h3>
+
+<ul>
+    <li><?= t('Title:') ?> <?= $this->text->e($subtask['title']) ?></li>
+    <li><?= t('Status:') ?> <?= t($subtask['status_name']) ?></li>
+    <li><?= t('Assignee:') ?> <?= $this->text->e($subtask['name'] ?: $subtask['username'] ?: '?') ?></li>
+    <?php if (! empty($subtask['time_estimated'])): ?>
+        <li>
+            <?= t('Time tracking:') ?>
+            <?= t('%sh estimated', n($subtask['time_estimated'])) ?>
+        </li>
+    <?php endif ?>
+</ul>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 15 - 0
Template/notification/subtask_delete.php

@@ -0,0 +1,15 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<h3><?= t('Subtask removed') ?></h3>
+
+<ul>
+    <li><?= t('Title:') ?> <?= $this->text->e($subtask['title']) ?></li>
+    <li><?= t('Status:') ?> <?= t($subtask['status_name']) ?></li>
+    <li><?= t('Assignee:') ?> <?= $this->text->e($subtask['name'] ?: $subtask['username'] ?: '?') ?></li>
+</ul>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 27 - 0
Template/notification/subtask_update.php

@@ -0,0 +1,27 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<h3><?= t('Sub-task updated') ?></h3>
+
+<ul>
+    <li><?= t('Title:') ?> <?= $this->text->e($subtask['title']) ?></li>
+    <li><?= t('Status:') ?> <?= t($subtask['status_name']) ?></li>
+    <li><?= t('Assignee:') ?> <?= $this->text->e($subtask['name'] ?: $subtask['username'] ?: '?') ?></li>
+    <?php if (! empty($subtask['time_spent']) || ! empty($subtask['time_estimated'])): ?>
+    <li>
+        <?= t('Time tracking:') ?>
+        <?php if (! empty($subtask['time_spent'])): ?>
+            <?= t('%sh spent', n($subtask['time_spent'])) ?>
+        <?php endif ?>
+        <?php if (! empty($subtask['time_spent']) && ! empty($subtask['time_estimated'])): ?>/<?php endif ?>
+        <?php if (! empty($subtask['time_estimated'])): ?>
+            <?= t('%sh estimated', n($subtask['time_estimated'])) ?>
+        <?php endif ?>
+    </li>
+    <?php endif ?>
+</ul>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 24 - 0
Template/notification/task_assignee_change.php

@@ -0,0 +1,24 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<ul>
+    <li>
+        <strong>
+        <?php if ($task['assignee_username']): ?>
+            <?= t('Assigned to %s', $task['assignee_name'] ?: $task['assignee_username']) ?>
+        <?php else: ?>
+            <?= t('There is nobody assigned') ?>
+        <?php endif ?>
+        </strong>
+    </li>
+</ul>
+
+<?php if (! empty($task['description'])): ?>
+    <h2><?= t('Description') ?></h2>
+    <?= $this->text->markdown($task['description'], true) ?: t('There is no description.') ?>
+<?php endif ?>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 9 - 0
Template/notification/task_close.php

@@ -0,0 +1,9 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<p><?= t('The task #%d has been closed.', $task['id']) ?></p>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 50 - 0
Template/notification/task_create.php

@@ -0,0 +1,50 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<ul>
+    <li>
+        <?= t('Created:').' '.$this->dt->datetime($task['date_creation']) ?>
+    </li>
+    <?php if ($task['date_due']): ?>
+    <li>
+        <strong><?= t('Due date:').' '.$this->dt->datetime($task['date_due']) ?></strong>
+    </li>
+    <?php endif ?>
+    <?php if (! empty($task['creator_username'])): ?>
+    <li>
+        <?= t('Created by %s', $task['creator_name'] ?: $task['creator_username']) ?>
+    </li>
+    <?php endif ?>
+    <li>
+        <strong>
+        <?php if (! empty($task['assignee_username'])): ?>
+            <?= t('Assigned to %s', $task['assignee_name'] ?: $task['assignee_username']) ?>
+        <?php else: ?>
+            <?= t('There is nobody assigned') ?>
+        <?php endif ?>
+        </strong>
+    </li>
+    <li>
+        <?= t('Column on the board:') ?>
+        <strong><?= $this->text->e($task['column_title']) ?></strong>
+    </li>
+    <li><?= t('Task position:').' '.$this->text->e($task['position']) ?></li>
+    <?php if (! empty($task['category_name'])): ?>
+    <li>
+        <?= t('Category:') ?> <strong><?= $this->text->e($task['category_name']) ?></strong>
+    </li>
+    <?php endif ?>
+</ul>
+
+<?php if (! empty($task['description'])): ?>
+    <h2><?= t('Description') ?></h2>
+    <?= $this->text->markdown($task['description'], true) ?>
+<?php endif ?>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+
+TEST TEST TEST
+
+</body>
+</html>

+ 9 - 0
Template/notification/task_file_create.php

@@ -0,0 +1,9 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<p><?= t('New attachment added "%s"', $file['name']) ?></p>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 9 - 0
Template/notification/task_file_destroy.php

@@ -0,0 +1,9 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<p><?= t('Attachment removed "%s"', $file['name']) ?></p>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 13 - 0
Template/notification/task_internal_link_create_update.php

@@ -0,0 +1,13 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<p>
+    <?= e('This task is now linked to the task %s with the relation "%s"',
+          $this->url->absoluteLink(t('#%d', $task_link['opposite_task_id']), 'TaskViewController', 'show', array('task_id' => $task_link['opposite_task_id'])),
+          e($task_link['label'])) ?>
+</p>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 13 - 0
Template/notification/task_internal_link_delete.php

@@ -0,0 +1,13 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<p>
+    <?= e('The link with the relation "%s" to the task %s has been removed',
+          e($task_link['label']),
+          $this->url->absoluteLink(t('#%d', $task_link['opposite_task_id']), 'TaskViewController', 'show', array('task_id' => $task_link['opposite_task_id']))) ?>
+</p>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 15 - 0
Template/notification/task_move_column.php

@@ -0,0 +1,15 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<ul>
+    <li>
+        <?= t('Column on the board:') ?>
+        <strong><?= $this->text->e($task['column_title']) ?></strong>
+    </li>
+    <li><?= t('Task position:').' '.$this->text->e($task['position']) ?></li>
+</ul>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 15 - 0
Template/notification/task_move_position.php

@@ -0,0 +1,15 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<ul>
+    <li>
+        <?= t('Column on the board:') ?>
+        <strong><?= $this->text->e($task['column_title']) ?></strong>
+    </li>
+    <li><?= t('Task position:').' '.$this->text->e($task['position']) ?></li>
+</ul>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 9 - 0
Template/notification/task_move_project.php

@@ -0,0 +1,9 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<p><?= t('Task #%d "%s" has been moved to the project "%s"', $task['id'], $task['title'], $task['project_name']) ?></p>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 23 - 0
Template/notification/task_move_swimlane.php

@@ -0,0 +1,23 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<ul>
+    <li>
+        <?php if ($task['swimlane_id'] == 0): ?>
+            <?= t('The task has been moved to the first swimlane') ?>
+        <?php else: ?>
+            <?= t('The task has been moved to another swimlane:') ?>
+            <strong><?= $this->text->e($task['swimlane_name']) ?></strong>
+        <?php endif ?>
+    </li>
+    <li>
+        <?= t('Column on the board:') ?>
+        <strong><?= $this->text->e($task['column_title']) ?></strong>
+    </li>
+    <li><?= t('Task position:').' '.$this->text->e($task['position']) ?></li>
+</ul>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 9 - 0
Template/notification/task_open.php

@@ -0,0 +1,9 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<p><?= t('The task #%d has been opened.', $task['id']) ?></p>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 41 - 0
Template/notification/task_overdue.php

@@ -0,0 +1,41 @@
+<html>
+<body>
+<h2><?= t('Overdue tasks for the project(s) "%s"', $project_name) ?></h2>
+
+<table style="font-size: .8em; table-layout: fixed; width: 100%; border-collapse: collapse; border-spacing: 0; margin-bottom: 20px;" cellpadding=5 cellspacing=1>
+    <tr style="background: #fbfbfb; text-align: left; padding-top: .5em; padding-bottom: .5em; padding-left: 3px; padding-right: 3px;">
+        <th style="border: 1px solid #eee;"><?= t('Id') ?></th>
+        <th style="border: 1px solid #eee;"><?= t('Title') ?></th>
+        <th style="border: 1px solid #eee;"><?= t('Due date') ?></th>
+        <th style="border: 1px solid #eee;"><?= t('Project') ?></th>
+        <th style="border: 1px solid #eee;"><?= t('Assignee') ?></th>
+    </tr>
+
+    <?php foreach ($tasks as $task): ?>
+        <tr style="overflow: hidden; background: #fff; text-align: left; padding-top: .5em; padding-bottom: .5em; padding-left: 3px; padding-right: 3px;">
+            <td style="border: 1px solid #eee;">#<?= $task['id'] ?></td>
+            <td style="border: 1px solid #eee;">
+                <?php if ($this->app->config('application_url') !== ''): ?>
+                    <?= $this->url->absoluteLink($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id'])) ?>
+                <?php else: ?>
+                    <?= $this->text->e($task['title']) ?>
+                <?php endif ?>
+            </td>
+            <td style="border: 1px solid #eee;"><?= $this->dt->datetime($task['date_due']) ?></td>
+            <td style="border: 1px solid #eee;">
+                <?php if ($this->app->config('application_url') !== ''): ?>
+                    <?= $this->url->absoluteLink($this->text->e($task['project_name']), 'BoardViewController', 'show', array('project_id' => $task['project_id'])) ?>
+                <?php else: ?>
+                    <?= $this->text->e($task['project_name']) ?>
+                <?php endif ?>
+            </td>
+            <td style="border: 1px solid #eee;">
+                <?php if (! empty($task['assignee_username'])): ?>
+                    <?= $this->text->e($task['assignee_name'] ?: $task['assignee_username']) ?>
+                <?php endif ?>
+            </td>
+        </tr>
+    <?php endforeach ?>
+</table>
+</body>
+</html>

+ 8 - 0
Template/notification/task_update.php

@@ -0,0 +1,8 @@
+<html>
+<body>
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<?= $this->render('task/changes', array('changes' => $changes, 'task' => $task, 'public' => true)) ?>
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 11 - 0
Template/notification/task_user_mention.php

@@ -0,0 +1,11 @@
+<html>
+<body>
+<h2><?= t('You were mentioned in the task #%d', $task['id']) ?></h2>
+<p><?= $this->text->e($task['title']) ?></p>
+
+<h2><?= t('Description') ?></h2>
+<?= $this->text->markdown($task['description'], true) ?>
+
+<?= $this->render('notification/footer', array('task' => $task)) ?>
+</body>
+</html>

+ 1 - 0
ver

@@ -0,0 +1 @@
+1.0.0