diff --git a/Asset/js/relocate.js b/Asset/js/relocate.js
new file mode 100644
index 0000000..b5376b3
--- /dev/null
+++ b/Asset/js/relocate.js
@@ -0,0 +1,59 @@
+/*
+ * FinanceBuddy -- place the "Hide/Show column total" entry inside the native column menu.
+ *
+ * Kanboard only exposes the template:board:column:dropdown hook AFTER the column menu
, and its
+ * dropdown JS clones just that when the menu opens. So the hook item cannot reach the menu on
+ * its own. column_dropdown.php renders the item hidden next to the menu; this script moves each such
+ * item into that column's real so it appears alongside "Hide this column", etc.
+ *
+ * BoardDragAndDrop.refresh() does $("#board-container").replaceWith(data) on every drag-and-drop and
+ * poll refresh, so the observer is bound to the STABLE parent of #board-container (which survives the
+ * swap), not to #board-container itself -- otherwise it would be left watching a detached node and
+ * the entry would vanish until a full page reload.
+ */
+(function () {
+ "use strict";
+
+ function relocate() {
+ var items = document.querySelectorAll(".financebuddy-menu-item");
+
+ for (var i = 0; i < items.length; i++) {
+ var li = items[i];
+
+ // Already moved into a menu list: just make sure it is visible.
+ if (li.closest("ul")) {
+ li.style.display = "";
+ continue;
+ }
+
+ // The hook renders the item as a child of the column's dropdown wrapper; its first
+ // is the native menu.
+ var menu = li.parentNode ? li.parentNode.querySelector("ul") : null;
+
+ if (menu) {
+ menu.appendChild(li);
+ li.style.display = "";
+ }
+ }
+ }
+
+ function init() {
+ var container = document.getElementById("board-container");
+
+ if (!container) {
+ return;
+ }
+
+ relocate();
+
+ if (window.MutationObserver && container.parentNode) {
+ new MutationObserver(relocate).observe(container.parentNode, { childList: true, subtree: true });
+ }
+ }
+
+ if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", init);
+ } else {
+ init();
+ }
+})();
diff --git a/Controller/FinanceColumnController.php b/Controller/FinanceColumnController.php
new file mode 100644
index 0000000..51d25aa
--- /dev/null
+++ b/Controller/FinanceColumnController.php
@@ -0,0 +1,34 @@
+checkCSRFParam();
+ $project = $this->getProject();
+
+ if (! $this->helper->user->hasProjectAccess('ProjectEditController', 'edit', $project['id'])) {
+ throw new AccessForbiddenException();
+ }
+
+ $key = FinanceHelper::HIDE_TOTAL_PREFIX.$this->request->getIntegerParam('column_id');
+
+ // Store the literal 'on'/'off' (both non-empty) rather than 1/0, so the flag is never lost
+ // to PHP's empty("0") === true on the metadata save.
+ $hidden = $this->projectMetadataModel->get($project['id'], $key, FinanceHelper::HIDE_TOTAL_OFF) === FinanceHelper::HIDE_TOTAL_ON;
+ $this->projectMetadataModel->save($project['id'], array($key => $hidden ? FinanceHelper::HIDE_TOTAL_OFF : FinanceHelper::HIDE_TOTAL_ON));
+
+ $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])));
+ }
+}
diff --git a/Helper/FinanceHelper.php b/Helper/FinanceHelper.php
index ddb5e24..2b3b146 100644
--- a/Helper/FinanceHelper.php
+++ b/Helper/FinanceHelper.php
@@ -19,6 +19,13 @@ class FinanceHelper extends Base
const INSTALLMENT_CURRENT_KEY = 'financebuddy_installment_current';
const INSTALLMENT_TOTAL_KEY = 'financebuddy_installment_total';
+ // Per-column opt-out of the header total. Stored per column in PROJECT metadata as
+ // financebuddy_hide_total_ = 'on' (hidden) or 'off' (shown, the default). Literal
+ // on/off, never 1/0, so the flag is never dropped by the metadata save (PHP empty("0") === true).
+ const HIDE_TOTAL_PREFIX = 'financebuddy_hide_total_';
+ const HIDE_TOTAL_ON = 'on';
+ const HIDE_TOTAL_OFF = 'off';
+
// Hidden marker present only when the task form was submitted with the finance fieldset, so
// non-finance task updates (moves, API calls) are ignored by the persistence hooks.
const FORM_MARKER = 'financebuddy_form';
@@ -110,6 +117,18 @@ class FinanceHelper extends Base
return $net;
}
+ /**
+ * Has this column's header total been hidden (per-column opt-out)?
+ *
+ * @param integer $project_id
+ * @param integer $column_id
+ * @return bool
+ */
+ public function isTotalHidden($project_id, $column_id)
+ {
+ return $this->projectMetadataModel->get((int) $project_id, self::HIDE_TOTAL_PREFIX.(int) $column_id, self::HIDE_TOTAL_OFF) === self::HIDE_TOTAL_ON;
+ }
+
/**
* Values to prefill the task-form fields: a re-post (after a validation error) wins, then the
* stored metadata for an existing task, otherwise blanks for a new task.
diff --git a/Plugin.php b/Plugin.php
index 325f57e..56068ea 100644
--- a/Plugin.php
+++ b/Plugin.php
@@ -47,6 +47,14 @@ class Plugin extends Base
// Per-column net total (credits - debits) in the column header.
$this->template->hook->attach('template:board:column:header', 'financeBuddy:board/column_total');
+ // "Hide/Show column total" toggle in the column dropdown menu. The only column-menu hook
+ // renders after the native , so column_dropdown.php emits the item hidden and relocate.js
+ // moves it into the menu (same technique as BulkMoveTasks, with the stable-parent observer).
+ $this->template->hook->attach('template:board:column:dropdown', 'financeBuddy:board/column_dropdown');
+ $this->hook->on('template:layout:js', array(
+ 'template' => 'plugins/FinanceBuddy/Asset/js/relocate.js',
+ ));
+
// The same money badge at the top of the opened card (task view), under the title, so a
// card shows its money whether you are on the board or reading the card. Reuses the board
// badge template (both hooks pass $task).
diff --git a/Template/board/column_dropdown.php b/Template/board/column_dropdown.php
new file mode 100644
index 0000000..65fb46d
--- /dev/null
+++ b/Template/board/column_dropdown.php
@@ -0,0 +1,17 @@
+FinanceHelper->isEnabled($column['project_id']) && $this->user->hasProjectAccess('ProjectEditController', 'edit', $column['project_id'])): ?>
+ ) by the only column-menu hook, then moved into the
+ column's real dropdown by Asset/js/relocate.js so it sits next to the native
+ entries. Hidden until relocated to avoid a flash in the header. */ ?>
+ FinanceHelper->isTotalHidden($column['project_id'], $column['id']); ?>
+
+
diff --git a/Template/board/column_total.php b/Template/board/column_total.php
index aa0be64..fcf8402 100644
--- a/Template/board/column_total.php
+++ b/Template/board/column_total.php
@@ -7,6 +7,11 @@ if (empty($column['project_id']) || ! $this->FinanceHelper->isEnabled($column['p
return;
}
+// Per-column opt-out, toggled from the column dropdown menu.
+if ($this->FinanceHelper->isTotalHidden($column['project_id'], $column['id'])) {
+ return;
+}
+
$net = $this->FinanceHelper->columnNet($column);
if ($net > 0) {