5 Commits

9 changed files with 155 additions and 5 deletions

59
Asset/js/relocate.js Normal file
View File

@@ -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 <ul>, and its
* dropdown JS clones just that <ul> 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 <ul> 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 <ul>
// 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();
}
})();

View File

@@ -0,0 +1,34 @@
<?php
namespace Kanboard\Plugin\FinanceBuddy\Controller;
use Kanboard\Controller\BaseController;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Plugin\FinanceBuddy\Helper\FinanceHelper;
/**
* Toggle a board column's header total on/off from the column dropdown menu. The per-column flag
* lives in project metadata; project-manager gated (same scope as enabling FinanceBuddy). Returns
* to the board, whose reload re-renders the header with or without the total.
*/
class FinanceColumnController extends BaseController
{
public function toggleTotal()
{
$this->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'])));
}
}

View File

@@ -19,6 +19,13 @@ class FinanceHelper extends Base
const INSTALLMENT_CURRENT_KEY = 'financebuddy_installment_current'; const INSTALLMENT_CURRENT_KEY = 'financebuddy_installment_current';
const INSTALLMENT_TOTAL_KEY = 'financebuddy_installment_total'; 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_<column_id> = '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 // 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. // non-finance task updates (moves, API calls) are ignored by the persistence hooks.
const FORM_MARKER = 'financebuddy_form'; const FORM_MARKER = 'financebuddy_form';
@@ -110,6 +117,18 @@ class FinanceHelper extends Base
return $net; 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 * 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. * stored metadata for an existing task, otherwise blanks for a new task.

View File

@@ -47,6 +47,14 @@ class Plugin extends Base
// Per-column net total (credits - debits) in the column header. // Per-column net total (credits - debits) in the column header.
$this->template->hook->attach('template:board:column:header', 'financeBuddy:board/column_total'); $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 <ul>, 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 // 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 // card shows its money whether you are on the board or reading the card. Reuses the board
// badge template (both hooks pass $task). // badge template (both hooks pass $task).
@@ -77,7 +85,7 @@ class Plugin extends Base
public function getPluginVersion() public function getPluginVersion()
{ {
return '1.1.1'; return '1.2.2';
} }
public function getPluginHomepage() public function getPluginHomepage()

View File

@@ -51,6 +51,10 @@ Each column header shows a **`Total:`** of its cards -- credits positive, debits
net): red when negative (what you owe), green when positive, gray at zero. It is shown for every net): red when negative (what you owe), green when positive, gray at zero. It is shown for every
column, including `Total: R$0,00`. column, including `Total: R$0,00`.
You can hide the total on a per-column basis: open the column's dropdown menu (the caret next to
the column title) and pick **"Hide column total"**; the same menu then offers **"Show column
total"** to bring it back. This is a per-board manager action, remembered per column.
### Installments and recurrence ### Installments and recurrence
FinanceBuddy carries the money across copies. Kanboard's built-in recurrence and RecoReco's FinanceBuddy carries the money across copies. Kanboard's built-in recurrence and RecoReco's
@@ -88,13 +92,16 @@ Settings -> Integrations and tick "Enable FinanceBuddy on this board".
AGPL-3.0. See LICENSE. AGPL-3.0. See LICENSE.
## More Kanboard plugins by Ruben (drbeco) ## More Kanboard plugins by Dr. Beco
All free and AGPL-3.0, at [code.beco.cc](https://code.beco.cc/beco): All free and AGPL-3.0, at [code.beco.cc](https://code.beco.cc/beco):
- **[RecoReco](https://code.beco.cc/beco/RecoReco)** -- calendar-scheduled recurring cards (yearly, - **[RecoReco](https://code.beco.cc/beco/RecoReco)** -- calendar-scheduled recurring cards (yearly,
monthly, weekly, daily), driven by the card due date. Pairs directly with FinanceBuddy: recurring monthly, weekly, daily), driven by the card due date.
bills spawn on schedule and their installments count down until the plan is paid off. - **[QualKard](https://code.beco.cc/beco/QualKard)** -- flashcard spaced repetition: grade a card by
dragging it to a column, and it comes back when due (needs RecoReco).
- **[WorkspaceOrg](https://code.beco.cc/beco/WorkspaceOrg)** -- group your projects into personal
per-user workspaces, shown on a "My workspaces" dashboard page with a badge on each project.
- **[OrganonTweaks](https://code.beco.cc/beco/OrganonTweaks)** -- an umbrella of small - **[OrganonTweaks](https://code.beco.cc/beco/OrganonTweaks)** -- an umbrella of small
quality-of-life board tweaks: remove an empty column, always show the comment icon, emphasize due quality-of-life board tweaks: remove an empty column, always show the comment icon, emphasize due
dates, extra search keywords and shared board filters, and more. dates, extra search keywords and shared board filters, and more.

View File

@@ -0,0 +1,17 @@
<?php if ($this->FinanceHelper->isEnabled($column['project_id']) && $this->user->hasProjectAccess('ProjectEditController', 'edit', $column['project_id'])): ?>
<?php /* Rendered here (outside the menu <ul>) by the only column-menu hook, then moved into the
column's real dropdown <ul> by Asset/js/relocate.js so it sits next to the native
entries. Hidden until relocated to avoid a flash in the header. */ ?>
<?php $hidden = $this->FinanceHelper->isTotalHidden($column['project_id'], $column['id']); ?>
<li class="financebuddy-menu-item" style="display: none;">
<?= $this->url->link(
$hidden
? '<i class="fa fa-eye fa-fw"></i> '.t('Show column total')
: '<i class="fa fa-eye-slash fa-fw"></i> '.t('Hide column total'),
'FinanceColumnController',
'toggleTotal',
array('plugin' => 'FinanceBuddy', 'project_id' => $column['project_id'], 'column_id' => $column['id']),
true
) ?>
</li>
<?php endif ?>

View File

@@ -7,6 +7,11 @@ if (empty($column['project_id']) || ! $this->FinanceHelper->isEnabled($column['p
return; 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); $net = $this->FinanceHelper->columnNet($column);
if ($net > 0) { if ($net > 0) {

View File

@@ -18,3 +18,4 @@
<div class="form-actions"> <div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button> <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
</div> </div>
<hr>

View File

@@ -1 +1 @@
FinanceBuddy v1.1.1 FinanceBuddy v1.2.2