summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLester Caine <lester@lsces.co.uk>2026-05-22 15:44:44 +0100
committerLester Caine <lester@lsces.co.uk>2026-05-22 15:44:44 +0100
commit572b562f433532429423a5ccace64838efec93c7 (patch)
tree2c7a9dbe8570578a58d5b396a15388243628ffda
parent825ce764d0e6bcdcaccad653a346e03a25cd267f (diff)
downloadliberty-572b562f433532429423a5ccace64838efec93c7.tar.gz
liberty-572b562f433532429423a5ccace64838efec93c7.tar.bz2
liberty-572b562f433532429423a5ccace64838efec93c7.zip
Add generic xref group/source admin pages to liberty
LibertyXrefType: add getContentTypeGuids() and getGroupList() static methods. New admin pages with package filter (session-persisted content_type_guid dropdown): admin_xref_groups.php manages liberty_xref_type rows; admin_xref_sources.php manages liberty_xref_source rows. Both support add and delete (delete guarded by entry/source count). Wired into menu_liberty_admin.tpl as Xref Groups / Xref Sources links. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--admin/admin_xref_groups.php62
-rw-r--r--admin/admin_xref_sources.php71
-rw-r--r--includes/classes/LibertyXrefType.php42
-rw-r--r--templates/admin_xref_groups.tpl100
-rw-r--r--templates/admin_xref_sources.tpl120
-rwxr-xr-xtemplates/menu_liberty_admin.tpl2
6 files changed, 397 insertions, 0 deletions
diff --git a/admin/admin_xref_groups.php b/admin/admin_xref_groups.php
new file mode 100644
index 0000000..8d4ae58
--- /dev/null
+++ b/admin/admin_xref_groups.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Admin page for managing liberty_xref_type groups across all packages.
+ * @package liberty
+ */
+
+use Bitweaver\Liberty\LibertyXrefType;
+use Bitweaver\KernelTools;
+
+require_once '../../kernel/includes/setup_inc.php';
+
+$gBitSystem->verifyPermission( 'p_admin' );
+
+// Persist selected content_type_guid in session
+if ( isset( $_REQUEST['content_type_guid'] ) ) {
+ $_SESSION['liberty_xref_admin_guid'] = $_REQUEST['content_type_guid'];
+}
+$activeGuid = $_SESSION['liberty_xref_admin_guid'] ?? '';
+
+// Add a new group
+if ( !empty( $_REQUEST['fAddGroup'] ) ) {
+ $xrefType = trim( $_REQUEST['xref_type'] ?? '' );
+ $guid = trim( $_REQUEST['new_content_type_guid'] ?? $activeGuid );
+ $title = trim( $_REQUEST['title'] ?? '' );
+ $sortOrder = (int)( $_REQUEST['sort_order'] ?? 0 );
+ $roleId = (int)( $_REQUEST['role_id'] ?? 3 );
+ if ( $xrefType && $guid && $title ) {
+ $gBitDb->query(
+ "INSERT INTO `".BIT_DB_PREFIX."liberty_xref_type` (`xref_type`,`content_type_guid`,`title`,`sort_order`,`role_id`,`type_href`) VALUES (?,?,?,?,?,'')",
+ [ $xrefType, $guid, $title, $sortOrder, $roleId ]
+ );
+ }
+}
+
+// Delete a group (only if no sources are attached)
+if ( !empty( $_REQUEST['fDeleteGroup'] ) ) {
+ $xrefType = $_REQUEST['xref_type'] ?? '';
+ $guid = $_REQUEST['del_content_type_guid'] ?? '';
+ if ( $xrefType && $guid ) {
+ $count = $gBitDb->getOne(
+ "SELECT COUNT(*) FROM `".BIT_DB_PREFIX."liberty_xref_source` WHERE `xref_type` = ? AND `content_type_guid` = ?",
+ [ $xrefType, $guid ]
+ );
+ if ( $count == 0 ) {
+ $gBitDb->query(
+ "DELETE FROM `".BIT_DB_PREFIX."liberty_xref_type` WHERE `xref_type` = ? AND `content_type_guid` = ?",
+ [ $xrefType, $guid ]
+ );
+ } else {
+ $gBitSmarty->assign( 'deleteError', "Cannot delete '$xrefType' — $count source(s) still attached." );
+ }
+ }
+}
+
+$guidList = LibertyXrefType::getContentTypeGuids();
+$groups = LibertyXrefType::getGroupList( $activeGuid ? [ 'content_type_guid' => $activeGuid ] : [] );
+
+$gBitSmarty->assign( 'activeGuid', $activeGuid );
+$gBitSmarty->assign( 'guidList', $guidList );
+$gBitSmarty->assign( 'xref_groups', $groups );
+
+$gBitSystem->display( 'bitpackage:liberty/admin_xref_groups.tpl', KernelTools::tra( 'Xref Groups' ), [ 'display_mode' => 'admin' ] );
diff --git a/admin/admin_xref_sources.php b/admin/admin_xref_sources.php
new file mode 100644
index 0000000..c86b0d3
--- /dev/null
+++ b/admin/admin_xref_sources.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Admin page for managing liberty_xref_source entries across all packages.
+ * @package liberty
+ */
+
+use Bitweaver\Liberty\LibertyXrefType;
+use Bitweaver\KernelTools;
+
+require_once '../../kernel/includes/setup_inc.php';
+
+$gBitSystem->verifyPermission( 'p_admin' );
+
+// Persist selected content_type_guid in session
+if ( isset( $_REQUEST['content_type_guid'] ) ) {
+ $_SESSION['liberty_xref_admin_guid'] = $_REQUEST['content_type_guid'];
+}
+$activeGuid = $_SESSION['liberty_xref_admin_guid'] ?? '';
+
+// Add a new source
+if ( !empty( $_REQUEST['fAddSource'] ) ) {
+ $source = trim( $_REQUEST['source'] ?? '' );
+ $guid = trim( $_REQUEST['new_content_type_guid'] ?? $activeGuid );
+ $xrefType = trim( $_REQUEST['xref_type'] ?? '' );
+ $title = trim( $_REQUEST['cross_ref_title'] ?? '' );
+ $template = trim( $_REQUEST['template'] ?? '' );
+ $href = trim( $_REQUEST['cross_ref_href'] ?? '' );
+ $multi = (int)( $_REQUEST['multi'] ?? 0 );
+ $roleId = (int)( $_REQUEST['role_id'] ?? 3 );
+ if ( $source && $guid && $xrefType && $title ) {
+ $gBitDb->query(
+ "INSERT INTO `".BIT_DB_PREFIX."liberty_xref_source` (`source`,`content_type_guid`,`xref_type`,`cross_ref_title`,`multi`,`role_id`,`cross_ref_href`,`template`,`data`) VALUES (?,?,?,?,?,?,?,?,NULL)",
+ [ $source, $guid, $xrefType, $title, $multi, $roleId, $href, $template ]
+ );
+ }
+}
+
+// Delete a source (only if no xref records use it)
+if ( !empty( $_REQUEST['fDeleteSource'] ) ) {
+ $source = $_REQUEST['source'] ?? '';
+ $guid = $_REQUEST['del_content_type_guid'] ?? '';
+ if ( $source && $guid ) {
+ $count = $gBitDb->getOne(
+ "SELECT COUNT(*) FROM `".BIT_DB_PREFIX."liberty_xref` WHERE `source` = ?",
+ [ $source ]
+ );
+ if ( $count == 0 ) {
+ $gBitDb->query(
+ "DELETE FROM `".BIT_DB_PREFIX."liberty_xref_source` WHERE `source` = ? AND `content_type_guid` = ?",
+ [ $source, $guid ]
+ );
+ } else {
+ $gBitSmarty->assign( 'deleteError', "Cannot delete '$source' — $count xref record(s) still use it." );
+ }
+ }
+}
+
+$guidList = LibertyXrefType::getContentTypeGuids();
+$sources = LibertyXrefType::getXrefTypeList( $activeGuid ? [ 'content_type_guid' => $activeGuid ] : [] );
+
+// Build group list for the add-form dropdown, filtered to active guid
+$groups = $activeGuid
+ ? LibertyXrefType::getGroupList( [ 'content_type_guid' => $activeGuid ] )
+ : [];
+
+$gBitSmarty->assign( 'activeGuid', $activeGuid );
+$gBitSmarty->assign( 'guidList', $guidList );
+$gBitSmarty->assign( 'xref_sources', $sources );
+$gBitSmarty->assign( 'xref_groups', $groups );
+
+$gBitSystem->display( 'bitpackage:liberty/admin_xref_sources.tpl', KernelTools::tra( 'Xref Sources' ), [ 'display_mode' => 'admin' ] );
diff --git a/includes/classes/LibertyXrefType.php b/includes/classes/LibertyXrefType.php
index 321db07..00f2cb2 100644
--- a/includes/classes/LibertyXrefType.php
+++ b/includes/classes/LibertyXrefType.php
@@ -50,4 +50,46 @@ class LibertyXrefType extends LibertyBase {
return $ret;
}
+
+ /**
+ * Returns the distinct content_type_guid values present in liberty_xref_type.
+ */
+ public static function getContentTypeGuids(): array {
+ global $gBitSystem;
+ $result = $gBitSystem->mDb->query(
+ "SELECT DISTINCT `content_type_guid` FROM `".BIT_DB_PREFIX."liberty_xref_type` ORDER BY `content_type_guid`",
+ []
+ );
+ $ret = [];
+ while ( $res = $result->fetchRow() ) {
+ $ret[] = $res['content_type_guid'];
+ }
+ return $ret;
+ }
+
+ /**
+ * Returns liberty_xref_type rows, optionally filtered by content_type_guid.
+ * Each row includes num_sources: count of sources defined for that group.
+ */
+ public static function getGroupList( $pOptionHash = NULL ): array {
+ global $gBitSystem;
+ $where = '';
+ $bindVars = [];
+ if ( !empty( $pOptionHash['content_type_guid'] ) ) {
+ $where = " WHERE cxt.`content_type_guid` = ?";
+ $bindVars[] = $pOptionHash['content_type_guid'];
+ }
+ $query = "SELECT cxt.* FROM `".BIT_DB_PREFIX."liberty_xref_type` cxt
+ $where ORDER BY cxt.`content_type_guid`, cxt.`sort_order`";
+ $result = $gBitSystem->mDb->query( $query, $bindVars );
+ $ret = [];
+ while ( $res = $result->fetchRow() ) {
+ $res['num_sources'] = $gBitSystem->mDb->getOne(
+ "SELECT COUNT(*) FROM `".BIT_DB_PREFIX."liberty_xref_source` WHERE `xref_type` = ? AND `content_type_guid` = ?",
+ [ $res['xref_type'], $res['content_type_guid'] ]
+ );
+ $ret[] = $res;
+ }
+ return $ret;
+ }
}
diff --git a/templates/admin_xref_groups.tpl b/templates/admin_xref_groups.tpl
new file mode 100644
index 0000000..3618c79
--- /dev/null
+++ b/templates/admin_xref_groups.tpl
@@ -0,0 +1,100 @@
+{* Admin: liberty_xref_type groups *}
+{strip}
+<div class="floaticon">{bithelp}</div>
+
+<div class="admin liberty">
+ <div class="header">
+ <h1>{tr}Xref Groups{/tr}</h1>
+ </div>
+
+ <div class="body">
+
+ {* Package filter *}
+ <form method="get" action="{$smarty.const.LIBERTY_PKG_URL}admin/admin_xref_groups.php" class="form-inline">
+ <div class="form-group">
+ <label for="content_type_guid">{tr}Package{/tr}</label>
+ <select name="content_type_guid" id="content_type_guid" class="form-control">
+ <option value="">{tr}— All packages —{/tr}</option>
+ {foreach from=$guidList item=guid}
+ <option value="{$guid|escape}" {if $guid eq $activeGuid}selected="selected"{/if}>{$guid|escape}</option>
+ {/foreach}
+ </select>
+ <button type="submit" class="btn btn-default">{tr}Filter{/tr}</button>
+ {if $activeGuid}
+ <a href="{$smarty.const.LIBERTY_PKG_URL}admin/admin_xref_groups.php?content_type_guid=" class="btn btn-link">{tr}Clear{/tr}</a>
+ {/if}
+ </div>
+ </form>
+
+ {if $deleteError}
+ <div class="alert alert-danger">{$deleteError|escape}</div>
+ {/if}
+
+ {* Add group form *}
+ {if $activeGuid}
+ {form legend="Add Xref Group" action="{$smarty.const.LIBERTY_PKG_URL}admin/admin_xref_groups.php"}
+ <input type="hidden" name="new_content_type_guid" value="{$activeGuid|escape}" />
+ <input type="hidden" name="content_type_guid" value="{$activeGuid|escape}" />
+ <div class="form-group">
+ {formlabel label="Key (xref_type)" for="xref_type"}
+ {forminput}<input type="text" id="xref_type" name="xref_type" class="form-control" />{/forminput}
+ </div>
+ <div class="form-group">
+ {formlabel label="Title" for="title"}
+ {forminput}<input type="text" id="title" name="title" class="form-control" />{/forminput}
+ </div>
+ <div class="form-group">
+ {formlabel label="Sort Order" for="sort_order"}
+ {forminput}<input type="number" id="sort_order" name="sort_order" value="1" class="form-control" style="width:5em" />{/forminput}
+ </div>
+ <div class="form-group">
+ {formlabel label="Role ID" for="role_id"}
+ {forminput}<input type="number" id="role_id" name="role_id" value="3" class="form-control" style="width:5em" />{/forminput}
+ </div>
+ <div class="form-group submit">
+ <input type="submit" class="btn btn-primary" name="fAddGroup" value="{tr}Add Group{/tr}" />
+ </div>
+ {/form}
+ {/if}
+
+ {* Group list *}
+ <table class="table table-striped data">
+ <caption>{tr}Xref Groups{/tr} {if $activeGuid}— {$activeGuid|escape}{/if}</caption>
+ <thead>
+ <tr>
+ <th>{tr}Package{/tr}</th>
+ <th>{tr}Key{/tr}</th>
+ <th>{tr}Title{/tr}</th>
+ <th>{tr}Sort{/tr}</th>
+ <th>{tr}Role{/tr}</th>
+ <th>{tr}Sources{/tr}</th>
+ <th>{tr}Actions{/tr}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {foreach from=$xref_groups item=grp}
+ <tr>
+ <td>{$grp.content_type_guid|escape}</td>
+ <td><code>{$grp.xref_type|escape}</code></td>
+ <td>{$grp.title|escape}</td>
+ <td>{$grp.sort_order}</td>
+ <td>{$grp.role_id}</td>
+ <td>
+ <a href="{$smarty.const.LIBERTY_PKG_URL}admin/admin_xref_sources.php?content_type_guid={$grp.content_type_guid|escape}">{$grp.num_sources}</a>
+ </td>
+ <td>
+ {if $grp.num_sources eq 0}
+ <a href="{$smarty.const.LIBERTY_PKG_URL}admin/admin_xref_groups.php?fDeleteGroup=1&amp;xref_type={$grp.xref_type|escape}&amp;del_content_type_guid={$grp.content_type_guid|escape}&amp;content_type_guid={$activeGuid|escape}"
+ onclick="return confirm('{tr}Delete this group?{/tr}')">{booticon iname="icon-trash" ipackage="icons" iforce=icon_text iexplain="Delete"}</a>
+ {/if}
+ </td>
+ </tr>
+ {foreachelse}
+ <tr class="norecords"><td colspan="7">{tr}No groups found{/tr}</td></tr>
+ {/foreach}
+ </tbody>
+ </table>
+
+ </div><!-- end .body -->
+</div><!-- end .admin -->
+{/strip}
diff --git a/templates/admin_xref_sources.tpl b/templates/admin_xref_sources.tpl
new file mode 100644
index 0000000..58fddf6
--- /dev/null
+++ b/templates/admin_xref_sources.tpl
@@ -0,0 +1,120 @@
+{* Admin: liberty_xref_source entries *}
+{strip}
+<div class="floaticon">{bithelp}</div>
+
+<div class="admin liberty">
+ <div class="header">
+ <h1>{tr}Xref Sources{/tr}</h1>
+ </div>
+
+ <div class="body">
+
+ {* Package filter *}
+ <form method="get" action="{$smarty.const.LIBERTY_PKG_URL}admin/admin_xref_sources.php" class="form-inline">
+ <div class="form-group">
+ <label for="content_type_guid">{tr}Package{/tr}</label>
+ <select name="content_type_guid" id="content_type_guid" class="form-control">
+ <option value="">{tr}— All packages —{/tr}</option>
+ {foreach from=$guidList item=guid}
+ <option value="{$guid|escape}" {if $guid eq $activeGuid}selected="selected"{/if}>{$guid|escape}</option>
+ {/foreach}
+ </select>
+ <button type="submit" class="btn btn-default">{tr}Filter{/tr}</button>
+ {if $activeGuid}
+ <a href="{$smarty.const.LIBERTY_PKG_URL}admin/admin_xref_sources.php?content_type_guid=" class="btn btn-link">{tr}Clear{/tr}</a>
+ {/if}
+ </div>
+ </form>
+
+ {if $deleteError}
+ <div class="alert alert-danger">{$deleteError|escape}</div>
+ {/if}
+
+ {* Add source form — only when a package is selected (need a group to assign to) *}
+ {if $activeGuid && $xref_groups}
+ {form legend="Add Xref Source" action="{$smarty.const.LIBERTY_PKG_URL}admin/admin_xref_sources.php"}
+ <input type="hidden" name="new_content_type_guid" value="{$activeGuid|escape}" />
+ <input type="hidden" name="content_type_guid" value="{$activeGuid|escape}" />
+ <div class="form-group">
+ {formlabel label="Source key" for="source"}
+ {forminput}<input type="text" id="source" name="source" class="form-control" />{/forminput}
+ </div>
+ <div class="form-group">
+ {formlabel label="Group" for="xref_type"}
+ {forminput}
+ <select name="xref_type" id="xref_type" class="form-control">
+ {foreach from=$xref_groups item=grp}
+ <option value="{$grp.xref_type|escape}">{$grp.title|escape} ({$grp.xref_type|escape})</option>
+ {/foreach}
+ </select>
+ {/forminput}
+ </div>
+ <div class="form-group">
+ {formlabel label="Title" for="cross_ref_title"}
+ {forminput}<input type="text" id="cross_ref_title" name="cross_ref_title" class="form-control" />{/forminput}
+ </div>
+ <div class="form-group">
+ {formlabel label="Template" for="template"}
+ {forminput}<input type="text" id="template" name="template" placeholder="text / address / phone …" class="form-control" />{/forminput}
+ </div>
+ <div class="form-group">
+ {formlabel label="Href" for="cross_ref_href"}
+ {forminput}<input type="text" id="cross_ref_href" name="cross_ref_href" class="form-control" />{/forminput}
+ </div>
+ <div class="form-group">
+ {formlabel label="Multi" for="multi"}
+ {forminput}<input type="number" id="multi" name="multi" value="0" class="form-control" style="width:5em" />{/forminput}
+ </div>
+ <div class="form-group">
+ {formlabel label="Role ID" for="role_id"}
+ {forminput}<input type="number" id="role_id" name="role_id" value="3" class="form-control" style="width:5em" />{/forminput}
+ </div>
+ <div class="form-group submit">
+ <input type="submit" class="btn btn-primary" name="fAddSource" value="{tr}Add Source{/tr}" />
+ </div>
+ {/form}
+ {/if}
+
+ {* Source list *}
+ <table class="table table-striped data">
+ <caption>{tr}Xref Sources{/tr} {if $activeGuid}— {$activeGuid|escape}{/if}</caption>
+ <thead>
+ <tr>
+ <th>{tr}Package{/tr}</th>
+ <th>{tr}Source{/tr}</th>
+ <th>{tr}Group{/tr}</th>
+ <th>{tr}Title{/tr}</th>
+ <th>{tr}Template{/tr}</th>
+ <th>{tr}Multi{/tr}</th>
+ <th>{tr}Role{/tr}</th>
+ <th>{tr}Entries{/tr}</th>
+ <th>{tr}Actions{/tr}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {foreach from=$xref_sources item=src}
+ <tr>
+ <td>{$src.content_type_guid|escape}</td>
+ <td><code>{$src.source|escape}</code></td>
+ <td><code>{$src.xref_type|escape}</code></td>
+ <td>{$src.cross_ref_title|escape}</td>
+ <td>{$src.template|escape}</td>
+ <td>{$src.multi}</td>
+ <td>{$src.role_id}</td>
+ <td>{$src.num_entries}</td>
+ <td>
+ {if $src.num_entries eq 0}
+ <a href="{$smarty.const.LIBERTY_PKG_URL}admin/admin_xref_sources.php?fDeleteSource=1&amp;source={$src.source|escape}&amp;del_content_type_guid={$src.content_type_guid|escape}&amp;content_type_guid={$activeGuid|escape}"
+ onclick="return confirm('{tr}Delete this source?{/tr}')">{booticon iname="icon-trash" ipackage="icons" iforce=icon_text iexplain="Delete"}</a>
+ {/if}
+ </td>
+ </tr>
+ {foreachelse}
+ <tr class="norecords"><td colspan="9">{tr}No sources found{/tr}</td></tr>
+ {/foreach}
+ </tbody>
+ </table>
+
+ </div><!-- end .body -->
+</div><!-- end .admin -->
+{/strip}
diff --git a/templates/menu_liberty_admin.tpl b/templates/menu_liberty_admin.tpl
index 57a29dd..8dd5e64 100755
--- a/templates/menu_liberty_admin.tpl
+++ b/templates/menu_liberty_admin.tpl
@@ -5,6 +5,8 @@
<li><a class="item" href="{$smarty.const.LIBERTY_PKG_URL}admin/plugins.php">{tr}Plugins{/tr}</a></li>
<li><a class="item" href="{$smarty.const.LIBERTY_PKG_URL}admin/action_logs.php">{tr}Action Logs{/tr}</a></li>
<li><a class="item" href="{$smarty.const.LIBERTY_PKG_URL}admin/comments.php">{tr}Comments{/tr}</a></li>
+ <li><a class="item" href="{$smarty.const.LIBERTY_PKG_URL}admin/admin_xref_groups.php">{tr}Xref Groups{/tr}</a></li>
+ <li><a class="item" href="{$smarty.const.LIBERTY_PKG_URL}admin/admin_xref_sources.php">{tr}Xref Sources{/tr}</a></li>
{if $gBitSystem->isPackageActive( 'pdf' ) }
<li><a class="item" href="{$smarty.const.KERNEL_PKG_URL}admin/index.php?page=pdf">{tr}PDF{/tr}</a></li>
{/if}