@azpro @beerbee @Kofod95
@ArtcoInc @14Steve14 and anyone interested in enabling/disabling Hooks.
what you think
- One core-file override (hooks.php),
- sql (Adds `hooks_status`)
- 2 -3 new files
Enabling / Disabling Hooks
-
Omar_one
- Senior Contributor
- Posts: 676
- Joined: Fri Oct 25, 2019 5:06 pm
- Phoenix Version: v1.0.8.16
- Has thanked: 100 times
- Been thanked: 56 times
Re: No Core Changes - A different perspective
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.
-
14Steve14
- Senior Contributor
- Posts: 921
- Joined: Fri Oct 25, 2019 7:01 pm
- Phoenix Version: v1.0.9.1
- Has thanked: 17 times
- Been thanked: 103 times
-
Omar_one
- Senior Contributor
- Posts: 676
- Joined: Fri Oct 25, 2019 5:06 pm
- Phoenix Version: v1.0.8.16
- Has thanked: 100 times
- Been thanked: 56 times
Re: No Core Changes - A different perspective
@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.
Please don't test this on the live shop, as it's still under development and testing
@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);
You do not have the required permissions to view the files attached to this post.
- burt
- Core Team
- Posts: 4550
- Joined: Tue Oct 29, 2019 9:37 am
- Phoenix Version: v1.1.0.8
- : Buy Me A Beverage
- Has thanked: 252 times
- Been thanked: 412 times
-
Omar_one
- Senior Contributor
- Posts: 676
- Joined: Fri Oct 25, 2019 5:06 pm
- Phoenix Version: v1.0.8.16
- Has thanked: 100 times
- Been thanked: 56 times
Re: Enabling / Disabling Hooks
@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. Alternatively, you can manually replace the default.php file located in:
[YOUR ADMIN]/includes/actions/modules_hooks_manager/views/
@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. 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>You do not have the required permissions to view the files attached to this post.