on, 'off' -> off, 'default'/absent -> follow the LIVE global default. * * The stored values are the non-falsy strings 'on'/'off' (not '1'/'0'): Kanboard's * MetadataModel::get() uses `?:`, and PHP treats the string "0" as falsy, so a stored "0" would * come back as the 'default' fallback and "Always off" would never be honored. * * Also maintains shared, reserved-name custom filters on every board (v1.5 "Show all tasks", v1.6 * "Due this month" / "Around this month"), each group gated by its own config toggle. */ class OrganonProjectHelper extends Base { const EMPHASIZE_KEY = 'organon_tweaks_emphasize_duedate'; const EMPHASIZE_DEFAULT_KEY = 'organon_tweaks_emphasize_duedate_default'; // 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 // with a reserved name is absorbed into the canonical form. "Show all tasks" is a REPLACE filter // (append 0) so it clears; the rest are APPEND (1) so clicks stack (distinct attributes AND). const GROUP_SHOW_ALL = array( array('Show all tasks', '', 0), ); const GROUP_MONTH = array( 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). * * @return bool */ public function isEmphasizeDueDate() { $project_id = $this->request->getIntegerParam('project_id'); if ($project_id <= 0) { return false; } $value = $this->projectMetadataModel->get($project_id, self::EMPHASIZE_KEY, 'default'); if ($value === 'on') { return true; } if ($value === 'off') { return false; } // 'default' (or absent) -> follow the live global default. return (int) $this->configModel->get(self::EMPHASIZE_DEFAULT_KEY, 0) === 1; } /** * Ensure one canonical reserved custom filter on a board (shared). Idempotent: create when * absent, force a same-named one back to the canonical form (query + shared + append) since the * name is reserved, and drop duplicates. No-op without a real project and a real owner * (custom_filters.user_id has a FK to users, so a system id like 0 would fail the insert). * * @param int $project_id * @param string $name * @param string $query * @param int $append */ public function ensureReservedFilter($project_id, $name, $query, $append) { $project_id = (int) $project_id; $user_id = (int) $this->userSession->getId(); if ($project_id <= 0 || $user_id <= 0) { return; } $append = $append ? 1 : 0; $existing = $this->db->table(CustomFilterModel::TABLE) ->eq('project_id', $project_id) ->eq('name', $name) ->findAll(); if (empty($existing)) { $this->db->table(CustomFilterModel::TABLE)->insert(array( 'filter' => $query, 'project_id' => $project_id, 'user_id' => $user_id, 'name' => $name, 'is_shared' => 1, 'append' => $append, )); return; } // Reserved name: keep one, force it to the canonical form, drop the rest. $keep = array_shift($existing); if ($keep['filter'] !== $query || (int) $keep['is_shared'] !== 1 || (int) $keep['append'] !== $append) { $this->db->table(CustomFilterModel::TABLE) ->eq('id', $keep['id']) ->update(array('filter' => $query, 'is_shared' => 1, 'append' => $append)); } foreach ($existing as $duplicate) { $this->db->table(CustomFilterModel::TABLE)->eq('id', $duplicate['id'])->remove(); } } /** * Ensure every ENABLED group's filters on a board. Called lazily on each project-header render, * so it covers old and new boards alike. * * @param int $project_id */ public function ensureBoardFilters($project_id) { if ((int) $this->configModel->get(self::SHOW_ALL_FILTER_KEY, 1) === 1) { $this->ensureGroup($project_id, self::GROUP_SHOW_ALL); } 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) { foreach ($rows as $row) { $this->ensureReservedFilter($project_id, $row[0], $row[1], $row[2]); } } /** * Toggle turned ON: eagerly (re)create a group's filters on every existing board. * ensureBoardFilters() keeps covering boards created afterwards. * * @param array $rows */ public function addGroupToAllProjects(array $rows) { foreach ($this->projectModel->getAll() as $project) { $this->ensureGroup((int) $project['id'], $rows); } } /** * Toggle turned OFF: remove a group's filters across all boards (by their reserved names). * * @param array $rows */ public function removeGroup(array $rows) { $names = array(); foreach ($rows as $row) { $names[] = $row[0]; } $this->db->table(CustomFilterModel::TABLE)->in('name', $names)->remove(); } }