From 4bd9e7836bac4e6b6d0196243eb814570ecb0730 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Tue, 21 Apr 2026 17:17:51 +0100 Subject: [PATCH] Bug 42188: (follow-up) Pretty-render biblio info column in log viewer The CATALOGUING info column for MODIFY/DELETE logs stores "biblio " followed by a pretty-printed JSON dump of the biblio hashref, which includes a MARC-in-JSON _marc structure. Raw, this is noisy and hard for librarians to read in the log viewer. Extract the nested-object formatter already used by the struct-diff renderer into a shared fmtJsonValue() helper, and use it from a new renderCatalogInfo() that runs over each div.loginfo on page load. If the cell text matches "biblio {JSON}" it is replaced with the pretty rendering; otherwise the cell is left untouched so non-catalog info payloads (and the bare "biblio" logged on ADD) are unaffected. Falling back silently on a parse failure means the old non-JSON Dumper-format item payloads continue to display as-is. Test plan: 1. Enable CataloguingLog. 2. Create, edit, and delete a biblio record via the staff UI. 3. Go to Tools -> Log viewer, filter by module=Cataloguing. 4. Verify: - ADD row's info cell shows just "biblio". - MODIFY and DELETE rows' info cells render as a nested list (leader, fields array of tag/indicator/subfield objects, plus frameworkcode/opac_suppressed/serial). - Diff column still renders the before/after struct-diff table. 5. Check another module's log entries (e.g. MEMBERS) are untouched. Signed-off-by: David Nind Signed-off-by: Lisette Scheer Signed-off-by: Lucas Gass --- .../prog/en/modules/tools/viewlog.tt | 10 +++ koha-tmpl/intranet-tmpl/prog/js/viewlog.js | 89 +++++++++++++------ 2 files changed, 72 insertions(+), 27 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt index e666323931..179875227f 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt @@ -95,6 +95,16 @@ table.struct-diff ul.diff-obj li { white-space: nowrap; } + /* Catalog log info column (biblio JSON payload) */ + div.loginfo-struct { + font-family: monospace; + font-size: 0.85em; + } + div.loginfo-struct ul.diff-obj { + list-style: none; + margin: 0; + padding: 0 0 0 0.8em; + } [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/js/viewlog.js b/koha-tmpl/intranet-tmpl/prog/js/viewlog.js index 399732f0e2..c2b45ebea6 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/viewlog.js +++ b/koha-tmpl/intranet-tmpl/prog/js/viewlog.js @@ -21,6 +21,56 @@ function collectDiffEntries(node, prefix) { return entries; } +/** + * Pretty-print a parsed JSON value as nested
    markup. Arrays become + * ordered lists preserving MARC-in-JSON field order; objects render their + * keys. Used by both the struct-diff renderer and the catalog info renderer. + */ +function fmtJsonValue(v) { + if (v === undefined || v === null) return ""; + if (typeof v !== "object") return $("").text(String(v)).html(); + if (Array.isArray(v)) { + var items = v + .map(function (item) { + return "
  • " + fmtJsonValue(item) + "
  • "; + }) + .join(""); + return '
      ' + items + "
    "; + } + var items = Object.keys(v) + .map(function (k) { + return ( + "
  • " + + $("").text(k).html() + + ": " + + fmtJsonValue(v[k]) + + "
  • " + ); + }) + .join(""); + return '
      ' + items + "
    "; +} + +/** + * Render the info column for a CATALOGUING log entry. The payload is + * "biblio " where the JSON is the pre-change (MODIFY) or final + * (DELETE) state of the record, including a MARC-in-JSON `_marc` key. + * Returns rendered HTML, or null if the text doesn't look like a + * biblio JSON payload (e.g. the bare "biblio" prefix logged on ADD). + */ +function renderCatalogInfo(raw) { + if (!raw) return null; + var match = raw.match(/^biblio\s+(\{[\s\S]*\})\s*$/); + if (!match) return null; + var parsed; + try { + parsed = JSON.parse(match[1]); + } catch (e) { + return null; + } + return '
    ' + fmtJsonValue(parsed) + "
    "; +} + /** * Render Struct::Diff JSON as a human-readable before/after table. * Falls back to a
     block for non-Struct::Diff JSON or plain text.
    @@ -40,39 +90,16 @@ function renderStructDiff(raw) {
         var entries = collectDiffEntries(diff.D, "");
         if (!entries.length) return "";
     
    -    function fmt(v) {
    -        if (v === undefined || v === null) return "";
    -        if (typeof v !== "object") return $("").text(String(v)).html();
    -        if (Array.isArray(v)) {
    -            var items = v
    -                .map(function (item) {
    -                    return "
  • " + fmt(item) + "
  • "; - }) - .join(""); - return '
      ' + items + "
    "; - } - var items = Object.keys(v) - .map(function (k) { - return ( - "
  • " + - $("").text(k).html() + - ": " + - fmt(v[k]) + - "
  • " - ); - }) - .join(""); - return '
      ' + items + "
    "; - } - var rows = entries .map(function (e) { var before = e.before !== undefined - ? "" + fmt(e.before) + "" + ? "" + fmtJsonValue(e.before) + "" : ""; var after = - e.after !== undefined ? "" + fmt(e.after) + "" : ""; + e.after !== undefined + ? "" + fmtJsonValue(e.after) + "" + : ""; return ( "" + '' + @@ -269,6 +296,14 @@ $(document).ready(function () { $(this).html(renderStructDiff(raw)); } }); + + $("div.loginfo").each(function () { + var raw = $(this).text().trim(); + var rendered = renderCatalogInfo(raw); + if (rendered) { + $(this).html(rendered); + } + }); patron_autocomplete($("#user"), { "on-select-callback": function (event, ui) { $("#user").val(ui.item.patron_id);