Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/comment/assets/js/comment.admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
var status = document.getElementById('comment-status');

grid = tigerDataTable('#comment-grid', {
service: { module: 'comment', service: 'comment', method: 'datatable' },
service: { module: 'comment', service: 'comment', action: 'datatable' },
extraData: function () { return { status: status ? status.value : 'pending' }; },
order: [[3, 'desc']],
columns: [
Expand Down
5 changes: 3 additions & 2 deletions modules/comment/controllers/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ public function indexAction()
throw new Zend_Controller_Action_Exception('Comments are disabled', 404);
}

$this->view->title = 'Comments — Tiger Admin';
$this->view->statuses = Tiger_Model_Comment::STATUSES;
$this->view->title = 'Comments — Tiger Admin';
$this->view->statuses = Tiger_Model_Comment::STATUSES;
$this->view->useDataTables = true; // the layout loads jQuery + DataTables when set

// The AI spam control only APPEARS when there is a live agent to run it — an always-visible
// toggle that silently does nothing is worse than no toggle.
Expand Down
2 changes: 1 addition & 1 deletion modules/comment/services/Comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ public function datatable(array $params): void
if (!$this->_isAllowed('moderate')) { $this->_error('core.api.error.not_allowed'); return; }
if (!$this->_enabled()) { return; }

$dt = $this->_dtParams();
$dt = $this->_dtParams($params);
$status = (string) ($params['status'] ?? Tiger_Model_Comment::STATUS_PENDING);
$rows = (new Tiger_Model_Comment())->byStatus($status, 500);

Expand Down
2 changes: 1 addition & 1 deletion modules/newsletter/assets/js/newsletter.admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
var status = document.getElementById('newsletter-status');

grid = tigerDataTable('#newsletter-grid', {
service: { module: 'newsletter', service: 'subscribe', method: 'datatable' },
service: { module: 'newsletter', service: 'subscribe', action: 'datatable' },
extraData: function () { return { status: status ? status.value : '' }; },
order: [[4, 'desc']],
columns: [
Expand Down
5 changes: 3 additions & 2 deletions modules/newsletter/controllers/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public function init()
/** The subscriber list. */
public function indexAction()
{
$this->view->title = 'Newsletter — Tiger Admin';
$this->view->statuses = Newsletter_Model_Subscriber::STATUSES;
$this->view->title = 'Newsletter — Tiger Admin';
$this->view->statuses = Newsletter_Model_Subscriber::STATUSES;
$this->view->useDataTables = true; // the layout loads jQuery + DataTables when set
}
}
7 changes: 4 additions & 3 deletions modules/system/controllers/SitesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ public function init()
/** `/system/sites` — the host → org mappings grid + an add-domain form. */
public function indexAction()
{
$this->view->title = 'Sites — Tiger Admin';
$this->view->form = new System_Form_Site();
$this->view->orgCount = (int) (new Tiger_Model_Org())->fetchAll(
$this->view->title = 'Sites — Tiger Admin';
$this->view->useDataTables = true; // the layout loads jQuery + DataTables when set
$this->view->form = new System_Form_Site();
$this->view->orgCount = (int) (new Tiger_Model_Org())->fetchAll(
(new Tiger_Model_Org())->activeSelect()
)->count();
}
Expand Down
18 changes: 13 additions & 5 deletions tests/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,18 @@ test account. Without it the authenticated scenarios are skipped; everything ano

## Exit codes

Both probes exit non-zero on a real failure, so they drop straight into CI or a cron.
A check that could not run (e.g. reCAPTCHA refused a tokenless client) reports **GATED**, not
PASS — a vacuous green is worse than no check.
Both probes exit non-zero on a real failure, so either drops into a script when you want that.
A check that could not run reports **GATED**, not PASS — a vacuous green is worse than no check.
With reCAPTCHA switched off on the test install (`tiger.recaptcha.enabled = 0`) nothing is gated;
see the probe's own footer.

## Promotion
## Scope — scenarios, by decision

A confirmed finding becomes a spec in `regression/` so it cannot come back silently.
These are **scenarios, run on demand.** There is deliberately no CI job, no promoted-spec
directory and no Playwright suite to maintain: the cost of a regression harness is ongoing and
the value here is finding real defects on real screens, which is what an on-demand pass does.

So a confirmed finding becomes a **Jira ticket** (labelled `functional-user-test` + `ui-probe`),
not a test file. Where a whole CLASS of defect turns out to be statically detectable, the cheap
exhaustive home for it is a check in `paradigm-scan.php` — one scan, every view, milliseconds —
rather than a scenario per screen.
18 changes: 18 additions & 0 deletions themes/puma/assets/js/tiger.datatable.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,28 @@
var $ = window.jQuery;
if (!$ || !$.fn || !$.fn.DataTable) { return null; }

// DataTables reports its own errors with a browser alert() by default, which breaks the house
// rule (no alert/confirm/prompt — AGENTS.md) and, worse, hides the failure from the console,
// where our cheapest detector looks. 'throw' routes it to the console instead: visible to a
// developer, visible to the browser probe, and it can never block the page behind a modal.
if ($.fn.dataTable && $.fn.dataTable.ext) { $.fn.dataTable.ext.errMode = 'throw'; }

opts = opts || {};
var svc = opts.service || {};
var extra = (typeof opts.extraData === 'function') ? opts.extraData : function () { return {}; };

// The /api message names module + service + ACTION. `action` is the key this helper reads; a
// grid that says `method` instead sends no action at all, and the gateway can only refuse it.
// Refusing here names the real cause at the call site rather than letting DataTables report a
// generic warning about a request that was never going to work.
if (!svc.module || !svc.service || !svc.action) {
window.console && console.error(
'tigerDataTable(' + selector + '): service needs { module, service, action } — got ' +
JSON.stringify(svc) + (svc.method ? ' (use `action`, not `method`)' : '')
);
return null;
}

// Sensible defaults; caller's opts win. `service`/`extraData` are ours, not DT's.
// stateSave persists page / length / search / order per grid (localStorage, keyed by table id +
// path) so a grid remembers where you were across reloads + navigation. A caller can opt out with
Expand Down
Loading