diff --git a/Controller/ConfigController.php b/Controller/ConfigController.php index 55d8d24..084f535 100644 --- a/Controller/ConfigController.php +++ b/Controller/ConfigController.php @@ -21,6 +21,7 @@ class ConfigController extends BaseController 'organon_tweaks_emphasize_duedate_default' => (int) $this->configModel->get('organon_tweaks_emphasize_duedate_default', 0), 'organon_tweaks_show_all_filter' => (int) $this->configModel->get('organon_tweaks_show_all_filter', 1), 'organon_tweaks_month_filters' => (int) $this->configModel->get('organon_tweaks_month_filters', 1), + 'organon_tweaks_recurring_filters' => (int) $this->configModel->get('organon_tweaks_recurring_filters', 1), ), 'errors' => array(), ))); @@ -37,6 +38,7 @@ class ConfigController extends BaseController $emphasizeDueDateDefault = isset($values['organon_tweaks_emphasize_duedate_default']) ? 1 : 0; $showAllFilter = isset($values['organon_tweaks_show_all_filter']) ? 1 : 0; $monthFilters = isset($values['organon_tweaks_month_filters']) ? 1 : 0; + $recurringFilters = isset($values['organon_tweaks_recurring_filters']) ? 1 : 0; if ($this->configModel->save(array( 'organon_tweaks_always_comment_icon' => $alwaysCommentIcon, @@ -46,6 +48,7 @@ class ConfigController extends BaseController 'organon_tweaks_emphasize_duedate_default' => $emphasizeDueDateDefault, 'organon_tweaks_show_all_filter' => $showAllFilter, 'organon_tweaks_month_filters' => $monthFilters, + 'organon_tweaks_recurring_filters' => $recurringFilters, ))) { $this->flash->success(t('Settings saved successfully.')); } else { @@ -68,6 +71,12 @@ class ConfigController extends BaseController $projectHelper->removeGroup($projectHelper::GROUP_MONTH); } + if ($recurringFilters === 1) { + $projectHelper->addGroupToAllProjects($projectHelper::GROUP_RECURRING); + } else { + $projectHelper->removeGroup($projectHelper::GROUP_RECURRING); + } + $this->response->redirect($this->helper->url->to('ConfigController', 'show', array('plugin' => 'OrganonTweaks'))); } } diff --git a/Filter/TaskRecurringFilter.php b/Filter/TaskRecurringFilter.php new file mode 100644 index 0000000..a99299c --- /dev/null +++ b/Filter/TaskRecurringFilter.php @@ -0,0 +1,100 @@ + single-value path, so the predicates below are grouped in a scoped + * beginOr/closeOr and the whole group still ANDs with any other search attribute. + */ +class TaskRecurringFilter extends BaseFilter implements FilterInterface +{ + private $db; + + public function setDatabase(Database $db) + { + $this->db = $db; + return $this; + } + + public function getAttributes() + { + return array('recurring'); + } + + public function apply() + { + $value = strtolower(trim((string) $this->value)); + $system = substr($value, 0, 3); // evt | cal | any + $role = substr($value, 3, 3); // ori | dup | all + + if (! in_array($system, array('evt', 'cal', 'any'), true) || + ! in_array($role, array('ori', 'dup', 'all'), true)) { + return $this; // unknown/typo value -> no-op + } + + $wantEvt = $system === 'evt' || $system === 'any'; + $wantCal = $system === 'cal' || $system === 'any'; + $wantOri = $role === 'ori' || $role === 'all'; + $wantDup = $role === 'dup' || $role === 'all'; + + $this->query->beginOr(); + + if ($wantEvt && $wantOri) { + $this->query->eq(TaskModel::TABLE.'.recurrence_status', TaskModel::RECURRING_STATUS_PENDING); + } + + if ($wantEvt && $wantDup) { + $this->query->eq(TaskModel::TABLE.'.recurrence_status', TaskModel::RECURRING_STATUS_PROCESSED); + } + + if ($wantCal && $wantOri) { + $this->query->in(TaskModel::TABLE.'.id', $this->metaIds('recoreco_enabled')); + } + + if ($wantCal && $wantDup) { + $this->query->in(TaskModel::TABLE.'.id', $this->metaIds('recoreco_clone')); + } + + $this->query->closeOr(); + + return $this; + } + + /** + * Task ids carrying a RecoReco metadata flag set to 1. Returns a non-matching sentinel when the + * set is empty so the IN clause matches nothing (RecoReco absent -> event-only degrade). + * + * @param string $name + * @return array + */ + private function metaIds($name) + { + $ids = $this->db->table('task_has_metadata') + ->eq('name', $name) + ->eq('value', '1') + ->findAllByColumn('task_id'); + + return ! empty($ids) ? $ids : array(-1); + } +} diff --git a/Helper/OrganonProjectHelper.php b/Helper/OrganonProjectHelper.php index 3862af2..9e036a5 100644 --- a/Helper/OrganonProjectHelper.php +++ b/Helper/OrganonProjectHelper.php @@ -27,6 +27,7 @@ class OrganonProjectHelper extends Base // Config toggles for the auto-managed custom-filter groups. const SHOW_ALL_FILTER_KEY = 'organon_tweaks_show_all_filter'; const MONTH_FILTERS_KEY = 'organon_tweaks_month_filters'; + const RECURRING_FILTERS_KEY = 'organon_tweaks_recurring_filters'; // Reserved custom-filter groups: each row is [name, query, append]. The NAME is reserved by the // plugin (fixed, non-translated strings so dedup is stable across locales); a user's own filter @@ -39,6 +40,13 @@ class OrganonProjectHelper extends Base array('Due this month', 'duemonth:this', 1), array('Around this month', 'aroundmonth:this', 1), ); + const GROUP_RECURRING = array( + array('All recurring', 'recurring:anyall', 1), + array('Event recurring', 'recurring:evtall', 1), + array('Calendar recurring', 'recurring:calall', 1), + array('Recurring templates', 'recurring:anyori', 1), + array('Recurring duplicates', 'recurring:anydup', 1), + ); /** * Should the current board emphasize the due date? Board pages only (project_id in the route). @@ -135,6 +143,10 @@ class OrganonProjectHelper extends Base if ((int) $this->configModel->get(self::MONTH_FILTERS_KEY, 1) === 1) { $this->ensureGroup($project_id, self::GROUP_MONTH); } + + if ((int) $this->configModel->get(self::RECURRING_FILTERS_KEY, 1) === 1) { + $this->ensureGroup($project_id, self::GROUP_RECURRING); + } } private function ensureGroup($project_id, array $rows) diff --git a/Plugin.php b/Plugin.php index 3307cf8..41329a4 100644 --- a/Plugin.php +++ b/Plugin.php @@ -58,26 +58,29 @@ class Plugin extends Base )); } - // Auto-managed shared custom filters (v1.5 "Show all tasks", v1.6 month 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 (ConfigController). + // 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; - if ($showAllFilters || $monthFilters) { + $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` = a whole calendar month; `aroundmonth:VALUE` = that month padded by 10 - // days on each side. Always available to type, independent of the bookmark toggles above. - // Guarded so a future Kanboard service rename no-ops (keyword absent) instead of breaking. + // `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\TaskMonthRangeFilter())->setAttribute('aroundmonth')->setPadDays(10)) + ->withFilter((new \Kanboard\Plugin\OrganonTweaks\Filter\TaskRecurringFilter())->setDatabase($c['db'])); }); } } @@ -106,7 +109,7 @@ class Plugin extends Base public function getPluginVersion() { - return '1.6.1'; + return '1.7.0'; } public function getPluginHomepage() diff --git a/Template/config/show.php b/Template/config/show.php index dbe418a..0ed5312 100644 --- a/Template/config/show.php +++ b/Template/config/show.php @@ -46,7 +46,7 @@

form->checkbox('organon_tweaks_recurring_filters', t('Add recurring filters to every board'), 1, isset($values['organon_tweaks_recurring_filters']) && $values['organon_tweaks_recurring_filters'] == 1) ?> -

+

diff --git a/VERSION b/VERSION index 50e995b..05031d4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -OrganonTweaks v1.6.1 +OrganonTweaks v1.7