Page 1 of 1
Enabling / Disabling Hooks
Posted: Thu Jul 09, 2026 2:25 pm
by Omar_one
@azpro @beerbee @Kofod95
@ArtcoInc @14Steve14 and anyone interested in enabling/disabling Hooks.
what you think
hooks.gif
- One core-file override (hooks.php),
- sql (Adds `hooks_status`)
- 2 -3 new files
Re: No Core Changes - A different perspective
Posted: Thu Jul 09, 2026 2:39 pm
by bonbec
@Omar_one
Wow! That aligns with my view on this subject—bravo!
Sincerely, bravo!
Re: No Core Changes - A different perspective
Posted: Thu Jul 09, 2026 3:05 pm
by Omar_one
I plan to share the files today or tomorrow. If anyone would like to test them and help me get everything properly finalized, please let me know.
Re: No Core Changes - A different perspective
Posted: Fri Jul 10, 2026 7:01 am
by 14Steve14
Omar_one wrote: ↑Thu Jul 09, 2026 2:25 pm
@burt @azpro @beerbee @Kofod95
@ArtcoInc @14Steve14 and anyone interested in enabling/disabling Hooks.
what you think
hooks.gif
- One core-file override (hooks.php),
- sql (Adds `hooks_status`)
- 2 -3 new files
Looks an interesting feature
Re: No Core Changes - A different perspective
Posted: Fri Jul 10, 2026 8:06 am
by azpro
@Omar_one
Great stuff - Exactly what I had in mind.
Thank you for sharing!
Re: No Core Changes - A different perspective
Posted: Fri Jul 10, 2026 8:48 pm
by Omar_one
@azpro @beerbee @Kofod95
@ArtcoInc @14Steve14
- I added another filter for hook locations (Shop and Admin). The page will display hooks that are defined in override template also.
Code: Select all
ALTER TABLE hooks
ADD COLUMN hooks_status ENUM('1','0') NOT NULL DEFAULT '1' AFTER hooks_method;
ALTER TABLE hooks
ADD UNIQUE KEY hooks_unique (hooks_site, hooks_group, hooks_action, hooks_code);
Hooks_Manager.zip
Please don't test this on the live shop, as it's still under development and testing
Re: Enabling / Disabling Hooks
Posted: Sat Jul 11, 2026 2:14 pm
by burt
Split from the thread No Core Changes - A different perspective
Re: Enabling / Disabling Hooks
Posted: Sat Jul 11, 2026 6:56 pm
by Omar_one
@azpro @beerbee @Kofod95
@ArtcoInc @14Steve14 and anyone interested in enabling/disabling Hooks.
Disabling or enabling a hook file does not automatically toggle all of its individual methods and actions.
The ZIP folder has been updated with the fix.
Hooks_Manager_v1.1.zip
Alternatively, you can manually replace the default.php file located in:
[YOUR ADMIN]/includes/actions/modules_hooks_manager/views/
Code: Select all
<?php
/*
$Id$
CE Phoenix, E-Commerce made Easy
https://phoenixcart.org
Copyright (c) 2024 Phoenix Cart
Released under the GNU General Public License
*/
// 1. Process Toggle Logic First
global $db, $Admin;
$site = $_GET['site'] ?? '';
$group = $_GET['group'] ?? '';
$code = $_GET['code'] ?? '';
$action_name = $_GET['action_name'] ?? '';
if ($site !== '' && $group !== '' && $code !== '') {
// Get the current status from any method of this hook
$check = $db->query(sprintf(
"SELECT hooks_status
FROM hooks
WHERE hooks_site = '%s'
AND hooks_group = '%s'
AND hooks_code = '%s'
LIMIT 1",
$db->escape($site),
$db->escape($group),
$db->escape($code)
));
$current = '1';
if ($hook = $check->fetch_assoc()) {
$current = $hook['hooks_status'];
}
$new_status = ($current === '1') ? '0' : '1';
// Update ALL methods belonging to this hook
$db->query(sprintf(
"UPDATE hooks
SET hooks_status = '%s'
WHERE hooks_site = '%s'
AND hooks_group = '%s'
AND hooks_code = '%s'",
$db->escape($new_status),
$db->escape($site),
$db->escape($group),
$db->escape($code)
));
// If it's an AJAX request, respond immediately and exit
if (($_GET['action_type'] ?? '') === 'ajax') {
exit('success');
}
// For non-AJAX fallback clicks, redirect back cleanly to prevent double-submits
$redirect_params = [];
if (!empty($_GET['hook_location'])) {
$redirect_params[] = 'hook_location=' . urlencode($_GET['hook_location']);
}
if (!empty($_GET['hook_group'])) {
$redirect_params[] = 'hook_group=' . urlencode($_GET['hook_group']);
}
header('Location: ' . $Admin->link('modules_hooks_manager', implode('&', $redirect_params)));
exit;
}
// 2. Original Page Rendering & Filter Setup
$hook_locations = [];
$hook_groups = [];
foreach ($contents as $site_key => $groups) {
$hook_locations[$site_key] = $site_key;
foreach ($groups as $group_key => $actions) {
$hook_groups[$group_key] = $group_key;
}
}
ksort($hook_locations);
ksort($hook_groups);
$selected_location = trim($_GET['hook_location'] ?? '');
$selected_group = trim($_GET['hook_group'] ?? '');
// Build hook status lookup by site/group/code
$hook_statuses = [];
foreach ($statuses as $key => $status) {
[$s, $g, $a, $c] = explode("\0", $key);
$hook_statuses["{$s}\0{$g}\0{$c}"] = $status;
}
// 3. Collect and Sort Row Data by File Name
$table_rows = [];
foreach ($contents as $site => $groups) {
if ($selected_location !== '' && strtolower(trim($site)) !== strtolower(trim($selected_location))) {
continue;
}
foreach ($groups as $group => $actions) {
if ($selected_group !== '' && strtolower(trim($group)) !== strtolower(trim($selected_group))) {
continue;
}
foreach ($actions as $action => $codes) {
foreach ($codes as $code => $locations) {
foreach ($locations as $location) {
if (is_array($location)) {
$file = implode('->', $location);
$class = !empty($location[0]) ? explode('::', $location[0])[0] : null;
$version = ($class && class_exists($class))
? (get_class_vars($class)['version'] ?? null)
: null;
} else {
$file = "$code.php";
$version = get_class_vars("hook_{$site}_{$group}_{$code}")['version'] ?? null;
}
// Use one status for the entire hook file
$hook_key = "{$site}\0{$group}\0{$code}";
$enabled = ($hook_statuses[$hook_key] ?? '1') === '1';
$current_script = basename($_SERVER['PHP_SELF']);
$query_string = 'app=hooks'
. '&site=' . urlencode($site)
. '&group=' . urlencode($group)
. '&action_name=' . urlencode($action)
. '&code=' . urlencode($code);
if (!empty($selected_location)) {
$query_string .= '&hook_location=' . urlencode($selected_location);
}
if (!empty($selected_group)) {
$query_string .= '&hook_group=' . urlencode($selected_group);
}
$toggle_href = $current_script . '?' . $query_string;
$table_rows[] = [
'site' => $site,
'group' => $group,
'file' => $file,
'action' => $action,
'version' => $version,
'enabled' => $enabled,
'toggle_href' => $toggle_href
];
}
}
}
}
}
// Perform alphabetical sort on the computed filename string
usort($table_rows, function ($a, $b) {
return strcasecmp($a['file'], $b['file']);
});
?>
<form method="get" class="mb-3">
<?php
foreach ($_GET as $k => $v) {
if (!in_array($k, ['hook_location', 'hook_group', 'site', 'group', 'action_name', 'code', 'action_type'])) {
?>
<input type="hidden" name="<?= htmlspecialchars($k) ?>" value="<?= htmlspecialchars($v) ?>">
<?php
}
}
?>
<div class="row mb-3">
<div class="col-md-4 mb-2 mb-md-0">
<select name="hook_location" class="form-select" onchange="this.form.submit()">
<option value="">All Locations</option>
<?php foreach ($hook_locations as $location) { ?>
<option value="<?= htmlspecialchars($location) ?>" <?= ($selected_location === $location ? 'selected' : '') ?>>
<?= htmlspecialchars($location) ?>
</option>
<?php } ?>
</select>
</div>
<div class="col-md-4">
<select name="hook_group" class="form-select" onchange="this.form.submit()">
<option value="">All Hook Groups</option>
<?php foreach ($hook_groups as $group_item) { ?>
<option value="<?= htmlspecialchars($group_item) ?>" <?= ($selected_group === $group_item ? 'selected' : '') ?>>
<?= htmlspecialchars($group_item) ?>
</option>
<?php } ?>
</select>
</div>
</div>
</form>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th colspan="5"><?= defined('TABLE_HEADING_HOOKS') ? TABLE_HEADING_HOOKS : 'Registered Hooks' ?></th>
</tr>
</thead>
<thead class="table-light">
<tr>
<th><?= TABLE_HEADING_GROUP ?></th>
<th><?= TABLE_HEADING_FILE ?></th>
<th><?= TABLE_HEADING_METHOD ?></th>
<th class="text-end"><?= TABLE_HEADING_VERSION ?></th>
<th class="text-end"><?= TABLE_HEADING_STATUS ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($table_rows as $row) { ?>
<tr
class="<?= $row['enabled'] ? '' : 'text-muted' ?>"
data-hook-site="<?= htmlspecialchars($row['site']) ?>"
data-hook-group="<?= htmlspecialchars($row['group']) ?>"
data-hook-file="<?= htmlspecialchars($row['file']) ?>"
>
<td><?= htmlspecialchars($row['group']) ?></td>
<td><?= htmlspecialchars($row['file']) ?></td>
<td><?= htmlspecialchars($row['action']) ?></td>
<td class="text-end"><?= htmlspecialchars($row['version'] ?? 'N/A') ?></td>
<td class="text-end">
<a href="<?= $row['toggle_href'] ?>"
class="js-hook-toggle"
title="<?= $row['enabled'] ? TEXT_CLICK_TO_DISABLE : TEXT_CLICK_TO_ENABLE ?>">
<i class="fa fa-toggle-<?= $row['enabled'] ? 'on text-success' : 'off text-muted' ?> fa-lg"></i>
</a>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
<hr>
<p><?= TEXT_HOOKS_DIRECTORY . ' ' . DIR_FS_CATALOG . 'includes/hooks/' ?></p>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.js-hook-toggle').forEach(function (link) {
link.addEventListener('click', function (e) {
e.preventDefault();
const clickedRow = this.closest('tr');
const site = clickedRow.dataset.hookSite;
const group = clickedRow.dataset.hookGroup;
const file = clickedRow.dataset.hookFile;
// All rows belonging to the same hook file
const rows = document.querySelectorAll(
`tr[data-hook-site="${site}"][data-hook-group="${group}"][data-hook-file="${file}"]`
);
const turningOff =
this.querySelector('i').classList.contains('fa-toggle-on');
// Update all matching rows immediately
rows.forEach(function (row) {
const icon = row.querySelector('.js-hook-toggle i');
const link = row.querySelector('.js-hook-toggle');
if (turningOff) {
icon.classList.remove('fa-toggle-on', 'text-success');
icon.classList.add('fa-toggle-off', 'text-muted');
row.classList.add('text-muted');
link.title = '<?= addslashes(TEXT_CLICK_TO_ENABLE) ?>';
} else {
icon.classList.remove('fa-toggle-off', 'text-muted');
icon.classList.add('fa-toggle-on', 'text-success');
row.classList.remove('text-muted');
link.title = '<?= addslashes(TEXT_CLICK_TO_DISABLE) ?>';
}
});
const ajaxUrl = this.href + '&action_type=ajax';
fetch(ajaxUrl)
.then(function (response) {
if (!response.ok) {
throw new Error('AJAX request failed');
}
})
.catch(function (error) {
console.error(error);
// Roll back all rows if the request fails
rows.forEach(function (row) {
const icon = row.querySelector('.js-hook-toggle i');
const link = row.querySelector('.js-hook-toggle');
if (turningOff) {
icon.classList.remove('fa-toggle-off', 'text-muted');
icon.classList.add('fa-toggle-on', 'text-success');
row.classList.remove('text-muted');
link.title = '<?= addslashes(TEXT_CLICK_TO_DISABLE) ?>';
} else {
icon.classList.remove('fa-toggle-on', 'text-success');
icon.classList.add('fa-toggle-off', 'text-muted');
row.classList.add('text-muted');
link.title = '<?= addslashes(TEXT_CLICK_TO_ENABLE) ?>';
}
});
});
});
});
});
</script>