Files
OrganonTweaks/Plugin.php

178 lines
9.1 KiB
PHP

<?php
namespace Kanboard\Plugin\OrganonTweaks;
use Kanboard\Core\Plugin\Base;
use Kanboard\Model\TaskModel;
class Plugin extends Base
{
public function initialize()
{
// "Remove this Column" entry in the board column header dropdown. The only
// column hook renders outside the native menu <ul>, so the item is rendered
// hidden and relocate.js moves it into the menu (like BulkMoveTasks).
$this->template->hook->attach('template:board:column:dropdown', 'organonTweaks:board/column_dropdown');
$this->hook->on('template:layout:js', array(
'template' => 'plugins/OrganonTweaks/Asset/js/relocate.js',
));
// Settings page for the plugin's tweaks.
$this->template->hook->attach('template:config:sidebar', 'organonTweaks:config/sidebar');
// Per-board tweak: emphasize the due date on the board. Configured with 3-state radios on the
// board's Settings -> Integrations (Use default / Always on / Always off); the global default
// (for "Use default" boards) lives in the plugin settings above. The emphasis <style> is
// emitted into the head only on enabled board pages (gated by OrganonProjectHelper).
$this->template->hook->attach('template:project:integrations', 'organonTweaks:project/integration');
$this->template->hook->attach('template:layout:head', 'organonTweaks:layout/emphasize_duedate');
// Tweak: always show a comment icon on board cards. When a card has no comments
// yet, add a bubble "+" that opens the same comment modal (add the first comment
// straight from the board). Cards with comments already show the native count.
if ((int) $this->configModel->get('organon_tweaks_always_comment_icon', 1) === 1) {
$this->template->hook->attach('template:board:task:icons', 'organonTweaks:board/task_comment_icon');
}
// Tweak: keep the board horizontal scroll position across refreshes (Kanboard
// rebuilds #board-container on card drop / polling, resetting scrollLeft to 0).
if ((int) $this->configModel->get('organon_tweaks_keep_scroll', 1) === 1) {
$this->hook->on('template:layout:js', array(
'template' => 'plugins/OrganonTweaks/Asset/js/keep-scroll.js',
));
}
// Tweak: only open a card on a quick click (a press held > 100ms is a drag, so the
// trailing click is swallowed and the card does not open).
if ((int) $this->configModel->get('organon_tweaks_quick_click_only', 1) === 1) {
$this->hook->on('template:layout:js', array(
'template' => 'plugins/OrganonTweaks/Asset/js/click-guard.js',
));
}
// Tweak: enhance the native "Edit recurrence" dialog (Yes/No radios + a [+7] button
// on the factor). No template hook there, so it is done in JS.
if ((int) $this->configModel->get('organon_tweaks_enhance_recurrence', 1) === 1) {
$this->hook->on('template:layout:js', array(
'template' => 'plugins/OrganonTweaks/Asset/js/enhance-recurrence.js',
));
}
// Tweak: show the keyboard shortcuts Kanboard already binds next to the task Actions menu
// items (display only -- no new bindings). Done in JS since those menu <li> have no hook.
if ((int) $this->configModel->get('organon_tweaks_shortcut_labels', 1) === 1) {
$this->hook->on('template:layout:js', array(
'template' => 'plugins/OrganonTweaks/Asset/js/shortcut-labels.js',
));
}
// Tweak: clicking a task title opens the edit modal (instead of the read-only card view).
// JS reuses Kanboard's own modal opener. On the board the title is a link, so a hidden marker
// (rendered by the hook below, only when the user may edit) carries the server-generated edit
// URL for the JS to open; on the card view the JS reads #task-view's data-edit-url.
if ((int) $this->configModel->get('organon_tweaks_title_click_edit', 1) === 1) {
$this->hook->on('template:layout:js', array(
'template' => 'plugins/OrganonTweaks/Asset/js/title-click-edit.js',
));
$this->template->hook->attach('template:board:private:task:before-title', 'organonTweaks:board/title_edit');
}
// Tweak: per-column persistent sort. A sort control (number-aware title sort + a
// "Persistent sort: ON/OFF" toggle) replaces the native column sort. Sorting always reorders
// physically (writes positions, like native); when persistent is ON, the column is re-sorted
// whenever a card is dropped in or created, so it stays sorted. Persistent OFF = native
// one-shot. Control injected via the column dropdown hook + relocate.js (which also hides the
// native fa-sort menu).
if ((int) $this->configModel->get('organon_tweaks_persistent_sort', 1) === 1) {
$this->template->hook->attach('template:board:column:dropdown', 'organonTweaks:board/sort_control');
$container = $this->container;
$reapply = function ($project_id, $swimlane_id, $column_id) use ($container) {
$helper = new \Kanboard\Plugin\OrganonTweaks\Helper\OrganonSortHelper($container);
if ($helper->isSticky($project_id, $column_id)) {
$helper->reapply($project_id, $swimlane_id, $column_id);
}
};
// Card dropped into a column -> re-apply that column's sort (the plugin on() wrapper drops
// the event, so subscribe on the dispatcher to receive the TaskEvent).
$this->dispatcher->addListener(TaskModel::EVENT_MOVE_COLUMN, function ($event) use ($reapply) {
$task = $event['task'];
$reapply((int) $task['project_id'], (int) $task['swimlane_id'], (int) $event['dst_column_id']);
});
// New card created in a column -> re-apply that column's sort.
$this->hook->on('model:task:creation:aftersave', function ($task_id) use ($container, $reapply) {
$task = $container['taskFinderModel']->getById($task_id);
if (! empty($task)) {
$reapply((int) $task['project_id'], (int) $task['swimlane_id'], (int) $task['column_id']);
}
});
}
// Auto-managed shared custom filters (v1.5 "Show all tasks", v1.6 month filters, v1.7
// recurring filters). Ensured lazily whenever a project header renders (covers old + new
// boards); each group is gated by its own config toggle inside
// OrganonProjectHelper::ensureBoardFilters(). The toggles also bulk add/remove the filters.
$showAllFilters = (int) $this->configModel->get('organon_tweaks_show_all_filter', 1) === 1;
$monthFilters = (int) $this->configModel->get('organon_tweaks_month_filters', 1) === 1;
$recurringFilters = (int) $this->configModel->get('organon_tweaks_recurring_filters', 1) === 1;
if ($showAllFilters || $monthFilters || $recurringFilters) {
$this->template->hook->attach('template:project:header:before', 'organonTweaks:project_header/board_filters');
}
// Custom board-search keywords, registered into the task lexer by extending its factory
// service (Pimple preserves factory status on extend, so the lexer stays per-request fresh).
// `duemonth:VALUE` / `aroundmonth:VALUE` = a calendar month (padded by 10 days for around);
// `recurring:XY` = classify recurrence (X system evt/cal/any, Y role ori/dup/all). Always
// available to type, independent of the bookmark toggles above. Guarded so a future Kanboard
// service rename no-ops (keyword absent) instead of breaking.
if (isset($this->container['taskLexer'])) {
$this->container->extend('taskLexer', function ($builder, $c) {
return $builder
->withFilter((new \Kanboard\Plugin\OrganonTweaks\Filter\TaskMonthRangeFilter())->setAttribute('duemonth')->setPadDays(0))
->withFilter((new \Kanboard\Plugin\OrganonTweaks\Filter\TaskMonthRangeFilter())->setAttribute('aroundmonth')->setPadDays(10))
->withFilter((new \Kanboard\Plugin\OrganonTweaks\Filter\TaskRecurringFilter())->setDatabase($c['db']));
});
}
}
public function getHelpers()
{
return array(
'Plugin\OrganonTweaks\Helper' => array('OrganonColumnHelper', 'OrganonProjectHelper', 'OrganonSortHelper'),
);
}
public function getPluginName()
{
return 'OrganonTweaks';
}
public function getPluginDescription()
{
return t('Umbrella plugin for small Kanboard tweaks: remove an empty board column, always show the card comment icon, and more.');
}
public function getPluginAuthor()
{
return 'Ruben (drbeco)';
}
public function getPluginVersion()
{
return '1.9.1';
}
public function getPluginHomepage()
{
return 'https://code.beco.cc/beco/OrganonTweaks';
}
public function getCompatibleVersion()
{
return '>=1.2.0';
}
}