PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / admin / actions / BulkActionHandler.php

BulkActionHandler.php in 404 Solution trunk, at includes/admin/actions/BulkActionHandler.php

162 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Handles any action whose verb starts with 'bulk' (bulktrash,
9 * bulk_trash_delete_permanently, bulk_trash_restore, bulkignore, bulklater,
10 * bulkcaptured). Validates that $_POST['idnum'] is present (echoes an error
11 * and returns '' if not, matching pre-refactor behavior) and that it does not
12 * exceed MAX_BULK_ACTION_IDS (echoes an error and returns '' without running
13 * any mutation if it does), then iterates ids and applies the per-row
14 * status/trash/delete operation.
15 *
16 * Registered in the dispatcher's prefix-match map (not the exact-match map).
17 *
18 * The per-row dispatch logic previously lived on
19 * PluginLogicAdminActions::doBulkAction() (M201, design-audit-2026-06-02).
20 * That parent method is now a thin shim that calls doBulkAction() here so
21 * existing tests/callers do not break.
22 */
23 class ABJ_404_Solution_BulkActionHandler implements ABJ_404_Solution_AdminActionHandlerInterface {
24
25 /**
26 * Maximum number of ids accepted in a single bulk action POST. The
27 * admin redirects/captured tables only ever render bulk-select
28 * checkboxes for rows on the CURRENT page (see
29 * includes/html/tableRowPageRedirects.html and
30 * tableRowCapturedURLs.html) -- there is no "select all across pages"
31 * feature -- so the largest legitimate submission is bounded by the
32 * table's own page-size cap (ABJ404_OPTION_MAX_PERPAGE = 500, defined
33 * in includes/Loader.php). This constant sits well above that (2x
34 * headroom) so a legitimate max-page-size selection always passes,
35 * while a forged or accidental oversized POST (e.g. 100,000 ids)
36 * cannot monopolize PHP execution time or hammer the database with
37 * that many mutation queries in a single request.
38 */
39 const MAX_BULK_ACTION_IDS = 1000;
40
41 /** @var ABJ_404_Solution_PluginLogicAdminActions */
42 private $parent;
43
44 public function __construct(ABJ_404_Solution_PluginLogicAdminActions $parent) {
45 $this->parent = $parent;
46 }
47
48 public function nonceAction(): string {
49 return 'abj404_bulkProcess';
50 }
51
52 public function nonceArg(): string {
53 return '_wpnonce';
54 }
55
56 public function useCheckAdminReferer(): bool {
57 return true;
58 }
59
60 public function handle(string $action, string &$sub): string {
61 if (!isset($_POST['idnum']) || !is_array($_POST['idnum'])) {
62 $this->parent->getLogger()->debugMessage(
63 "No ID(s) specified for bulk action: " . esc_html($action));
64 echo sprintf(__("Error: No ID(s) specified for bulk action. (%s)", '404-solution'),
65 esc_html($action));
66 return '';
67 }
68 if (count($_POST['idnum']) > self::MAX_BULK_ACTION_IDS) {
69 $this->parent->getLogger()->debugMessage(
70 "Too many ID(s) specified for bulk action (" . count($_POST['idnum']) .
71 ", max " . self::MAX_BULK_ACTION_IDS . "): " . esc_html($action));
72 echo sprintf(
73 __("Error: Too many ID(s) specified for bulk action (maximum %d). (%s)", '404-solution'),
74 self::MAX_BULK_ACTION_IDS, esc_html($action));
75 return '';
76 }
77 $ids = array();
78 foreach ($_POST['idnum'] as $rawId) {
79 if (is_scalar($rawId)) {
80 $ids[] = absint($rawId);
81 }
82 }
83 $message = $this->doBulkAction($action, $ids);
84 return $message;
85 }
86
87 /**
88 * Iterate the given ids and apply the per-row operation implied by the
89 * bulk action verb. Returns a human-readable summary. Public so the
90 * legacy PluginLogicAdminActions::doBulkAction() shim and existing tests
91 * can call it directly without going through handle()'s POST gate.
92 *
93 * @param string $action
94 * @param array<int, int> $ids
95 * @return string
96 */
97 public function doBulkAction(string $action, array $ids): string {
98 $message = "";
99 $logger = $this->parent->getLogger();
100 $redirectsRepo = $this->parent->getRedirectsRepo();
101
102 $logger->debugMessage("In doBulkAction. Action: " .
103 esc_html($action == '' ? '(none)' : $action) . ", ids: " . wp_kses_post((string)json_encode($ids)));
104
105 if ($action == "bulkignore" || $action == "bulkcaptured" || $action == "bulklater" ||
106 $action == "bulk_trash_restore") {
107
108 $status = 0;
109 if ($action == "bulkignore") {
110 $status = ABJ404_STATUS_IGNORED;
111 } else if ($action == "bulkcaptured") {
112 $status = ABJ404_STATUS_CAPTURED;
113 } else if ($action == "bulklater") {
114 $status = ABJ404_STATUS_LATER;
115 }
116
117 $count = 0;
118 foreach ($ids as $id) {
119 $s = $redirectsRepo->moveRedirectsToTrash($id, 0);
120 if ($action != "bulk_trash_restore") {
121 $s = $redirectsRepo->updateRedirectTypeStatus($id, (string)$status);
122 }
123 if ($s == "") {
124 $count++;
125 }
126 }
127 if ($action == "bulkignore") {
128 $message = $count . " " . __('URL(s) marked as Ignored.', '404-solution');
129 } else if ($action == "bulkcaptured") {
130 $message = $count . " " . __('URL(s) marked as Captured.', '404-solution');
131 } else if ($action == "bulklater") {
132 $message = $count . " " . __('URL(s) marked as Later.', '404-solution');
133 } else {
134 $message = $count . " " . __('URL(s) restored.', '404-solution');
135 }
136
137 } else if ($action == "bulk_trash_delete_permanently") {
138 $count = 0;
139 foreach ($ids as $id) {
140 $redirectsRepo->deleteRedirect(absint($id));
141 $count ++;
142 }
143 $message = $count . " " . __('URL(s) deleted', '404-solution');
144
145 } else if ($action == "bulktrash") {
146 $count = 0;
147 foreach ($ids as $id) {
148 $s = $redirectsRepo->moveRedirectsToTrash($id, 1);
149 if ($s == "") {
150 $count ++;
151 }
152 }
153 $message = $count . " " . __('URL(s) moved to trash', '404-solution');
154
155 } else {
156 $logger->errorMessage("Unrecognized bulk action: " . esc_html($action));
157 echo sprintf(__("Error: Unrecognized bulk action. (%s)", '404-solution'), esc_html($action));
158 }
159 return $message;
160 }
161 }
162