diff --git a/inc/spbc-admin.php b/inc/spbc-admin.php
index 70a91b9b7..a4f6fe83b 100644
--- a/inc/spbc-admin.php
+++ b/inc/spbc-admin.php
@@ -118,6 +118,7 @@ function spbc_admin_init()
// Settings
add_action('wp_ajax_spbc_settings__draw_elements', 'spbc_settings__draw_elements');
add_action('wp_ajax_spbc_scanner_tab__reload_accordion', 'spbc_field_scanner__show_accordion');
+ add_action('wp_ajax_spbc_scanner__get_accordions', 'spbc_scanner__get_accordions');
// Tabs requests handlers (React)
add_action('wp_ajax_spbct_get_tab_data', 'spbct_get_tab_data');
diff --git a/inc/spbc-settings.php b/inc/spbc-settings.php
index 62e166fda..41ec052a3 100644
--- a/inc/spbc-settings.php
+++ b/inc/spbc-settings.php
@@ -5,6 +5,7 @@
use CleantalkSP\Security\LoginCollectingProtector;
use CleantalkSP\SpbctWP\API;
use CleantalkSP\SpbctWP\Cron as SpbcCron;
+use CleantalkSP\SpbctWP\DTO\ReactDataDTO;
use CleantalkSP\SpbctWP\Escape;
use CleantalkSP\SpbctWP\Helpers\Arr;
use CleantalkSP\SpbctWP\Helpers\IP;
@@ -1715,12 +1716,6 @@ function spbc_field_scanner()
spbc_scanner__healLastScan();
}
- echo '
';
- if ( function_exists('spbc_scanner__hasCompletedScan') ? spbc_scanner__hasCompletedScan() : ! empty($spbc->data['scanner']['last_scan']) ) {
- spbc_field_scanner__show_accordion(true);
- }
- echo '
';
-
echo ' ';
echo spbc_field_scanner__log();
@@ -1816,12 +1811,8 @@ function spbc__analysyis_files_stats__get_data()
return $out;
}
-function spbc_field_scanner__show_accordion($direct_call = false)
+function spbc_scanner__accordion_catalog()
{
- if ( ! $direct_call) {
- spbc_check_ajax_referer('spbc_secret_nonce', 'security');
- }
-
global $spbc;
//analysis log description
@@ -1977,6 +1968,17 @@ function spbc_field_scanner__show_accordion($direct_call = false)
),
);
+ return array($tables_files, $accordions_order);
+}
+
+function spbc_field_scanner__show_accordion($direct_call = false)
+{
+ if ( ! $direct_call) {
+ spbc_check_ajax_referer('spbc_secret_nonce', 'security');
+ }
+
+ list($tables_files, $accordions_order) = spbc_scanner__accordion_catalog();
+
foreach ($accordions_order as $_category => $data) {
if ( isset($data['display']) && ! $data['display'] ) {
continue;
@@ -2045,6 +2047,191 @@ function spbc_field_scanner__show_accordion($direct_call = false)
}
}
+/**
+ * Structured scan-result accordions for the React scanner tab.
+ * Same categories, tables and row actions as spbc_field_scanner__show_accordion().
+ *
+ * @param array $states Per-type view state: page, order_by, order, view_type, domain.
+ * @param string $open_panel Panel id whose rows should be built. Other panels return a count only.
+ * @return array
+ */
+function spbc_scanner__accordion_payload($states = array(), $open_panel = '')
+{
+ global $spbc;
+
+ $feature_state = $spbc->feature_restrictions->getState($spbc, 'scanner');
+ if (false === $feature_state->is_active) {
+ return spbc_scanner__accordion_react_data(true, $feature_state->sanitizedReasonOutput(), array());
+ }
+
+ if ( function_exists('spbc_scanner__healLastScan') ) {
+ spbc_scanner__healLastScan();
+ }
+ $scan_in_progress = function_exists('spbc_scanner__isScannerIsInProgress') && spbc_scanner__isScannerIsInProgress();
+ if (
+ function_exists('spbc_scanner__hasCompletedScan') &&
+ ! spbc_scanner__hasCompletedScan() &&
+ ! $scan_in_progress
+ ) {
+ return spbc_scanner__accordion_react_data(false, '', array());
+ }
+
+ list($tables_files, $accordions_order) = spbc_scanner__accordion_catalog();
+ $categories = array();
+
+ foreach ($accordions_order as $category_id => $data) {
+ if ( isset($data['display']) && ! $data['display'] ) {
+ continue;
+ }
+
+ $panels = array();
+ foreach ($data['types'] as $type_name) {
+ if ( ! isset($tables_files[$type_name]) ) {
+ continue;
+ }
+
+ if ( $type_name !== 'frontend_malware' && $type_name !== 'frontend_scan_results_approved' ) {
+ if ( empty(ScanResultsTableActions::getFilesByCategory($type_name, true)) ) {
+ continue;
+ }
+ }
+
+ $state = (isset($states[$type_name]) && is_array($states[$type_name])) ? $states[$type_name] : array();
+ $view_type = $type_name;
+ if (
+ $type_name === 'outbound_links' &&
+ isset($state['view_type']) &&
+ $state['view_type'] === 'links'
+ ) {
+ $view_type = 'links';
+ }
+
+ $args = spbc_scanner__table_args_for_state($view_type, $state);
+ $args['id'] = 'spbc_tbl__scanner_' . $view_type;
+ $args['type'] = $view_type;
+
+ $table = new ListTable($args);
+ $build_rows = ($open_panel !== '' && $open_panel === $type_name);
+ $table->getData($build_rows);
+
+ $if_empty = array_key_exists('if_empty_items', $args) ? $args['if_empty_items'] : null;
+ if ( $if_empty === false && (int) $table->items_total === 0 && $view_type === $type_name ) {
+ continue;
+ }
+
+ $danger = (
+ ($type_name === 'critical' && ! empty($spbc->data['display_scanner_warnings']['critical']))
+ || ($type_name === 'frontend_malware' && ! empty($spbc->data['display_scanner_warnings']['frontend']))
+ || ($type_name === 'analysis_log' && ! empty($spbc->data['display_scanner_warnings']['analysis']))
+ || ($type_name === 'oscron' && ! empty($spbc->data['display_scanner_warnings']['oscron']))
+ || ($type_name === 'db_trigger' && ! empty($spbc->data['display_scanner_warnings']['db_triggers']))
+ );
+
+ $exported = $table->exportForReact();
+ if ( ! $build_rows ) {
+ $exported['summary'] = true;
+ $exported['rows'] = array();
+ $exported['empty_html'] = '';
+ }
+ $exported['panel_id'] = $type_name;
+ $exported['title'] = ucwords(str_replace('_', ' ', $type_name));
+ $exported['description_html'] = $tables_files[$type_name];
+ $exported['danger'] = $danger;
+ $exported['count'] = (int) $table->items_total;
+ $panels[] = $exported;
+ }
+
+ if ( $panels ) {
+ $categories[] = array(
+ 'id' => $category_id,
+ 'title' => $data['category_description'],
+ 'panels' => $panels,
+ );
+ }
+ }
+
+ return spbc_scanner__accordion_react_data(false, '', $categories);
+}
+
+/**
+ * Pack scan-result accordions into the React tab DTO.
+ *
+ * @param bool $restricted Scanner feature is inactive.
+ * @param string $reason_html Restriction notice HTML.
+ * @param array $categories Accordion categories.
+ * @return array
+ */
+function spbc_scanner__accordion_react_data($restricted, $reason_html, $categories)
+{
+ $data = ReactDataDTO::getDefaultArray();
+ $data['flags']['restricted'] = (bool) $restricted;
+ $data['strings']['reason_html'] = (string) $reason_html;
+ $data['objects']['categories'] = $categories;
+
+ $dto = new ReactDataDTO($data);
+
+ return $dto->getArray();
+}
+
+/**
+ * Apply pagination, sort and domain filter requested by the React table.
+ *
+ * @param string $type
+ * @param array $state
+ * @return array
+ */
+function spbc_scanner__table_args_for_state($type, $state)
+{
+ if ( $type === 'links' && ! empty($state['domain']) ) {
+ $_POST['domain'] = sanitize_text_field($state['domain']);
+ }
+
+ $args = spbc_list_table__get_args_by_type($type);
+
+ if ( ! isset($args['pagination']) || ! is_array($args['pagination']) ) {
+ $args['pagination'] = array('page' => 1, 'per_page' => SPBC_LAST_ACTIONS_TO_VIEW);
+ }
+
+ $page = 1;
+ if ( isset($state['page']) && $state['page'] !== '' && $state['page'] !== null ) {
+ $page = max(1, (int) $state['page']);
+ }
+ $args['pagination']['page'] = $page;
+ // Cure log reads $_POST['page']. Set it for this table, including page 1,
+ // so a previous panel's page cannot leak into the next callback.
+ $_POST['page'] = $page;
+
+ if ( ! empty($state['order_by']) ) {
+ $order_by = (string) $state['order_by'];
+ $order = (isset($state['order']) && strtolower((string) $state['order']) === 'desc') ? 'desc' : 'asc';
+ $allowed = ! empty($args['sortable']) ? $args['sortable'] : array();
+ if ( preg_match('/^[a-z0-9_]+$/i', $order_by) === 1 && in_array($order_by, $allowed, true) ) {
+ /** @psalm-var non-empty-string $order_by */
+ $args['order_by'] = array($order_by => $order);
+ }
+ }
+
+ return $args;
+}
+
+/**
+ * AJAX: scan result accordions for React.
+ */
+function spbc_scanner__get_accordions()
+{
+ spbc_check_ajax_referer('spbc_secret_nonce', 'security');
+
+ $raw = isset($_POST['tables']) ? wp_unslash($_POST['tables']) : '';
+ $states = is_string($raw) ? json_decode($raw, true) : array();
+ if ( ! is_array($states) ) {
+ $states = array();
+ }
+
+ $open_panel = isset($_POST['open_panel']) ? sanitize_key(wp_unslash($_POST['open_panel'])) : '';
+
+ wp_send_json_success(spbc_scanner__accordion_payload($states, $open_panel));
+}
+
/**
* Return arguments for ListTable::__constructor()
*
@@ -2400,16 +2587,16 @@ function spbc_list_table__get_args_by_type($table_type)
. '',
'columns' => array(
'cb' => array('heading' => ' ', 'class' => 'check-column', 'width_percent' => 2),
- 'about_trigger' => array( 'heading' => 'About trigger', 'width_percent' => 30 ), // name, table, time, action
+ 'about_trigger' => array( 'heading' => 'About trigger', 'primary' => true, 'width_percent' => 30 ), // name, table, time, action
'code' => array( 'heading' => 'Code', 'width_percent' => 43 ),
'signature' => array( 'heading' => 'Signature', 'width_percent' => 10 ),
'analysis_status' => array( 'heading' => 'Verdict', 'width_percent' => 15 ),
),
'actions' => array(
- 'delete' => array('name' => 'Delete',),
+ 'delete-trigger' => array('name' => __('Delete', 'security-malware-firewall'),),
),
'bulk_actions' => array(
- 'delete' => array('name' => 'Delete',),
+ 'delete-trigger' => array('name' => __('Delete', 'security-malware-firewall'),),
),
);
break;
diff --git a/js/public/spbct-react-bundle.js b/js/public/spbct-react-bundle.js
index eac4c31aa..440d62668 100644
--- a/js/public/spbct-react-bundle.js
+++ b/js/public/spbct-react-bundle.js
@@ -490,6 +490,16 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
/***/ },
+/***/ "./js/src/react/components/TabContent/TabScanner/ScannerAccordions.js"
+/*!****************************************************************************!*\
+ !*** ./js/src/react/components/TabContent/TabScanner/ScannerAccordions.js ***!
+ \****************************************************************************/
+(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
+
+eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ ScannerAccordions)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../modules/Http/Ajax */ \"./js/src/react/modules/Http/Ajax.js\");\n/* harmony import */ var _Preloader_Preloader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../Preloader/Preloader */ \"./js/src/react/components/Preloader/Preloader.js\");\n/* harmony import */ var _ScannerTable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./ScannerTable */ \"./js/src/react/components/TabContent/TabScanner/ScannerTable.js\");\n/* harmony import */ var _scannerActions__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./scannerActions */ \"./js/src/react/components/TabContent/TabScanner/scannerActions.js\");\nfunction _typeof(o) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && \"function\" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? \"symbol\" : typeof o; }, _typeof(o); }\nfunction _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }\nfunction _toPropertyKey(t) { var i = _toPrimitive(t, \"string\"); return \"symbol\" == _typeof(i) ? i : i + \"\"; }\nfunction _toPrimitive(t, r) { if (\"object\" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || \"default\"); if (\"object\" != _typeof(i)) return i; throw new TypeError(\"@@toPrimitive must return a primitive value.\"); } return (\"string\" === r ? String : Number)(t); }\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2)); }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\nfunction _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }\nfunction _nonIterableRest() { throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\"); }\nfunction _unsupportedIterableToArray(r, a) { if (r) { if (\"string\" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return \"Object\" === t && r.constructor && (t = r.constructor.name), \"Map\" === t || \"Set\" === t ? Array.from(r) : \"Arguments\" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }\nfunction _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }\nfunction _iterableToArrayLimit(r, l) { var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t[\"return\"] && (u = t[\"return\"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }\nfunction _arrayWithHoles(r) { if (Array.isArray(r)) return r; }\n;\n\n\n\n\nfunction ScannerAccordions(_ref) {\n var scanning = _ref.scanning;\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null),\n _useState2 = _slicedToArray(_useState, 2),\n payload = _useState2[0],\n setPayload = _useState2[1];\n var _useState3 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({}),\n _useState4 = _slicedToArray(_useState3, 2),\n tableState = _useState4[0],\n setTableState = _useState4[1];\n var _useState5 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null),\n _useState6 = _slicedToArray(_useState5, 2),\n openPanel = _useState6[0],\n setOpenPanel = _useState6[1];\n var _useState7 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(true),\n _useState8 = _slicedToArray(_useState7, 2),\n loading = _useState8[0],\n setLoading = _useState8[1];\n var _useState9 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({}),\n _useState0 = _slicedToArray(_useState9, 2),\n selected = _useState0[0],\n setSelected = _useState0[1];\n var _useState1 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false),\n _useState10 = _slicedToArray(_useState1, 2),\n busy = _useState10[0],\n setBusy = _useState10[1];\n var _useState11 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(''),\n _useState12 = _slicedToArray(_useState11, 2),\n error = _useState12[0],\n setError = _useState12[1];\n var requestId = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(0);\n var tableStateRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(tableState);\n var openPanelRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(openPanel);\n tableStateRef.current = tableState;\n openPanelRef.current = openPanel;\n var load = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(/*#__PURE__*/function () {\n var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(states, openPanelId) {\n var id, data, _categories, _t;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.p = _context.n) {\n case 0:\n id = ++requestId.current;\n _context.p = 1;\n _context.n = 2;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_1__.sendAjaxRequest)({\n action: 'spbc_scanner__get_accordions',\n tables: JSON.stringify(states || {}),\n open_panel: openPanelId || ''\n }, true);\n case 2:\n data = _context.v;\n if (!(id !== requestId.current)) {\n _context.n = 3;\n break;\n }\n return _context.a(2);\n case 3:\n if (data && data.success && data.data) {\n setPayload(data.data);\n setError('');\n _categories = data.data.objects && data.data.objects.categories || [];\n setTableState(function (prev) {\n var changed = false;\n var next = Object.assign({}, prev);\n _categories.forEach(function (category) {\n (category.panels || []).forEach(function (panel) {\n var serverPage = panel.pagination && panel.pagination.page;\n if (!serverPage) {\n return;\n }\n var localPage = prev[panel.panel_id] && prev[panel.panel_id].page ? Number(prev[panel.panel_id].page) : 1;\n if (localPage === Number(serverPage)) {\n return;\n }\n next[panel.panel_id] = Object.assign({}, prev[panel.panel_id] || {}, {\n page: Number(serverPage)\n });\n changed = true;\n });\n });\n if (changed) {\n tableStateRef.current = next;\n }\n return changed ? next : prev;\n });\n } else {\n setError('Could not load scan results.');\n }\n _context.n = 6;\n break;\n case 4:\n _context.p = 4;\n _t = _context.v;\n if (!(id !== requestId.current)) {\n _context.n = 5;\n break;\n }\n return _context.a(2);\n case 5:\n console.error(_t);\n setError('Could not load scan results.');\n case 6:\n _context.p = 6;\n if (id === requestId.current) {\n setLoading(false);\n }\n return _context.f(6);\n case 7:\n return _context.a(2);\n }\n }, _callee, null, [[1, 4, 6, 7]]);\n }));\n return function (_x, _x2) {\n return _ref2.apply(this, arguments);\n };\n }(), []);\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n load(tableState, openPanel);\n if (!scanning) {\n return undefined;\n }\n var timer = setInterval(function () {\n return load(tableStateRef.current, openPanelRef.current);\n }, 5000);\n return function () {\n return clearInterval(timer);\n };\n }, [scanning, tableState, openPanel, load]);\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n if (scanning || openPanel !== 'analysis_log') {\n return undefined;\n }\n var timer = setInterval(function () {\n return load(tableStateRef.current, openPanelRef.current);\n }, 6000);\n return function () {\n return clearInterval(timer);\n };\n }, [scanning, openPanel, load]);\n var patchState = function patchState(panelId, patch) {\n setTableState(function (prev) {\n return Object.assign({}, prev, _defineProperty({}, panelId, Object.assign({}, prev[panelId] || {}, patch)));\n });\n };\n var clearSelection = function clearSelection(panelId) {\n setSelected(function (prev) {\n return Object.assign({}, prev, _defineProperty({}, panelId, []));\n });\n };\n var _onRowAction = /*#__PURE__*/function () {\n var _ref3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(panelId, actionKey, row, actionDef) {\n var outcome, _t2;\n return _regenerator().w(function (_context2) {\n while (1) switch (_context2.p = _context2.n) {\n case 0:\n if (!busy) {\n _context2.n = 1;\n break;\n }\n return _context2.a(2);\n case 1:\n setBusy(true);\n _context2.p = 2;\n _context2.n = 3;\n return (0,_scannerActions__WEBPACK_IMPORTED_MODULE_4__.runRowAction)(actionKey, row, actionDef);\n case 3:\n outcome = _context2.v;\n if (!(outcome === 'show_links')) {\n _context2.n = 4;\n break;\n }\n patchState(panelId, {\n view_type: 'links',\n domain: row.uid,\n page: 1\n });\n _context2.n = 5;\n break;\n case 4:\n if (!(outcome === 'refresh')) {\n _context2.n = 5;\n break;\n }\n setSelected(function (prev) {\n return Object.assign({}, prev, _defineProperty({}, panelId, []));\n });\n _context2.n = 5;\n return load(tableStateRef.current, openPanelRef.current);\n case 5:\n _context2.n = 7;\n break;\n case 6:\n _context2.p = 6;\n _t2 = _context2.v;\n console.error(_t2);\n case 7:\n _context2.p = 7;\n setBusy(false);\n return _context2.f(7);\n case 8:\n return _context2.a(2);\n }\n }, _callee2, null, [[2, 6, 7, 8]]);\n }));\n return function onRowAction(_x3, _x4, _x5, _x6) {\n return _ref3.apply(this, arguments);\n };\n }();\n var _onBulk = /*#__PURE__*/function () {\n var _ref4 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(panel, action, allItems) {\n var outcome, _t3;\n return _regenerator().w(function (_context3) {\n while (1) switch (_context3.p = _context3.n) {\n case 0:\n if (!busy) {\n _context3.n = 1;\n break;\n }\n return _context3.a(2);\n case 1:\n setBusy(true);\n _context3.p = 2;\n _context3.n = 3;\n return (0,_scannerActions__WEBPACK_IMPORTED_MODULE_4__.runBulkAction)(action, panel, selected[panel.panel_id] || [], allItems);\n case 3:\n outcome = _context3.v;\n if (!(outcome === 'refresh')) {\n _context3.n = 4;\n break;\n }\n setSelected(function (prev) {\n return Object.assign({}, prev, _defineProperty({}, panel.panel_id, []));\n });\n _context3.n = 4;\n return load(tableStateRef.current, openPanelRef.current);\n case 4:\n _context3.n = 6;\n break;\n case 5:\n _context3.p = 5;\n _t3 = _context3.v;\n console.error(_t3);\n case 6:\n _context3.p = 6;\n setBusy(false);\n return _context3.f(6);\n case 7:\n return _context3.a(2);\n }\n }, _callee3, null, [[2, 5, 6, 7]]);\n }));\n return function onBulk(_x7, _x8, _x9) {\n return _ref4.apply(this, arguments);\n };\n }();\n if (loading && !payload) {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Preloader_Preloader__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null);\n }\n if (error && !payload) {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"p\", {\n className: \"spbc_hint\"\n }, error);\n }\n if (!payload) {\n return null;\n }\n var categories = payload.objects && payload.objects.categories || [];\n if (payload.flags && payload.flags.restricted) {\n return null;\n }\n if (!categories.length) {\n return null;\n }\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n id: \"spbc_scan_accordions\"\n }, categories.map(function (category) {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n key: category.id,\n className: \"spbc_accordion_category_wrapper\",\n \"data-refresh-group\": category.id\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"h4\", {\n className: \"spbc_accordion_category_header\"\n }, category.title), category.panels.map(function (panel) {\n var isOpen = openPanel === panel.panel_id;\n var picked = selected[panel.panel_id] || [];\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n key: panel.panel_id,\n \"data-refresh-tab\": panel.panel_id\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"button\", {\n type: \"button\",\n className: 'spbc-react-accordion__header' + (isOpen ? ' is-open' : ''),\n \"aria-expanded\": isOpen,\n onClick: function onClick() {\n return setOpenPanel(isOpen ? null : panel.panel_id);\n }\n }, panel.title, \" (\", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n className: 'spbc_bad_type_count ' + panel.panel_id + '_counter'\n }, panel.count), \")\", panel.danger ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n className: \"red_dot\"\n }) : null), isOpen ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n id: 'spbc_scan_accordion_tab_' + panel.panel_id,\n className: \"spbc-react-accordion__body\"\n }, panel.description_html ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n className: \"spbc_hint\",\n dangerouslySetInnerHTML: {\n __html: panel.description_html\n }\n }) : null, panel.summary ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Preloader_Preloader__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ScannerTable__WEBPACK_IMPORTED_MODULE_3__[\"default\"], {\n table: panel,\n selected: picked,\n busy: busy,\n onToggle: function onToggle(id) {\n setSelected(function (prev) {\n var current = prev[panel.panel_id] || [];\n var next = current.indexOf(id) === -1 ? current.concat(id) : current.filter(function (item) {\n return item !== id;\n });\n return Object.assign({}, prev, _defineProperty({}, panel.panel_id, next));\n });\n },\n onToggleAll: function onToggleAll(on) {\n setSelected(function (prev) {\n return Object.assign({}, prev, _defineProperty({}, panel.panel_id, on ? (panel.rows || []).map(function (row) {\n return row.cb;\n }) : []));\n });\n },\n onPage: function onPage(page) {\n clearSelection(panel.panel_id);\n patchState(panel.panel_id, {\n page: page\n });\n },\n onSort: function onSort(column) {\n var same = panel.order_by === column;\n var order = same && panel.order !== 'desc' ? 'desc' : 'asc';\n clearSelection(panel.panel_id);\n patchState(panel.panel_id, {\n order_by: column,\n order: order,\n page: 1\n });\n },\n onRowAction: function onRowAction(actionKey, row) {\n return _onRowAction(panel.panel_id, actionKey, row, panel.actions[actionKey]);\n },\n onBulk: function onBulk(action, allItems) {\n return _onBulk(panel, action, allItems);\n },\n onBack: function onBack() {\n return patchState(panel.panel_id, {\n view_type: '',\n domain: '',\n page: 1\n });\n }\n })) : null);\n }));\n }));\n}\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabScanner/ScannerAccordions.js?\n}");
+
+/***/ },
+
/***/ "./js/src/react/components/TabContent/TabScanner/ScannerInfo/ScanButton.js"
/*!*********************************************************************************!*\
!*** ./js/src/react/components/TabContent/TabScanner/ScannerInfo/ScanButton.js ***!
@@ -510,13 +520,33 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
/***/ },
+/***/ "./js/src/react/components/TabContent/TabScanner/ScannerTable.js"
+/*!***********************************************************************!*\
+ !*** ./js/src/react/components/TabContent/TabScanner/ScannerTable.js ***!
+ \***********************************************************************/
+(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
+
+eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ ScannerTable)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\nfunction _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }\nfunction _nonIterableRest() { throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\"); }\nfunction _unsupportedIterableToArray(r, a) { if (r) { if (\"string\" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return \"Object\" === t && r.constructor && (t = r.constructor.name), \"Map\" === t || \"Set\" === t ? Array.from(r) : \"Arguments\" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }\nfunction _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }\nfunction _iterableToArrayLimit(r, l) { var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t[\"return\"] && (u = t[\"return\"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }\nfunction _arrayWithHoles(r) { if (Array.isArray(r)) return r; }\n;\nvar __ = wp.i18n.__;\nfunction ScannerTable(_ref) {\n var _table$pagination, _table$pagination3;\n var table = _ref.table,\n selected = _ref.selected,\n busy = _ref.busy,\n onToggle = _ref.onToggle,\n onToggleAll = _ref.onToggleAll,\n onPage = _ref.onPage,\n onSort = _ref.onSort,\n onRowAction = _ref.onRowAction,\n onBulk = _ref.onBulk,\n onBack = _ref.onBack;\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('-1'),\n _useState2 = _slicedToArray(_useState, 2),\n bulkAction = _useState2[0],\n setBulkAction = _useState2[1];\n var _useState3 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(String(((_table$pagination = table.pagination) === null || _table$pagination === void 0 ? void 0 : _table$pagination.page) || 1)),\n _useState4 = _slicedToArray(_useState3, 2),\n pageInput = _useState4[0],\n setPageInput = _useState4[1];\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n var _table$pagination2;\n setPageInput(String(((_table$pagination2 = table.pagination) === null || _table$pagination2 === void 0 ? void 0 : _table$pagination2.page) || 1));\n }, [(_table$pagination3 = table.pagination) === null || _table$pagination3 === void 0 ? void 0 : _table$pagination3.page]);\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n if (typeof spbcStartShowHide === 'function') {\n spbcStartShowHide();\n }\n }, [table.rows]);\n var rows = table.rows || [];\n var columns = table.columns || [];\n var allChecked = rows.length > 0 && rows.every(function (row) {\n return selected.indexOf(row.cb) !== -1;\n });\n var pagination = table.pagination || {\n page: 1,\n pages: 1,\n per_page: 0\n };\n var showPagination = pagination.per_page > 0 && table.items_total > pagination.per_page;\n var bulkKeys = Object.keys(table.bulk_actions || {});\n var go = function go(page) {\n var next = Math.min(Math.max(1, page), pagination.pages || 1);\n onPage(next);\n };\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n id: table.id,\n className: \"tbl-root\",\n \"data-type\": table.type\n }, table.html_before ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n className: \"spbc_hint\",\n onClickCapture: function onClickCapture(event) {\n if (table.type === 'links' && event.target.closest('a')) {\n event.preventDefault();\n event.stopPropagation();\n onBack();\n }\n },\n dangerouslySetInnerHTML: {\n __html: table.html_before\n }\n }) : null, bulkKeys.length > 0 && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n className: \"tbl-bulk_actions--wrapper\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"select\", {\n className: \"tbl-select\",\n value: bulkAction,\n onChange: function onChange(event) {\n return setBulkAction(event.target.value);\n }\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"option\", {\n value: \"-1\"\n }, __('Bulk actions', 'security-malware-firewall')), bulkKeys.map(function (key) {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"option\", {\n key: key,\n value: key\n }, table.bulk_actions[key].name);\n })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"button\", {\n type: \"button\",\n className: \"tbl-button tbl-button---white_blue tbl-bulk_actions--apply\",\n disabled: busy,\n onClick: function onClick() {\n return onBulk(bulkAction, false);\n }\n }, __('Apply to selected', 'security-malware-firewall')), table.bulk_actions_all ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"button\", {\n type: \"button\",\n className: \"tbl-button tbl-bulk_actions-all--apply\",\n disabled: busy,\n onClick: function onClick() {\n return onBulk(bulkAction, true);\n }\n }, __('Apply to all', 'security-malware-firewall')) : null), showPagination && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n className: \"tbl-pagination--wrapper\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n className: \"tbl-pagination--total\"\n }, table.items_total, \" \", __('Entries', 'security-malware-firewall')), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"button\", {\n type: \"button\",\n className: \"tbl-button tbl-pagination--button tbl-pagination--start\",\n \"aria-label\": __('First page', 'security-malware-firewall'),\n onClick: function onClick() {\n return go(1);\n }\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"i\", {\n className: \"spbc-icon-to-start\",\n \"aria-hidden\": \"true\"\n })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"button\", {\n type: \"button\",\n className: \"tbl-button tbl-pagination--button tbl-pagination--prev\",\n \"aria-label\": __('Previous page', 'security-malware-firewall'),\n onClick: function onClick() {\n return go(pagination.page - 1);\n }\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"i\", {\n className: \"spbc-icon-fast-bw\",\n \"aria-hidden\": \"true\"\n })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"input\", {\n type: \"text\",\n className: \"tbl-pagination--curr_page\",\n value: pageInput,\n \"aria-label\": __('Current page', 'security-malware-firewall'),\n onChange: function onChange(event) {\n return setPageInput(event.target.value);\n }\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n className: \"tbl-pagination--total\"\n }, __(' of ', 'security-malware-firewall'), pagination.pages), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"button\", {\n type: \"button\",\n className: \"tbl-button tbl-pagination--button tbl-pagination--go\",\n onClick: function onClick() {\n return go(parseInt(pageInput, 10) || 1);\n }\n }, __('Go', 'security-malware-firewall')), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"button\", {\n type: \"button\",\n className: \"tbl-button tbl-pagination--button tbl-pagination--next\",\n \"aria-label\": __('Next page', 'security-malware-firewall'),\n onClick: function onClick() {\n return go(pagination.page + 1);\n }\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"i\", {\n className: \"spbc-icon-fast-fw\",\n \"aria-hidden\": \"true\"\n })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"button\", {\n type: \"button\",\n className: \"tbl-button tbl-pagination--button tbl-pagination--end\",\n \"aria-label\": __('Last page', 'security-malware-firewall'),\n onClick: function onClick() {\n return go(pagination.pages);\n }\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"i\", {\n className: \"spbc-icon-to-end\",\n \"aria-hidden\": \"true\"\n }))), table.empty_html ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n dangerouslySetInnerHTML: {\n __html: table.empty_html\n }\n }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"table\", {\n className: \"wp-list-table widefat fixed striped\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"thead\", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"tr\", null, columns.map(function (column) {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"th\", {\n key: column.key,\n className: ['manage-column', 'column-' + column.key, column.primary ? 'column-primary' : '', column.sortable ? 'tbl-column-sortable' : '', column[\"class\"] || ''].filter(Boolean).join(' '),\n style: column.width_percent ? {\n width: column.width_percent + '%'\n } : undefined\n }, column.key === 'cb' ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"input\", {\n type: \"checkbox\",\n checked: allChecked,\n \"aria-label\": __('Select all rows', 'security-malware-firewall'),\n onChange: function onChange() {\n return onToggleAll(!allChecked);\n }\n }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, column.help_setting ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"i\", {\n className: \"spbc_long_description__show spbc-icon-help-circled\",\n \"data-setting\": column.help_setting\n }) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"button\", {\n type: \"button\",\n className: \"spbc-react-accordion__sort\",\n disabled: !column.sortable,\n onClick: function onClick() {\n return column.sortable && onSort(column.key);\n }\n }, column.heading, table.order_by === column.key ? table.order === 'desc' ? ' ↓' : ' ↑' : '')), column.hint ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n className: \"spbc_hint\",\n title: column.hint\n }, \" ?\") : null);\n }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"tbody\", null, rows.map(function (row) {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"tr\", {\n key: row.uid || row.cb,\n style: row.scheduled ? {\n color: 'darkgray'\n } : undefined\n }, columns.map(function (column) {\n var _row$cells$column$key;\n if (column.key === 'cb') {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"th\", {\n key: \"cb\",\n scope: \"row\",\n className: \"check-column\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"input\", {\n type: \"checkbox\",\n className: \"cb-select\",\n value: row.cb,\n checked: selected.indexOf(row.cb) !== -1,\n \"aria-label\": __('Select row', 'security-malware-firewall'),\n onChange: function onChange() {\n return onToggle(row.cb);\n }\n }));\n }\n var isPrimary = column.primary;\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"td\", {\n key: column.key,\n className: [column.key, 'column-' + column.key, isPrimary ? 'column-primary' : '', column[\"class\"] || ''].filter(Boolean).join(' '),\n \"data-before\": column.heading\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n dangerouslySetInnerHTML: {\n __html: (_row$cells$column$key = row.cells[column.key]) !== null && _row$cells$column$key !== void 0 ? _row$cells$column$key : '—'\n }\n }), isPrimary ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(RowActions, {\n row: row,\n table: table,\n onRowAction: onRowAction\n }) : null);\n }));\n }))));\n}\nfunction RowActions(_ref2) {\n var row = _ref2.row,\n table = _ref2.table,\n onRowAction = _ref2.onRowAction;\n var keys = (row.actions || []).filter(function (key) {\n return table.actions && table.actions[key];\n });\n if (!keys.length) {\n return null;\n }\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n className: \"row-actions\",\n \"data-uid\": row.uid,\n \"data-page-id\": row.page_id || ''\n }, keys.map(function (key, index) {\n var def = table.actions[key];\n var tip = def.tip || '';\n if (def.type === 'link') {\n var href = row.links && row.links[key] || '#';\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n key: key\n }, index > 0 ? ' | ' : '', /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n \"data-tooltip\": tip,\n className: 'tbl-row_action tbl-row_action_tooltip tbl-row_action--' + key\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"a\", {\n href: href,\n target: def.target || '_self',\n rel: \"noreferrer\"\n }, def.name)));\n }\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n key: key\n }, index > 0 ? ' | ' : '', /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"button\", {\n type: \"button\",\n \"data-tooltip\": tip,\n className: 'tbl-row_action tbl-row_action--ajax tbl-row_action_tooltip tbl-row_action--' + key,\n onClick: function onClick() {\n return onRowAction(key, row);\n }\n }, def.name));\n }));\n}\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabScanner/ScannerTable.js?\n}");
+
+/***/ },
+
/***/ "./js/src/react/components/TabContent/TabScanner/TabScanner.js"
/*!*********************************************************************!*\
!*** ./js/src/react/components/TabContent/TabScanner/TabScanner.js ***!
\*********************************************************************/
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
-eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ TabScanner)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _styles_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./styles.css */ \"./js/src/react/components/TabContent/TabScanner/styles.css\");\n/* harmony import */ var _TabContentTypes_Html_TabContentHtml__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../TabContentTypes/Html/TabContentHtml */ \"./js/src/react/components/TabContentTypes/Html/TabContentHtml.js\");\n/* harmony import */ var _modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../modules/Http/Ajax */ \"./js/src/react/modules/Http/Ajax.js\");\n/* harmony import */ var _Preloader_Preloader__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../Preloader/Preloader */ \"./js/src/react/components/Preloader/Preloader.js\");\n/* harmony import */ var _NoticeBanner_NoticeBanner__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../NoticeBanner/NoticeBanner */ \"./js/src/react/components/TabContent/NoticeBanner/NoticeBanner.js\");\n/* harmony import */ var _ScannerInfo_ScannerInfo__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ScannerInfo/ScannerInfo */ \"./js/src/react/components/TabContent/TabScanner/ScannerInfo/ScannerInfo.js\");\nfunction _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\"); }\nfunction _iterableToArray(r) { if (\"undefined\" != typeof Symbol && null != r[Symbol.iterator] || null != r[\"@@iterator\"]) return Array.from(r); }\nfunction _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2)); }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\nfunction _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }\nfunction _nonIterableRest() { throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\"); }\nfunction _unsupportedIterableToArray(r, a) { if (r) { if (\"string\" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return \"Object\" === t && r.constructor && (t = r.constructor.name), \"Map\" === t || \"Set\" === t ? Array.from(r) : \"Arguments\" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }\nfunction _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }\nfunction _iterableToArrayLimit(r, l) { var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t[\"return\"] && (u = t[\"return\"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }\nfunction _arrayWithHoles(r) { if (Array.isArray(r)) return r; }\n;\n\n\n\n\n\n\nfunction TabScanner(_ref) {\n var sectionId = _ref.sectionId,\n sectionData = _ref.sectionData,\n isActive = _ref.isActive;\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null),\n _useState2 = _slicedToArray(_useState, 2),\n scannerStatus = _useState2[0],\n setScannerStatus = _useState2[1];\n var prevStatusRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);\n var _useState3 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null),\n _useState4 = _slicedToArray(_useState3, 2),\n currentStage = _useState4[0],\n setCurrentStage = _useState4[1];\n var _useState5 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]),\n _useState6 = _slicedToArray(_useState5, 2),\n completedStages = _useState6[0],\n setCompletedStages = _useState6[1];\n // Progress (0-100%) of the currently running stage only, plus the raw offset/total it was\n // computed from. `null` percent means the backend couldn't determine a total for this stage\n // (e.g. quick stages like clean_results), so the UI shows an indeterminate progress state.\n var _useState7 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({\n offset: null,\n total: null,\n percent: null,\n detail: null\n }),\n _useState8 = _slicedToArray(_useState7, 2),\n currentStageProgress = _useState8[0],\n setCurrentStageProgress = _useState8[1];\n var intervalRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);\n // Tracks whether the previous poll tick saw the scanner running, so we know to do one last\n // log refresh right after it finishes (the last tick's fetch may have missed the very last\n // rows written while the \"send_results\"/cure stages were wrapping up).\n var scanWasInProgressRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);\n var errorShownRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);\n var clearInFlightRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);\n var legacyHandlersBoundRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);\n var scanFinishedRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);\n var pollGenerationRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(0);\n var logScannerDebug = function logScannerDebug(_event, _extra) {};\n\n // Pulls the freshest scan log rows (written server-side to the DB by the background scan,\n // see CleantalkSP\\SpbctWP\\Scanner\\ScanningLog\\Repository::write()) and renders them into the\n // live \".spbc_log-wrapper\" block. This reuses the same AJAX action the historical, paginated\n // \"#spbcscan-results-log-module\" log uses to lazy-load older rows on scroll; here it's always\n // called with offset 0 to get the latest rows (Repository::getAll() orders by timestamp DESC).\n var fetchScanLog = function fetchScanLog() {\n jQuery.ajax({\n url: spbcSettings.ajaxurl,\n type: 'POST',\n data: {\n action: 'spbc_scanner_load_more_scan_logs',\n security: spbcSettings.ajax_nonce,\n no_cache: Math.random(),\n offset: 0\n },\n success: function success(data) {\n if (!data || !data.success || !data.data) {\n return;\n }\n if (!jQuery('.spbc_log-wrapper .panel-body').length) {\n jQuery('.spbc_log-wrapper').append('
');\n }\n jQuery('.spbc-scan-log-title').removeClass('spbc---hidden');\n jQuery('.spbc_log-wrapper').removeClass('spbc---hidden');\n jQuery('#spbc_log_actions_wrapper').removeClass('spbc---hidden');\n jQuery('.spbc_log-wrapper .panel-body').html(data.data);\n }\n });\n };\n var fetchScannerStatus = function fetchScannerStatus() {\n var generation = pollGenerationRef.current;\n var data = {\n action: 'spbc_scanner__getScannerStatus'\n };\n var params = {\n callback: function callback(result) {\n if (generation !== pollGenerationRef.current) {\n return;\n }\n scannerStatusCallback(result);\n },\n errorOutput: function errorOutput(msg) {\n console.log(msg);\n }\n };\n spbcSendAJAXRequest(data, params);\n };\n\n // (Re)starts the polling interval. Used on mount and whenever a new scan is kicked off,\n // so polling reliably resumes even if it had been stopped after a previous scan ended.\n var startPolling = function startPolling() {\n clearInterval(intervalRef.current);\n pollGenerationRef.current += 1;\n errorShownRef.current = false;\n scanFinishedRef.current = false;\n window.spbcScanner = window.spbcScanner || {};\n window.spbcScanner.finished = false;\n\n // First request do immediately\n fetchScannerStatus();\n\n // Start polling to fetch the scanner state\n intervalRef.current = setInterval(fetchScannerStatus, 5000);\n };\n var escapeHtml = function escapeHtml(value) {\n return String(value == null ? '' : value).replace(/&/g, '&').replace(//g, '>').replace(/\"/g, '"').replace(/'/g, ''');\n };\n var ackRuntimeError = function ackRuntimeError(errorId) {\n if (!errorId) {\n return;\n }\n spbcSendAJAXRequest({\n action: 'spbc_scanner__ackRuntimeError',\n error_id: errorId\n }, {\n callback: function callback() {},\n errorOutput: function errorOutput() {}\n });\n };\n var showScannerError = function showScannerError(message, stage, errorId) {\n var errorMsg = escapeHtml(message || 'Internal server error.');\n var text = stage ? errorMsg + ' Stage: ' + escapeHtml(stage) : errorMsg;\n if (typeof spbcModal !== 'undefined' && spbcModal.open) {\n spbcModal.open().putError(text);\n }\n ackRuntimeError(errorId);\n };\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n // Stop polling on the scanner ended or failed. Keep polling while paused.\n if (scannerStatus === 'error' || scannerStatus === 'end' && scanFinishedRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n // Reload after a confirmed finished scan. Resume can go paused → end\n // without an in_progress tick if the last chunk already completed.\n if ((prevStatusRef.current === 'in_progress' || prevStatusRef.current === 'paused') && scannerStatus === 'end') {\n if (scanFinishedRef.current) {\n logScannerDebug('reload_on_finished', {\n prev: prevStatusRef.current\n });\n window.location.reload();\n } else {\n logScannerDebug('skip_reload_unfinished_end', {\n prev: prevStatusRef.current\n });\n }\n }\n prevStatusRef.current = scannerStatus;\n }, [scannerStatus]);\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n window.spbcScanner = window.spbcScanner || {};\n window.spbcScanner.active = false;\n startPolling();\n // Cleanup on unmount\n return function () {\n return clearInterval(intervalRef.current);\n };\n }, []);\n function scannerStatusCallback(result) {\n var _result$data;\n if (result && result !== null && result !== void 0 && (_result$data = result.data) !== null && _result$data !== void 0 && _result$data.status) {\n var _result$data4;\n var status = result.data.status;\n if (status === 'error') {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n setScannerStatus('error');\n window.spbcScanner = window.spbcScanner || {};\n window.spbcScanner.active = false;\n scanWasInProgressRef.current = false;\n if (!errorShownRef.current) {\n errorShownRef.current = true;\n showScannerError(result.data.error, result.data.stage, result.data.error_id);\n }\n return;\n }\n var finished = !!result.data.finished;\n scanFinishedRef.current = finished;\n window.spbcScanner = window.spbcScanner || {};\n window.spbcScanner.finished = finished;\n if (status === 'end' && !finished) {\n var _result$data2;\n logScannerDebug('ignore_end_not_finished', {\n stage: (result === null || result === void 0 || (_result$data2 = result.data) === null || _result$data2 === void 0 || (_result$data2 = _result$data2.current_stage) === null || _result$data2 === void 0 ? void 0 : _result$data2.stage) || null\n });\n return;\n }\n if (prevStatusRef.current !== status) {\n var _result$data3;\n logScannerDebug('status_change', {\n from: prevStatusRef.current,\n to: status,\n stage: (result === null || result === void 0 || (_result$data3 = result.data) === null || _result$data3 === void 0 || (_result$data3 = _result$data3.current_stage) === null || _result$data3 === void 0 ? void 0 : _result$data3.stage) || null,\n finished: finished\n });\n }\n setScannerStatus(status);\n if (result !== null && result !== void 0 && (_result$data4 = result.data) !== null && _result$data4 !== void 0 && (_result$data4 = _result$data4.current_stage) !== null && _result$data4 !== void 0 && _result$data4.stage) {\n var _result$data5, _result$data$current_, _result$data6, _result$data$current_2, _result$data7, _result$data$current_3, _result$data8, _result$data$current_4, _result$data9;\n setCurrentStage(result.data.current_stage.stage);\n setCompletedStages(((result === null || result === void 0 || (_result$data5 = result.data) === null || _result$data5 === void 0 ? void 0 : _result$data5.completed_stages) || []).map(function (stage) {\n return stage.stage;\n }));\n setCurrentStageProgress({\n offset: (_result$data$current_ = result === null || result === void 0 || (_result$data6 = result.data) === null || _result$data6 === void 0 || (_result$data6 = _result$data6.current_stage) === null || _result$data6 === void 0 ? void 0 : _result$data6.offset) !== null && _result$data$current_ !== void 0 ? _result$data$current_ : null,\n total: (_result$data$current_2 = result === null || result === void 0 || (_result$data7 = result.data) === null || _result$data7 === void 0 || (_result$data7 = _result$data7.current_stage) === null || _result$data7 === void 0 ? void 0 : _result$data7.total) !== null && _result$data$current_2 !== void 0 ? _result$data$current_2 : null,\n percent: (_result$data$current_3 = result === null || result === void 0 || (_result$data8 = result.data) === null || _result$data8 === void 0 || (_result$data8 = _result$data8.current_stage) === null || _result$data8 === void 0 ? void 0 : _result$data8.percent) !== null && _result$data$current_3 !== void 0 ? _result$data$current_3 : null,\n detail: (_result$data$current_4 = result === null || result === void 0 || (_result$data9 = result.data) === null || _result$data9 === void 0 || (_result$data9 = _result$data9.current_stage) === null || _result$data9 === void 0 ? void 0 : _result$data9.detail) !== null && _result$data$current_4 !== void 0 ? _result$data$current_4 : null\n });\n }\n if (status === 'in_progress' || status === 'paused') {\n scanWasInProgressRef.current = true;\n window.spbcScanner.active = status === 'in_progress';\n fetchScanLog();\n } else if (scanWasInProgressRef.current) {\n // Scan just finished: refresh once more to catch the last rows written\n // while the scan was wrapping up (e.g. the \"send_results\" stage duration entry).\n scanWasInProgressRef.current = false;\n window.spbcScanner.active = false;\n fetchScanLog();\n }\n } else {\n // Stop polling if the wrong answer coming\n clearInterval(intervalRef.current);\n setScannerStatus(null);\n setCurrentStage(null);\n setCompletedStages([]);\n setCurrentStageProgress({\n offset: null,\n total: null,\n percent: null,\n detail: null\n });\n }\n }\n\n /** **************** SUPPORT TEMPORARY BOTH CONTENT FETCHED - LEGACY AND NEW *****************/\n var _useState9 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({\n sectionData: sectionData\n }),\n _useState0 = _slicedToArray(_useState9, 2),\n sectionDataLegacy = _useState0[0],\n setSectionDataLegacy = _useState0[1];\n var _useState1 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false),\n _useState10 = _slicedToArray(_useState1, 2),\n scannerDataLegacyFetched = _useState10[0],\n setSectionDataLegacyFetched = _useState10[1];\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n fetchOldContent();\n }, []);\n var fetchOldContent = /*#__PURE__*/function () {\n var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {\n var requestData, data, _t;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.p = _context.n) {\n case 0:\n _context.p = 0;\n requestData = {\n action: 'spbc_settings__draw_elements',\n tab_name: sectionId\n };\n _context.n = 1;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__.sendAjaxRequest)(requestData);\n case 1:\n data = _context.v;\n setSectionDataLegacy(data);\n setSectionDataLegacyFetched(true);\n document.dispatchEvent(new CustomEvent('spbctTabLoaded', {\n bubbles: true,\n detail: {\n tab: sectionId\n }\n }));\n _context.n = 3;\n break;\n case 2:\n _context.p = 2;\n _t = _context.v;\n console.error('Error fetching content:', _t);\n setSectionDataLegacy('');\n setSectionDataLegacyFetched(true);\n case 3:\n return _context.a(2);\n }\n }, _callee, null, [[0, 2]]);\n }));\n return function fetchOldContent() {\n return _ref2.apply(this, arguments);\n };\n }();\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n if (!scannerDataLegacyFetched) {\n return;\n }\n if (jQuery('#spbcscan-results-log-module').length) {\n jQuery('#spbc_log_actions_wrapper').removeClass('spbc---hidden');\n }\n }, [scannerDataLegacyFetched, sectionDataLegacy]);\n if (!scannerDataLegacyFetched) {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Preloader_Preloader__WEBPACK_IMPORTED_MODULE_4__[\"default\"], null);\n }\n /** **************** END SUPPORT TEMPORARY BOTH CONTENT FETCHED *****************/\n\n /** **** L E G A C Y ******/\n\n /**\n * Show popup tip\n *\n * @param {object} showIcon\n */\n window.spbcPopupTipShow = function (showIcon) {\n // eslint-disable-line no-unused-vars\n var tipTitle = showIcon.attr('spbc_tip_title') || null;\n var tipText = showIcon.attr('spbc_tip_text') || null;\n var _removeDescFunc = function removeDescFunc(e) {\n if (typeof e === 'undefined' || jQuery(e.target).parent('.spbc_popup_tip--wrapper').length == 0 || jQuery(e.target).hasClass('spbc_popup_tip--icon---cancel')) {\n jQuery('.spbc_popup_tip--wrapper').hide();\n jQuery(document).off('click', _removeDescFunc);\n }\n };\n _removeDescFunc();\n showIcon.after('');\n var obj = jQuery('.spbc_popup_tip--wrapper');\n obj.empty().append('').css({\n top: showIcon.position().top - 2,\n left: showIcon.position().left + 25\n });\n obj.append('').append('').append('');\n jQuery(document).on('click', _removeDescFunc);\n showIcon.parents('tr').on('mouseout', _removeDescFunc);\n };\n\n /**\n * View file in the scanner\n *\n * @param {object} obj\n */\n window.spbcScannerButtonFileViewEvent = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var data = {\n action: 'spbc_scanner_file_view',\n file_id: self.parents('.row-actions').attr('uid')\n };\n var params = {\n spinner: self.parent().siblings('.tbl-preloader--tiny'),\n callback: spbcScannerButtonViewCallback,\n errorOutput: function errorOutput(msg) {\n spbcModal.open().putError(msg);\n document.addEventListener('spbcModalClosed', function (e) {\n spbcReloadAccordion();\n });\n }\n };\n spbcSendAJAXRequest(data, params);\n };\n\n /**\n * Delete file from analysis log\n *\n * @param {object} obj\n */\n window.spbcScannerAnalysisLogDeleteFromLog = function (obj) {\n // eslint-disable-line no-unused-vars\n var fileIds = [];\n var self = jQuery(obj);\n var spinner;\n if (jQuery(obj).hasClass('tbl-row_action')) {\n fileIds.push(self.parents('.row-actions').attr('uid'));\n spinner = self.parent().siblings('.tbl-preloader--tiny');\n } else {\n var selectedItems = self.closest('#spbc_tbl__scanner_analysis_log').find('.cb-select:checked');\n if (selectedItems.length === 0) {\n alert('Please, select elements.');\n return;\n }\n selectedItems.each(function (index, element) {\n var elementId = jQuery(element).val();\n fileIds.push(elementId);\n });\n spinner = self.children('.tbl-preloader--small');\n }\n var data = {\n action: 'spbc_scanner_analysis_log_delete_from_log',\n file_ids: fileIds\n };\n var params = {\n spinner: spinner,\n callback: function callback() {\n window.location.reload();\n }\n };\n spbcSendAJAXRequest(data, params);\n };\n\n /**\n * View page in the scanner\n *\n * @param {object} obj\n */\n window.spbcScannerButtonPageViewEvent = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var data = {\n action: 'spbc_scanner_page_view',\n page_url: self.parents('.row-actions').attr('uid')\n };\n var params = {\n spinner: self.parent().siblings('.tbl-preloader--tiny'),\n callback: spbcScannerButtonViewCallback,\n errorOutput: function errorOutput(msg) {\n spbcModal.open().putError(msg);\n }\n };\n var wpWrap = jQuery('#wpwrap');\n var dialogWindow = jQuery('#spbc_dialog');\n dialogWindow.dialog({\n modal: true,\n title: 'Loading..',\n position: {\n my: 'center top',\n at: 'center top+100px',\n of: window\n },\n width: +(wpWrap.width() / 100 * 80),\n show: {\n effect: 'blind',\n duration: 500\n },\n draggable: false,\n resizable: false,\n closeText: 'X',\n classes: {\n 'ui-dialog': 'spbc---top'\n },\n open: function open(event, ui) {\n event.target.style.overflow = 'auto';\n jQuery('#spbc_dialog').height(document.documentElement.clientHeight / 100 * 25);\n },\n beforeClose: function beforeClose(event, ui) {\n document.body.style.overflow = 'auto';\n jQuery('#spbc_dialog').empty();\n }\n });\n dialogWindow.append(' ');\n var spinner = jQuery('#spbc_file_view_preloader');\n var sizeMultiplier = wpWrap.width() * 0.0004;\n spinner.height(128 * sizeMultiplier);\n spinner.width(128 * sizeMultiplier);\n spinner.css({\n left: dialogWindow.width() / 2 - 128 * sizeMultiplier / 2\n });\n spinner.css({\n top: dialogWindow.height() / 2 - 128 * sizeMultiplier / 2\n });\n spbcSendAJAXRequest(data, params);\n };\n\n /**\n * Callback for file view\n *\n * @param {object} result\n */\n function spbcScannerButtonViewCallback(result) {\n // eslint-disable-line no-unused-vars\n var dialogWindow = jQuery('#spbc_dialog');\n dialogWindow.empty();\n jQuery('#spbc_file_view_preloader').css({\n display: 'none'\n });\n var rowTemplate = '%s ';\n rowTemplate += '
%s
';\n\n /* eslint-disable guard-for-in */\n for (var row in result.file) {\n dialogWindow.append(rowTemplate.printf(row, result.file[row]));\n }\n var contentHeight = Object.keys(result.file).length * 19 < 76 ? 76 : Object.keys(result.file).length * 19;\n var visibleHeight = document.documentElement.clientHeight / 100 * 75;\n var overflow = contentHeight < visibleHeight ? 'hidden' : 'scroll';\n var height = overflow === 'scroll' ? visibleHeight : contentHeight;\n dialogWindow.css({\n height: height,\n overflow: overflow\n });\n dialogWindow.dialog({\n modal: true,\n title: 'Loaded: ' + result.file_path,\n position: {\n my: 'center top',\n at: 'center top+100px',\n of: window\n },\n width: +(jQuery('#wpwrap').width() / 100 * 80),\n show: {\n effect: 'blind',\n duration: 500\n },\n draggable: true,\n resizable: false,\n height: height,\n closeText: 'X',\n classes: {\n 'ui-dialog': 'spbc---top'\n },\n open: function open(event, ui) {\n event.target.style.overflow = overflow;\n jQuery('#spbc_dialog').height(height);\n jQuery('.ui-widget-overlay').on('click', function () {\n jQuery('#spbc_dialog').dialog('close');\n });\n },\n beforeClose: function beforeClose(event, ui) {\n document.body.style.overflow = 'auto';\n jQuery('#spbc_dialog').empty();\n }\n });\n console.log('FILE_VIEWED');\n }\n\n /**\n * Event for file view bad\n *\n * @param {object} obj\n */\n window.spbcScannerButtonFileViewBadEvent = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var data = {\n action: 'spbc_scanner_file_view',\n file_id: self.parents('.row-actions').attr('uid')\n };\n var params = {\n spinner: self.parent().siblings('.tbl-preloader--tiny'),\n callback: spbcScannerButtonViewBadFileCallback,\n errorOutput: function errorOutput(msg) {\n spbcModal.open().putError(msg);\n }\n };\n spbcSendAJAXRequest(data, params);\n };\n\n /**\n * Event for page view bad\n *\n * @param {object} obj\n */\n window.spbcScannerButtonPageViewBadEvent = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var data = {\n action: 'spbc_scanner_page_view',\n page_url: self.parent().attr('uid')\n };\n var params = {\n spinner: self.parent().siblings('.tbl-preloader--tiny'),\n callback: spbcScannerButtonViewBadCallback,\n errorOutput: function errorOutput(msg) {\n spbcModal.open().putError(msg);\n }\n };\n spbcSendAJAXRequest(data, params);\n };\n\n /**\n * Callback for file view bad\n *\n * @param {object} result\n * @param {object} data\n * @param {object} params\n */\n function spbcScannerButtonViewBadFileCallback(result, data, params) {\n // eslint-disable-line no-unused-vars\n console.log('FILE_VIEWED_BAD_CODE_FILE');\n jQuery('#spbc_dialog').empty();\n var rowTemplate = '%s ';\n rowTemplate += '
%s
';\n var rowTemplateBad = '';\n rowTemplateBad += '
%s ';\n rowTemplateBad += '
%s
';\n var severityParts = [];\n var severityPartsSorted = [];\n var severityKeysSorted = [];\n result.weak_spots = JSON.parse(result.weak_spots);\n /* eslint-disable guard-for-in */\n for (var severity in result.weak_spots) {\n severityParts[severity] = Object.keys(result.weak_spots[severity]);\n }\n severityKeysSorted = Object.keys(severityParts).sort();\n\n /* eslint-disable guard-for-in */\n for (var key in severityKeysSorted) {\n severityPartsSorted[severityKeysSorted[key]] = severityParts[severityKeysSorted[key]];\n }\n\n /* eslint-disable guard-for-in */\n for (var severityPart in severityPartsSorted) {\n var headerName = 'spbc-dialog-row-header-' + severityPart.toLowerCase();\n var headerHtml = '';\n headerHtml += severityPart + ' weakspots: ';\n var dialog = jQuery('#spbc_dialog');\n dialog.append(rowTemplate.printf('', headerHtml));\n dialog.append(rowTemplate.printf('... ', '... '));\n var header = dialog.find('#' + headerName);\n header.parent().parent().css('text-align', 'center');\n header.parent().parent().css('border', '1px dotted');\n header.parent().parent().css('background', 'darkgrey');\n\n /* eslint-disable guard-for-in */\n for (var strNum in severityParts[severityPart]) {\n // Sort bad line\n strNum = _toConsumableArray(new Set(strNum)).sort();\n var row = +severityParts[severityPart][strNum];\n var displayDepth = 2;\n var temp = void 0;\n // Display only existing lines of file\n for (var currRow = row - displayDepth; currRow <= row + displayDepth; currRow++) {\n if (typeof result.file[currRow] !== 'undefined') {\n temp = row === currRow ? rowTemplateBad : rowTemplate;\n dialog.append(temp.printf(currRow, result.file[currRow]));\n }\n }\n\n // Placing an empty string between results\n dialog.append(rowTemplate.printf('... ', '... '));\n }\n }\n\n // Removing last empty string\n jQuery('#spbc_dialog div').last().remove();\n var contentHeight = jQuery('#spbc_dialog div').length * 19 < 76 ? 76 : jQuery('#spbc_dialog div').length * 19;\n var visibleHeight = document.documentElement.clientHeight / 100 * 75;\n var overflow = contentHeight < visibleHeight ? 'no_scroll' : 'scroll';\n var height = overflow === 'scroll' ? visibleHeight : contentHeight;\n jQuery('#spbc_dialog').dialog({\n modal: true,\n title: result.file_path,\n position: {\n my: 'center top',\n at: 'center top+100px',\n of: window\n },\n width: +(jQuery('#wpwrap').width() / 100 * 80),\n show: {\n effect: 'blind',\n duration: 500\n },\n draggable: true,\n resizable: false,\n closeText: 'X',\n classes: {\n 'ui-dialog': 'spbc---top'\n },\n open: function open(event, ui) {\n event.target.style.overflow = overflow;\n jQuery('#spbc_dialog').height(height);\n jQuery('.ui-widget-overlay').on('click', function () {\n jQuery('#spbc_dialog').dialog('close');\n });\n },\n beforeClose: function beforeClose(event, ui) {\n document.body.style.overflow = 'auto';\n jQuery('#spbc_dialog').empty();\n }\n });\n }\n\n /**\n * Callback for file view bad\n *\n * @param {object} result\n * @param {object} data\n * @param {object} params\n */\n function spbcScannerButtonViewBadCallback(result, data, params) {\n // eslint-disable-line no-unused-vars\n console.log('FILE_VIEWED_BAD_CODE');\n var afterClose = function afterClose() {};\n if (result.success == true) {\n console.log(result);\n var rowTemplate = '';\n var rowTemplateBad = '';\n var strNums = [];\n result.weak_spots = JSON.parse(result.weak_spots);\n /* eslint-disable guard-for-in */\n for (var severity in result.weak_spots) {\n strNums = strNums.concat(Object.keys(result.weak_spots[severity]));\n }\n strNums = _toConsumableArray(new Set(strNums)).sort(); // Sort bad line\n // get a map of bad strings\n // TODO: Probably there can get something else except CRITICAL in the future\n var badStringsMap = new Map(Object.entries(result.weak_spots.CRITICAL));\n /* eslint-disable guard-for-in */\n for (var strNum in strNums) {\n if (strNum == 0 && badStringsMap.get(strNums[strNum]).toLowerCase().includes('doctype')) {\n continue;\n }\n var row = +strNums[strNum];\n var displayDepth = 2;\n\n // Display only existing lines of file\n for (var currRow = row - displayDepth; currRow <= row + displayDepth; currRow++) {\n if (typeof result.file[currRow] !== 'undefined') {\n var isBadString = row === currRow;\n var unformattedRow = void 0;\n var highlitedRow = void 0;\n if (isBadString) {\n unformattedRow = rowTemplateBad;\n } else {\n unformattedRow = rowTemplate;\n }\n if (isBadString && !result.file[currRow].includes('iframe')) {\n highlitedRow = unformattedRow.printf(currRow, badStringsMap.get(strNums[strNum]));\n } else {\n highlitedRow = unformattedRow.printf(currRow, result.file[currRow]);\n }\n jQuery('#spbc_dialog').append(highlitedRow);\n }\n }\n\n // Placing an empty string between results\n jQuery('#spbc_dialog').append(rowTemplate.printf('', ''));\n\n // Removing last empty string\n jQuery('#spbc_dialog div').last().remove();\n }\n } else {\n var wrapperElement = '%s
';\n jQuery('#spbc_dialog').append(wrapperElement.printf(result.content));\n afterClose = function afterClose() {\n document.location.reload();\n };\n }\n var contentHeight = jQuery('#spbc_dialog div').length * 19 < 76 ? 76 : jQuery('#spbc_dialog div').length * 19;\n var visibleHeight = document.documentElement.clientHeight / 100 * 75;\n var height = 'auto';\n var overflow = contentHeight < visibleHeight ? 'no_scroll' : 'scroll';\n jQuery('#spbc_dialog').dialog({\n modal: true,\n title: result.file_path,\n position: {\n my: 'center top',\n at: 'center top+100px',\n of: window\n },\n width: +(jQuery('#wpwrap').width() / 100 * 80),\n show: {\n effect: 'blind',\n duration: 500\n },\n draggable: true,\n resizable: false,\n closeText: 'X',\n classes: {\n 'ui-dialog': 'spbc---top'\n },\n open: function open(event, ui) {\n event.target.style.overflow = overflow;\n jQuery('#spbc_dialog').height(height);\n jQuery('.ui-widget-overlay').on('click', function () {\n jQuery('#spbc_dialog').dialog('close');\n });\n },\n beforeClose: function beforeClose(event, ui) {\n document.body.style.overflow = 'auto';\n jQuery('#spbc_dialog').empty();\n },\n close: afterClose\n });\n }\n\n /**\n * Cure file\n * @param {object} obj\n */\n window.spbcScannerButtonCureFileAjaxHandler = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var fileFastHash = self.parents('.row-actions').attr('uid');\n var data = {\n action: 'spbc_file_cure_ajax_action',\n file_fast_hash: fileFastHash,\n security: spbcSettings.ajax_nonce\n };\n jQuery.ajax({\n type: 'POST',\n async: true,\n url: spbcSettings.ajaxurl,\n data: data,\n success: function success(result) {\n if (result.success) {\n spbcModal.open().put(result.data);\n document.addEventListener('spbcModalClosed', function (e) {\n document.location.reload();\n });\n } else {\n spbcModal.open().putError(result.data);\n }\n }\n });\n };\n\n /**\n * Restore file from backup\n * @param {object} obj\n */\n window.spbcScannerButtonRestoreFromBackupAjaxHandler = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var fileFastHash = self.parents('.row-actions').attr('uid');\n var data = {\n action: 'spbc_restore_file_from_backup_ajax_action',\n file_fast_hash: fileFastHash,\n security: spbcSettings.ajax_nonce\n };\n jQuery.ajax({\n type: 'POST',\n async: true,\n url: spbcSettings.ajaxurl,\n data: data,\n success: function success(result) {\n if (result.success) {\n spbcModal.open().put(result.data);\n } else {\n spbcModal.open().putError(result.data);\n }\n document.addEventListener('spbcModalClosed', function (e) {\n document.location.reload();\n });\n }\n });\n };\n\n /**\n * Show copy log to clipboard hint\n * @param {string} text\n */\n function spbcScannerCopyLogToClipboardHint(text) {\n jQuery('#spbc_scanner_copy_log_to_clipboard_hint').text(text);\n jQuery('#spbc_scanner_copy_log_to_clipboard_hint').show();\n jQuery('#spbc_scanner_copy_log_to_clipboard_hint').animate({\n opacity: 0\n }, 3000, function () {\n jQuery(this).hide();\n jQuery(this).css('opacity', 1);\n });\n }\n if (!legacyHandlersBoundRef.current) {\n legacyHandlersBoundRef.current = true;\n jQuery(document).ready(function () {\n // EVENT HADLING\n spbcTblBulkActionsListen(); // Table. Row bulk handler\n spbcTblRowActionsListen(); // Table. Row actions handler\n spbcTblPaginationListen(); // Table. Pagination handler\n spbcTblSortListen(); // Table. Sort handler\n\n spbcStartShowHide();\n jQuery(document).off('click.spbcPopupTip', '.spbc_popup_tip--icon---show');\n jQuery(document).on('click.spbcPopupTip', '.spbc_popup_tip--icon---show', function () {\n spbcPopupTipShow(jQuery(this));\n });\n jQuery('#spbc_perform_scan').off('click.spbcScanLog').on('click.spbcScanLog', function () {\n jQuery('#spbcscan-results-log-module').remove();\n // spbcScanner.actionControl();\n\n if (!jQuery('.spbc_log-wrapper .panel-body').length) {\n jQuery('.spbc_log-wrapper').append('
');\n }\n jQuery('.spbc_log-wrapper').resizable({\n handles: 'n, s',\n containment: 'document'\n });\n });\n jQuery('#spbcscan-results-log-module').resizable({\n handles: 'n, s',\n containment: 'document'\n });\n logActionsHandleClearLogs();\n logActionsHandleMoreLogs();\n logActionsHandleCopyToClipboard();\n });\n }\n function logActionsHandleClearLogs() {\n jQuery(document).off('click.spbcScannerClear', '#spbc_scanner_clear');\n jQuery(document).on('click.spbcScannerClear', '#spbc_scanner_clear', function (e) {\n e.preventDefault();\n if (clearInFlightRef.current) {\n return;\n }\n if (!confirm(spbcSettings.clear_results_confirm)) {\n return;\n }\n clearInFlightRef.current = true;\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n spbcSendAJAXRequest({\n action: 'spbc_scanner_clear'\n }, {\n timeout: 40000,\n callback: function callback() {\n document.location.reload();\n },\n errorOutput: function errorOutput(msg) {\n clearInFlightRef.current = false;\n startPolling();\n console.log(msg);\n }\n });\n });\n }\n function logActionsHandleCopyToClipboard() {\n jQuery(document).off('click.spbcScannerCopy', '#spbc_scanner_copy_log_to_clipboard');\n jQuery(document).on('click.spbcScannerCopy', '#spbc_scanner_copy_log_to_clipboard', function (e) {\n e.preventDefault();\n if (!navigator.clipboard) {\n spbcScannerCopyLogToClipboardHint(spbcScaner.copy_log_to_clipboard_hint_unsupported);\n return;\n }\n var $log = jQuery('#spbcscan-results-log-module .panel-body');\n if (!$log.length) {\n $log = jQuery('.spbc_log-wrapper .panel-body');\n }\n var log = $log.html() || '';\n var formattedLog = log.replace(//g, '').replace(/<\\/b>/g, '');\n formattedLog = formattedLog.replace(/ /g, '\\n').replace(/<\\/p>/g, '');\n navigator.clipboard.writeText(formattedLog).then(function () {\n spbcScannerCopyLogToClipboardHint(spbcScaner.copy_log_to_clipboard_hint);\n })[\"catch\"](function () {\n spbcScannerCopyLogToClipboardHint(spbcScaner.copy_log_to_clipboard_hint_failed);\n });\n });\n }\n function logActionsHandleMoreLogs() {\n var element = jQuery('#spbcscan_results_log_body');\n var logAppendPreloader = jQuery('#spbcscan_results_log_preloader');\n if (!element || !logAppendPreloader) {\n return;\n }\n element.on('scroll', function () {\n // Check if load completed\n if (jQuery(this).data('stop-load')) {\n return;\n }\n\n // Is end of scroll\n var isAtBottom = this.scrollHeight - this.scrollTop <= this.clientHeight + 1;\n if (isAtBottom) {\n var $this = jQuery(this);\n var currentOffset = parseInt($this.data('offset')) || 0;\n logAppendPreloader.css('visibility', 'visible');\n jQuery.ajax({\n url: spbcSettings.ajaxurl,\n type: 'POST',\n data: {\n action: 'spbc_scanner_load_more_scan_logs',\n security: spbcSettings.ajax_nonce,\n no_cache: Math.random(),\n offset: currentOffset\n },\n xhrFields: {\n responseType: 'text'\n },\n success: function success(data) {\n if (data.success && data.data) {\n // increase and set new offset\n var newOffset = currentOffset + 100;\n $this.data('offset', newOffset);\n\n // add html to the log\n jQuery('#spbcscan_results_log_body').append(data.data);\n\n // if no data - stop load\n if (data.data.length === 0 || data.data.trim() === '') {\n $this.data('stop-load', 1);\n }\n } else {\n $this.data('stop-load', 1);\n }\n logAppendPreloader.css('visibility', 'hidden');\n },\n error: function error(xhr, status, _error) {\n logAppendPreloader.css('visibility', 'hidden');\n $this.data('stop-load', 1);\n }\n });\n }\n });\n }\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, window.spbcSettings && window.spbcSettings.critical > 0 && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_NoticeBanner_NoticeBanner__WEBPACK_IMPORTED_MODULE_5__[\"default\"], null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n className: \"spbc_wrapper_field\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ScannerInfo_ScannerInfo__WEBPACK_IMPORTED_MODULE_6__[\"default\"], {\n sectionData: sectionData,\n scannerStatus: scannerStatus,\n currentStage: currentStage,\n completedStages: completedStages,\n currentStageProgress: currentStageProgress,\n onScanStarted: startPolling\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_TabContentTypes_Html_TabContentHtml__WEBPACK_IMPORTED_MODULE_2__[\"default\"], {\n sectionId: sectionId,\n sectionData: sectionDataLegacy,\n isActive: isActive\n })));\n}\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabScanner/TabScanner.js?\n}");
+eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ TabScanner)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _styles_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./styles.css */ \"./js/src/react/components/TabContent/TabScanner/styles.css\");\n/* harmony import */ var _TabContentTypes_Html_TabContentHtml__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../TabContentTypes/Html/TabContentHtml */ \"./js/src/react/components/TabContentTypes/Html/TabContentHtml.js\");\n/* harmony import */ var _modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../modules/Http/Ajax */ \"./js/src/react/modules/Http/Ajax.js\");\n/* harmony import */ var _Preloader_Preloader__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../Preloader/Preloader */ \"./js/src/react/components/Preloader/Preloader.js\");\n/* harmony import */ var _NoticeBanner_NoticeBanner__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../NoticeBanner/NoticeBanner */ \"./js/src/react/components/TabContent/NoticeBanner/NoticeBanner.js\");\n/* harmony import */ var _ScannerInfo_ScannerInfo__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ScannerInfo/ScannerInfo */ \"./js/src/react/components/TabContent/TabScanner/ScannerInfo/ScannerInfo.js\");\n/* harmony import */ var _ScannerAccordions__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./ScannerAccordions */ \"./js/src/react/components/TabContent/TabScanner/ScannerAccordions.js\");\nfunction _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\"); }\nfunction _iterableToArray(r) { if (\"undefined\" != typeof Symbol && null != r[Symbol.iterator] || null != r[\"@@iterator\"]) return Array.from(r); }\nfunction _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2)); }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\nfunction _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }\nfunction _nonIterableRest() { throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\"); }\nfunction _unsupportedIterableToArray(r, a) { if (r) { if (\"string\" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return \"Object\" === t && r.constructor && (t = r.constructor.name), \"Map\" === t || \"Set\" === t ? Array.from(r) : \"Arguments\" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }\nfunction _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }\nfunction _iterableToArrayLimit(r, l) { var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t[\"return\"] && (u = t[\"return\"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }\nfunction _arrayWithHoles(r) { if (Array.isArray(r)) return r; }\n;\n\n\n\n\n\n\n\nfunction TabScanner(_ref) {\n var sectionId = _ref.sectionId,\n sectionData = _ref.sectionData,\n isActive = _ref.isActive;\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null),\n _useState2 = _slicedToArray(_useState, 2),\n scannerStatus = _useState2[0],\n setScannerStatus = _useState2[1];\n var prevStatusRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);\n var _useState3 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null),\n _useState4 = _slicedToArray(_useState3, 2),\n currentStage = _useState4[0],\n setCurrentStage = _useState4[1];\n var _useState5 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]),\n _useState6 = _slicedToArray(_useState5, 2),\n completedStages = _useState6[0],\n setCompletedStages = _useState6[1];\n // Progress (0-100%) of the currently running stage only, plus the raw offset/total it was\n // computed from. `null` percent means the backend couldn't determine a total for this stage\n // (e.g. quick stages like clean_results), so the UI shows an indeterminate progress state.\n var _useState7 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({\n offset: null,\n total: null,\n percent: null,\n detail: null\n }),\n _useState8 = _slicedToArray(_useState7, 2),\n currentStageProgress = _useState8[0],\n setCurrentStageProgress = _useState8[1];\n var intervalRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);\n // Tracks whether the previous poll tick saw the scanner running, so we know to do one last\n // log refresh right after it finishes (the last tick's fetch may have missed the very last\n // rows written while the \"send_results\"/cure stages were wrapping up).\n var scanWasInProgressRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);\n var errorShownRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);\n var clearInFlightRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);\n var legacyHandlersBoundRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);\n var scanFinishedRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);\n var pollGenerationRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(0);\n var logScannerDebug = function logScannerDebug(_event, _extra) {};\n\n // Pulls the freshest scan log rows (written server-side to the DB by the background scan,\n // see CleantalkSP\\SpbctWP\\Scanner\\ScanningLog\\Repository::write()) and renders them into the\n // live \".spbc_log-wrapper\" block. This reuses the same AJAX action the historical, paginated\n // \"#spbcscan-results-log-module\" log uses to lazy-load older rows on scroll; here it's always\n // called with offset 0 to get the latest rows (Repository::getAll() orders by timestamp DESC).\n var fetchScanLog = function fetchScanLog() {\n jQuery.ajax({\n url: spbcSettings.ajaxurl,\n type: 'POST',\n data: {\n action: 'spbc_scanner_load_more_scan_logs',\n security: spbcSettings.ajax_nonce,\n no_cache: Math.random(),\n offset: 0\n },\n success: function success(data) {\n if (!data || !data.success || !data.data) {\n return;\n }\n if (!jQuery('.spbc_log-wrapper .panel-body').length) {\n jQuery('.spbc_log-wrapper').append('
');\n }\n jQuery('.spbc-scan-log-title').removeClass('spbc---hidden');\n jQuery('.spbc_log-wrapper').removeClass('spbc---hidden');\n jQuery('#spbc_log_actions_wrapper').removeClass('spbc---hidden');\n jQuery('.spbc_log-wrapper .panel-body').html(data.data);\n }\n });\n };\n var fetchScannerStatus = function fetchScannerStatus() {\n var generation = pollGenerationRef.current;\n var data = {\n action: 'spbc_scanner__getScannerStatus'\n };\n var params = {\n callback: function callback(result) {\n if (generation !== pollGenerationRef.current) {\n return;\n }\n scannerStatusCallback(result);\n },\n errorOutput: function errorOutput(msg) {\n console.log(msg);\n }\n };\n spbcSendAJAXRequest(data, params);\n };\n\n // (Re)starts the polling interval. Used on mount and whenever a new scan is kicked off,\n // so polling reliably resumes even if it had been stopped after a previous scan ended.\n var startPolling = function startPolling() {\n clearInterval(intervalRef.current);\n pollGenerationRef.current += 1;\n errorShownRef.current = false;\n scanFinishedRef.current = false;\n window.spbcScanner = window.spbcScanner || {};\n window.spbcScanner.finished = false;\n\n // First request do immediately\n fetchScannerStatus();\n\n // Start polling to fetch the scanner state\n intervalRef.current = setInterval(fetchScannerStatus, 5000);\n };\n var escapeHtml = function escapeHtml(value) {\n return String(value == null ? '' : value).replace(/&/g, '&').replace(//g, '>').replace(/\"/g, '"').replace(/'/g, ''');\n };\n var ackRuntimeError = function ackRuntimeError(errorId) {\n if (!errorId) {\n return;\n }\n spbcSendAJAXRequest({\n action: 'spbc_scanner__ackRuntimeError',\n error_id: errorId\n }, {\n callback: function callback() {},\n errorOutput: function errorOutput() {}\n });\n };\n var showScannerError = function showScannerError(message, stage, errorId) {\n var errorMsg = escapeHtml(message || 'Internal server error.');\n var text = stage ? errorMsg + ' Stage: ' + escapeHtml(stage) : errorMsg;\n if (typeof spbcModal !== 'undefined' && spbcModal.open) {\n spbcModal.open().putError(text);\n }\n ackRuntimeError(errorId);\n };\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n // Stop polling on the scanner ended or failed. Keep polling while paused.\n if (scannerStatus === 'error' || scannerStatus === 'end' && scanFinishedRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n // Reload after a confirmed finished scan. Resume can go paused → end\n // without an in_progress tick if the last chunk already completed.\n if ((prevStatusRef.current === 'in_progress' || prevStatusRef.current === 'paused') && scannerStatus === 'end') {\n if (scanFinishedRef.current) {\n logScannerDebug('reload_on_finished', {\n prev: prevStatusRef.current\n });\n window.location.reload();\n } else {\n logScannerDebug('skip_reload_unfinished_end', {\n prev: prevStatusRef.current\n });\n }\n }\n prevStatusRef.current = scannerStatus;\n }, [scannerStatus]);\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n window.spbcScanner = window.spbcScanner || {};\n window.spbcScanner.active = false;\n startPolling();\n // Cleanup on unmount\n return function () {\n return clearInterval(intervalRef.current);\n };\n }, []);\n function scannerStatusCallback(result) {\n var _result$data;\n if (result && result !== null && result !== void 0 && (_result$data = result.data) !== null && _result$data !== void 0 && _result$data.status) {\n var _result$data4;\n var status = result.data.status;\n if (status === 'error') {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n setScannerStatus('error');\n window.spbcScanner = window.spbcScanner || {};\n window.spbcScanner.active = false;\n scanWasInProgressRef.current = false;\n if (!errorShownRef.current) {\n errorShownRef.current = true;\n showScannerError(result.data.error, result.data.stage, result.data.error_id);\n }\n return;\n }\n var finished = !!result.data.finished;\n scanFinishedRef.current = finished;\n window.spbcScanner = window.spbcScanner || {};\n window.spbcScanner.finished = finished;\n if (status === 'end' && !finished) {\n var _result$data2;\n logScannerDebug('ignore_end_not_finished', {\n stage: (result === null || result === void 0 || (_result$data2 = result.data) === null || _result$data2 === void 0 || (_result$data2 = _result$data2.current_stage) === null || _result$data2 === void 0 ? void 0 : _result$data2.stage) || null\n });\n return;\n }\n if (prevStatusRef.current !== status) {\n var _result$data3;\n logScannerDebug('status_change', {\n from: prevStatusRef.current,\n to: status,\n stage: (result === null || result === void 0 || (_result$data3 = result.data) === null || _result$data3 === void 0 || (_result$data3 = _result$data3.current_stage) === null || _result$data3 === void 0 ? void 0 : _result$data3.stage) || null,\n finished: finished\n });\n }\n setScannerStatus(status);\n if (result !== null && result !== void 0 && (_result$data4 = result.data) !== null && _result$data4 !== void 0 && (_result$data4 = _result$data4.current_stage) !== null && _result$data4 !== void 0 && _result$data4.stage) {\n var _result$data5, _result$data$current_, _result$data6, _result$data$current_2, _result$data7, _result$data$current_3, _result$data8, _result$data$current_4, _result$data9;\n setCurrentStage(result.data.current_stage.stage);\n setCompletedStages(((result === null || result === void 0 || (_result$data5 = result.data) === null || _result$data5 === void 0 ? void 0 : _result$data5.completed_stages) || []).map(function (stage) {\n return stage.stage;\n }));\n setCurrentStageProgress({\n offset: (_result$data$current_ = result === null || result === void 0 || (_result$data6 = result.data) === null || _result$data6 === void 0 || (_result$data6 = _result$data6.current_stage) === null || _result$data6 === void 0 ? void 0 : _result$data6.offset) !== null && _result$data$current_ !== void 0 ? _result$data$current_ : null,\n total: (_result$data$current_2 = result === null || result === void 0 || (_result$data7 = result.data) === null || _result$data7 === void 0 || (_result$data7 = _result$data7.current_stage) === null || _result$data7 === void 0 ? void 0 : _result$data7.total) !== null && _result$data$current_2 !== void 0 ? _result$data$current_2 : null,\n percent: (_result$data$current_3 = result === null || result === void 0 || (_result$data8 = result.data) === null || _result$data8 === void 0 || (_result$data8 = _result$data8.current_stage) === null || _result$data8 === void 0 ? void 0 : _result$data8.percent) !== null && _result$data$current_3 !== void 0 ? _result$data$current_3 : null,\n detail: (_result$data$current_4 = result === null || result === void 0 || (_result$data9 = result.data) === null || _result$data9 === void 0 || (_result$data9 = _result$data9.current_stage) === null || _result$data9 === void 0 ? void 0 : _result$data9.detail) !== null && _result$data$current_4 !== void 0 ? _result$data$current_4 : null\n });\n }\n if (status === 'in_progress' || status === 'paused') {\n scanWasInProgressRef.current = true;\n window.spbcScanner.active = status === 'in_progress';\n fetchScanLog();\n } else if (scanWasInProgressRef.current) {\n // Scan just finished: refresh once more to catch the last rows written\n // while the scan was wrapping up (e.g. the \"send_results\" stage duration entry).\n scanWasInProgressRef.current = false;\n window.spbcScanner.active = false;\n fetchScanLog();\n }\n } else {\n // Stop polling if the wrong answer coming\n clearInterval(intervalRef.current);\n setScannerStatus(null);\n setCurrentStage(null);\n setCompletedStages([]);\n setCurrentStageProgress({\n offset: null,\n total: null,\n percent: null,\n detail: null\n });\n }\n }\n\n /** **************** SUPPORT TEMPORARY BOTH CONTENT FETCHED - LEGACY AND NEW *****************/\n var _useState9 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({\n sectionData: sectionData\n }),\n _useState0 = _slicedToArray(_useState9, 2),\n sectionDataLegacy = _useState0[0],\n setSectionDataLegacy = _useState0[1];\n var _useState1 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false),\n _useState10 = _slicedToArray(_useState1, 2),\n scannerDataLegacyFetched = _useState10[0],\n setSectionDataLegacyFetched = _useState10[1];\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n fetchOldContent();\n }, []);\n var fetchOldContent = /*#__PURE__*/function () {\n var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {\n var requestData, data, _t;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.p = _context.n) {\n case 0:\n _context.p = 0;\n requestData = {\n action: 'spbc_settings__draw_elements',\n tab_name: sectionId\n };\n _context.n = 1;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__.sendAjaxRequest)(requestData);\n case 1:\n data = _context.v;\n setSectionDataLegacy(data);\n setSectionDataLegacyFetched(true);\n document.dispatchEvent(new CustomEvent('spbctTabLoaded', {\n bubbles: true,\n detail: {\n tab: sectionId\n }\n }));\n _context.n = 3;\n break;\n case 2:\n _context.p = 2;\n _t = _context.v;\n console.error('Error fetching content:', _t);\n setSectionDataLegacy('');\n setSectionDataLegacyFetched(true);\n case 3:\n return _context.a(2);\n }\n }, _callee, null, [[0, 2]]);\n }));\n return function fetchOldContent() {\n return _ref2.apply(this, arguments);\n };\n }();\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n if (!scannerDataLegacyFetched) {\n return;\n }\n if (jQuery('#spbcscan-results-log-module').length) {\n jQuery('#spbc_log_actions_wrapper').removeClass('spbc---hidden');\n }\n }, [scannerDataLegacyFetched, sectionDataLegacy]);\n if (!scannerDataLegacyFetched) {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Preloader_Preloader__WEBPACK_IMPORTED_MODULE_4__[\"default\"], null);\n }\n /** **************** END SUPPORT TEMPORARY BOTH CONTENT FETCHED *****************/\n\n /** **** L E G A C Y ******/\n\n /**\n * Show popup tip\n *\n * @param {object} showIcon\n */\n window.spbcPopupTipShow = function (showIcon) {\n // eslint-disable-line no-unused-vars\n var tipTitle = showIcon.attr('spbc_tip_title') || null;\n var tipText = showIcon.attr('spbc_tip_text') || null;\n var _removeDescFunc = function removeDescFunc(e) {\n if (typeof e === 'undefined' || jQuery(e.target).parent('.spbc_popup_tip--wrapper').length == 0 || jQuery(e.target).hasClass('spbc_popup_tip--icon---cancel')) {\n jQuery('.spbc_popup_tip--wrapper').hide();\n jQuery(document).off('click', _removeDescFunc);\n }\n };\n _removeDescFunc();\n showIcon.after('');\n var obj = jQuery('.spbc_popup_tip--wrapper');\n obj.empty().append('').css({\n top: showIcon.position().top - 2,\n left: showIcon.position().left + 25\n });\n obj.append('').append('').append('');\n jQuery(document).on('click', _removeDescFunc);\n showIcon.parents('tr').on('mouseout', _removeDescFunc);\n };\n\n /**\n * View file in the scanner\n *\n * @param {object} obj\n */\n window.spbcScannerButtonFileViewEvent = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var data = {\n action: 'spbc_scanner_file_view',\n file_id: self.parents('.row-actions').attr('uid')\n };\n var params = {\n spinner: self.parent().siblings('.tbl-preloader--tiny'),\n callback: spbcScannerButtonViewCallback,\n errorOutput: function errorOutput(msg) {\n spbcModal.open().putError(msg);\n document.addEventListener('spbcModalClosed', function (e) {\n spbcReloadAccordion();\n });\n }\n };\n spbcSendAJAXRequest(data, params);\n };\n\n /**\n * Delete file from analysis log\n *\n * @param {object} obj\n */\n window.spbcScannerAnalysisLogDeleteFromLog = function (obj) {\n // eslint-disable-line no-unused-vars\n var fileIds = [];\n var self = jQuery(obj);\n var spinner;\n if (jQuery(obj).hasClass('tbl-row_action')) {\n fileIds.push(self.parents('.row-actions').attr('uid'));\n spinner = self.parent().siblings('.tbl-preloader--tiny');\n } else {\n var selectedItems = self.closest('#spbc_tbl__scanner_analysis_log').find('.cb-select:checked');\n if (selectedItems.length === 0) {\n alert('Please, select elements.');\n return;\n }\n selectedItems.each(function (index, element) {\n var elementId = jQuery(element).val();\n fileIds.push(elementId);\n });\n spinner = self.children('.tbl-preloader--small');\n }\n var data = {\n action: 'spbc_scanner_analysis_log_delete_from_log',\n file_ids: fileIds\n };\n var params = {\n spinner: spinner,\n callback: function callback() {\n window.location.reload();\n }\n };\n spbcSendAJAXRequest(data, params);\n };\n\n /**\n * View page in the scanner\n *\n * @param {object} obj\n */\n window.spbcScannerButtonPageViewEvent = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var data = {\n action: 'spbc_scanner_page_view',\n page_url: self.parents('.row-actions').attr('uid')\n };\n var params = {\n spinner: self.parent().siblings('.tbl-preloader--tiny'),\n callback: spbcScannerButtonViewCallback,\n errorOutput: function errorOutput(msg) {\n spbcModal.open().putError(msg);\n }\n };\n var wpWrap = jQuery('#wpwrap');\n var dialogWindow = jQuery('#spbc_dialog');\n dialogWindow.dialog({\n modal: true,\n title: 'Loading..',\n position: {\n my: 'center top',\n at: 'center top+100px',\n of: window\n },\n width: +(wpWrap.width() / 100 * 80),\n show: {\n effect: 'blind',\n duration: 500\n },\n draggable: false,\n resizable: false,\n closeText: 'X',\n classes: {\n 'ui-dialog': 'spbc---top'\n },\n open: function open(event, ui) {\n event.target.style.overflow = 'auto';\n jQuery('#spbc_dialog').height(document.documentElement.clientHeight / 100 * 25);\n },\n beforeClose: function beforeClose(event, ui) {\n document.body.style.overflow = 'auto';\n jQuery('#spbc_dialog').empty();\n }\n });\n dialogWindow.append(' ');\n var spinner = jQuery('#spbc_file_view_preloader');\n var sizeMultiplier = wpWrap.width() * 0.0004;\n spinner.height(128 * sizeMultiplier);\n spinner.width(128 * sizeMultiplier);\n spinner.css({\n left: dialogWindow.width() / 2 - 128 * sizeMultiplier / 2\n });\n spinner.css({\n top: dialogWindow.height() / 2 - 128 * sizeMultiplier / 2\n });\n spbcSendAJAXRequest(data, params);\n };\n\n /**\n * Callback for file view\n *\n * @param {object} result\n */\n function spbcScannerButtonViewCallback(result) {\n // eslint-disable-line no-unused-vars\n var dialogWindow = jQuery('#spbc_dialog');\n dialogWindow.empty();\n jQuery('#spbc_file_view_preloader').css({\n display: 'none'\n });\n var rowTemplate = '%s ';\n rowTemplate += '
%s
';\n\n /* eslint-disable guard-for-in */\n for (var row in result.file) {\n dialogWindow.append(rowTemplate.printf(row, result.file[row]));\n }\n var contentHeight = Object.keys(result.file).length * 19 < 76 ? 76 : Object.keys(result.file).length * 19;\n var visibleHeight = document.documentElement.clientHeight / 100 * 75;\n var overflow = contentHeight < visibleHeight ? 'hidden' : 'scroll';\n var height = overflow === 'scroll' ? visibleHeight : contentHeight;\n dialogWindow.css({\n height: height,\n overflow: overflow\n });\n dialogWindow.dialog({\n modal: true,\n title: 'Loaded: ' + result.file_path,\n position: {\n my: 'center top',\n at: 'center top+100px',\n of: window\n },\n width: +(jQuery('#wpwrap').width() / 100 * 80),\n show: {\n effect: 'blind',\n duration: 500\n },\n draggable: true,\n resizable: false,\n height: height,\n closeText: 'X',\n classes: {\n 'ui-dialog': 'spbc---top'\n },\n open: function open(event, ui) {\n event.target.style.overflow = overflow;\n jQuery('#spbc_dialog').height(height);\n jQuery('.ui-widget-overlay').on('click', function () {\n jQuery('#spbc_dialog').dialog('close');\n });\n },\n beforeClose: function beforeClose(event, ui) {\n document.body.style.overflow = 'auto';\n jQuery('#spbc_dialog').empty();\n }\n });\n console.log('FILE_VIEWED');\n }\n\n /**\n * Event for file view bad\n *\n * @param {object} obj\n */\n window.spbcScannerButtonFileViewBadEvent = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var data = {\n action: 'spbc_scanner_file_view',\n file_id: self.parents('.row-actions').attr('uid')\n };\n var params = {\n spinner: self.parent().siblings('.tbl-preloader--tiny'),\n callback: spbcScannerButtonViewBadFileCallback,\n errorOutput: function errorOutput(msg) {\n spbcModal.open().putError(msg);\n }\n };\n spbcSendAJAXRequest(data, params);\n };\n\n /**\n * Event for page view bad\n *\n * @param {object} obj\n */\n window.spbcScannerButtonPageViewBadEvent = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var data = {\n action: 'spbc_scanner_page_view',\n page_url: self.parent().attr('uid')\n };\n var params = {\n spinner: self.parent().siblings('.tbl-preloader--tiny'),\n callback: spbcScannerButtonViewBadCallback,\n errorOutput: function errorOutput(msg) {\n spbcModal.open().putError(msg);\n }\n };\n spbcSendAJAXRequest(data, params);\n };\n\n /**\n * Callback for file view bad\n *\n * @param {object} result\n * @param {object} data\n * @param {object} params\n */\n function spbcScannerButtonViewBadFileCallback(result, data, params) {\n // eslint-disable-line no-unused-vars\n console.log('FILE_VIEWED_BAD_CODE_FILE');\n jQuery('#spbc_dialog').empty();\n var rowTemplate = '%s ';\n rowTemplate += '
%s
';\n var rowTemplateBad = '';\n rowTemplateBad += '
%s ';\n rowTemplateBad += '
%s
';\n var severityParts = [];\n var severityPartsSorted = [];\n var severityKeysSorted = [];\n result.weak_spots = JSON.parse(result.weak_spots);\n /* eslint-disable guard-for-in */\n for (var severity in result.weak_spots) {\n severityParts[severity] = Object.keys(result.weak_spots[severity]);\n }\n severityKeysSorted = Object.keys(severityParts).sort();\n\n /* eslint-disable guard-for-in */\n for (var key in severityKeysSorted) {\n severityPartsSorted[severityKeysSorted[key]] = severityParts[severityKeysSorted[key]];\n }\n\n /* eslint-disable guard-for-in */\n for (var severityPart in severityPartsSorted) {\n var headerName = 'spbc-dialog-row-header-' + severityPart.toLowerCase();\n var headerHtml = '';\n headerHtml += severityPart + ' weakspots: ';\n var dialog = jQuery('#spbc_dialog');\n dialog.append(rowTemplate.printf('', headerHtml));\n dialog.append(rowTemplate.printf('... ', '... '));\n var header = dialog.find('#' + headerName);\n header.parent().parent().css('text-align', 'center');\n header.parent().parent().css('border', '1px dotted');\n header.parent().parent().css('background', 'darkgrey');\n\n /* eslint-disable guard-for-in */\n for (var strNum in severityParts[severityPart]) {\n // Sort bad line\n strNum = _toConsumableArray(new Set(strNum)).sort();\n var row = +severityParts[severityPart][strNum];\n var displayDepth = 2;\n var temp = void 0;\n // Display only existing lines of file\n for (var currRow = row - displayDepth; currRow <= row + displayDepth; currRow++) {\n if (typeof result.file[currRow] !== 'undefined') {\n temp = row === currRow ? rowTemplateBad : rowTemplate;\n dialog.append(temp.printf(currRow, result.file[currRow]));\n }\n }\n\n // Placing an empty string between results\n dialog.append(rowTemplate.printf('... ', '... '));\n }\n }\n\n // Removing last empty string\n jQuery('#spbc_dialog div').last().remove();\n var contentHeight = jQuery('#spbc_dialog div').length * 19 < 76 ? 76 : jQuery('#spbc_dialog div').length * 19;\n var visibleHeight = document.documentElement.clientHeight / 100 * 75;\n var overflow = contentHeight < visibleHeight ? 'no_scroll' : 'scroll';\n var height = overflow === 'scroll' ? visibleHeight : contentHeight;\n jQuery('#spbc_dialog').dialog({\n modal: true,\n title: result.file_path,\n position: {\n my: 'center top',\n at: 'center top+100px',\n of: window\n },\n width: +(jQuery('#wpwrap').width() / 100 * 80),\n show: {\n effect: 'blind',\n duration: 500\n },\n draggable: true,\n resizable: false,\n closeText: 'X',\n classes: {\n 'ui-dialog': 'spbc---top'\n },\n open: function open(event, ui) {\n event.target.style.overflow = overflow;\n jQuery('#spbc_dialog').height(height);\n jQuery('.ui-widget-overlay').on('click', function () {\n jQuery('#spbc_dialog').dialog('close');\n });\n },\n beforeClose: function beforeClose(event, ui) {\n document.body.style.overflow = 'auto';\n jQuery('#spbc_dialog').empty();\n }\n });\n }\n\n /**\n * Callback for file view bad\n *\n * @param {object} result\n * @param {object} data\n * @param {object} params\n */\n function spbcScannerButtonViewBadCallback(result, data, params) {\n // eslint-disable-line no-unused-vars\n console.log('FILE_VIEWED_BAD_CODE');\n var afterClose = function afterClose() {};\n if (result.success == true) {\n console.log(result);\n var rowTemplate = '';\n var rowTemplateBad = '';\n var strNums = [];\n result.weak_spots = JSON.parse(result.weak_spots);\n /* eslint-disable guard-for-in */\n for (var severity in result.weak_spots) {\n strNums = strNums.concat(Object.keys(result.weak_spots[severity]));\n }\n strNums = _toConsumableArray(new Set(strNums)).sort(); // Sort bad line\n // get a map of bad strings\n // TODO: Probably there can get something else except CRITICAL in the future\n var badStringsMap = new Map(Object.entries(result.weak_spots.CRITICAL));\n /* eslint-disable guard-for-in */\n for (var strNum in strNums) {\n if (strNum == 0 && badStringsMap.get(strNums[strNum]).toLowerCase().includes('doctype')) {\n continue;\n }\n var row = +strNums[strNum];\n var displayDepth = 2;\n\n // Display only existing lines of file\n for (var currRow = row - displayDepth; currRow <= row + displayDepth; currRow++) {\n if (typeof result.file[currRow] !== 'undefined') {\n var isBadString = row === currRow;\n var unformattedRow = void 0;\n var highlitedRow = void 0;\n if (isBadString) {\n unformattedRow = rowTemplateBad;\n } else {\n unformattedRow = rowTemplate;\n }\n if (isBadString && !result.file[currRow].includes('iframe')) {\n highlitedRow = unformattedRow.printf(currRow, badStringsMap.get(strNums[strNum]));\n } else {\n highlitedRow = unformattedRow.printf(currRow, result.file[currRow]);\n }\n jQuery('#spbc_dialog').append(highlitedRow);\n }\n }\n\n // Placing an empty string between results\n jQuery('#spbc_dialog').append(rowTemplate.printf('', ''));\n\n // Removing last empty string\n jQuery('#spbc_dialog div').last().remove();\n }\n } else {\n var wrapperElement = '%s
';\n jQuery('#spbc_dialog').append(wrapperElement.printf(result.content));\n afterClose = function afterClose() {\n document.location.reload();\n };\n }\n var contentHeight = jQuery('#spbc_dialog div').length * 19 < 76 ? 76 : jQuery('#spbc_dialog div').length * 19;\n var visibleHeight = document.documentElement.clientHeight / 100 * 75;\n var height = 'auto';\n var overflow = contentHeight < visibleHeight ? 'no_scroll' : 'scroll';\n jQuery('#spbc_dialog').dialog({\n modal: true,\n title: result.file_path,\n position: {\n my: 'center top',\n at: 'center top+100px',\n of: window\n },\n width: +(jQuery('#wpwrap').width() / 100 * 80),\n show: {\n effect: 'blind',\n duration: 500\n },\n draggable: true,\n resizable: false,\n closeText: 'X',\n classes: {\n 'ui-dialog': 'spbc---top'\n },\n open: function open(event, ui) {\n event.target.style.overflow = overflow;\n jQuery('#spbc_dialog').height(height);\n jQuery('.ui-widget-overlay').on('click', function () {\n jQuery('#spbc_dialog').dialog('close');\n });\n },\n beforeClose: function beforeClose(event, ui) {\n document.body.style.overflow = 'auto';\n jQuery('#spbc_dialog').empty();\n },\n close: afterClose\n });\n }\n\n /**\n * Cure file\n * @param {object} obj\n */\n window.spbcScannerButtonCureFileAjaxHandler = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var fileFastHash = self.parents('.row-actions').attr('uid');\n var data = {\n action: 'spbc_file_cure_ajax_action',\n file_fast_hash: fileFastHash,\n security: spbcSettings.ajax_nonce\n };\n jQuery.ajax({\n type: 'POST',\n async: true,\n url: spbcSettings.ajaxurl,\n data: data,\n success: function success(result) {\n if (result.success) {\n spbcModal.open().put(result.data);\n document.addEventListener('spbcModalClosed', function (e) {\n document.location.reload();\n });\n } else {\n spbcModal.open().putError(result.data);\n }\n }\n });\n };\n\n /**\n * Restore file from backup\n * @param {object} obj\n */\n window.spbcScannerButtonRestoreFromBackupAjaxHandler = function (obj) {\n // eslint-disable-line no-unused-vars\n var self = jQuery(obj);\n var fileFastHash = self.parents('.row-actions').attr('uid');\n var data = {\n action: 'spbc_restore_file_from_backup_ajax_action',\n file_fast_hash: fileFastHash,\n security: spbcSettings.ajax_nonce\n };\n jQuery.ajax({\n type: 'POST',\n async: true,\n url: spbcSettings.ajaxurl,\n data: data,\n success: function success(result) {\n if (result.success) {\n spbcModal.open().put(result.data);\n } else {\n spbcModal.open().putError(result.data);\n }\n document.addEventListener('spbcModalClosed', function (e) {\n document.location.reload();\n });\n }\n });\n };\n\n /**\n * Show copy log to clipboard hint\n * @param {string} text\n */\n function spbcScannerCopyLogToClipboardHint(text) {\n jQuery('#spbc_scanner_copy_log_to_clipboard_hint').text(text);\n jQuery('#spbc_scanner_copy_log_to_clipboard_hint').show();\n jQuery('#spbc_scanner_copy_log_to_clipboard_hint').animate({\n opacity: 0\n }, 3000, function () {\n jQuery(this).hide();\n jQuery(this).css('opacity', 1);\n });\n }\n if (!legacyHandlersBoundRef.current) {\n legacyHandlersBoundRef.current = true;\n jQuery(document).ready(function () {\n // EVENT HADLING\n spbcTblBulkActionsListen(); // Table. Row bulk handler\n spbcTblRowActionsListen(); // Table. Row actions handler\n spbcTblPaginationListen(); // Table. Pagination handler\n spbcTblSortListen(); // Table. Sort handler\n\n spbcStartShowHide();\n jQuery(document).off('click.spbcPopupTip', '.spbc_popup_tip--icon---show');\n jQuery(document).on('click.spbcPopupTip', '.spbc_popup_tip--icon---show', function () {\n spbcPopupTipShow(jQuery(this));\n });\n jQuery('#spbc_perform_scan').off('click.spbcScanLog').on('click.spbcScanLog', function () {\n jQuery('#spbcscan-results-log-module').remove();\n // spbcScanner.actionControl();\n\n if (!jQuery('.spbc_log-wrapper .panel-body').length) {\n jQuery('.spbc_log-wrapper').append('
');\n }\n jQuery('.spbc_log-wrapper').resizable({\n handles: 'n, s',\n containment: 'document'\n });\n });\n jQuery('#spbcscan-results-log-module').resizable({\n handles: 'n, s',\n containment: 'document'\n });\n logActionsHandleClearLogs();\n logActionsHandleMoreLogs();\n logActionsHandleCopyToClipboard();\n });\n }\n function logActionsHandleClearLogs() {\n jQuery(document).off('click.spbcScannerClear', '#spbc_scanner_clear');\n jQuery(document).on('click.spbcScannerClear', '#spbc_scanner_clear', function (e) {\n e.preventDefault();\n if (clearInFlightRef.current) {\n return;\n }\n if (!confirm(spbcSettings.clear_results_confirm)) {\n return;\n }\n clearInFlightRef.current = true;\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n spbcSendAJAXRequest({\n action: 'spbc_scanner_clear'\n }, {\n timeout: 40000,\n callback: function callback() {\n document.location.reload();\n },\n errorOutput: function errorOutput(msg) {\n clearInFlightRef.current = false;\n startPolling();\n console.log(msg);\n }\n });\n });\n }\n function logActionsHandleCopyToClipboard() {\n jQuery(document).off('click.spbcScannerCopy', '#spbc_scanner_copy_log_to_clipboard');\n jQuery(document).on('click.spbcScannerCopy', '#spbc_scanner_copy_log_to_clipboard', function (e) {\n e.preventDefault();\n if (!navigator.clipboard) {\n spbcScannerCopyLogToClipboardHint(spbcScaner.copy_log_to_clipboard_hint_unsupported);\n return;\n }\n var $log = jQuery('#spbcscan-results-log-module .panel-body');\n if (!$log.length) {\n $log = jQuery('.spbc_log-wrapper .panel-body');\n }\n var log = $log.html() || '';\n var formattedLog = log.replace(//g, '').replace(/<\\/b>/g, '');\n formattedLog = formattedLog.replace(/ /g, '\\n').replace(/<\\/p>/g, '');\n navigator.clipboard.writeText(formattedLog).then(function () {\n spbcScannerCopyLogToClipboardHint(spbcScaner.copy_log_to_clipboard_hint);\n })[\"catch\"](function () {\n spbcScannerCopyLogToClipboardHint(spbcScaner.copy_log_to_clipboard_hint_failed);\n });\n });\n }\n function logActionsHandleMoreLogs() {\n var element = jQuery('#spbcscan_results_log_body');\n var logAppendPreloader = jQuery('#spbcscan_results_log_preloader');\n if (!element || !logAppendPreloader) {\n return;\n }\n element.on('scroll', function () {\n // Check if load completed\n if (jQuery(this).data('stop-load')) {\n return;\n }\n\n // Is end of scroll\n var isAtBottom = this.scrollHeight - this.scrollTop <= this.clientHeight + 1;\n if (isAtBottom) {\n var $this = jQuery(this);\n var currentOffset = parseInt($this.data('offset')) || 0;\n logAppendPreloader.css('visibility', 'visible');\n jQuery.ajax({\n url: spbcSettings.ajaxurl,\n type: 'POST',\n data: {\n action: 'spbc_scanner_load_more_scan_logs',\n security: spbcSettings.ajax_nonce,\n no_cache: Math.random(),\n offset: currentOffset\n },\n xhrFields: {\n responseType: 'text'\n },\n success: function success(data) {\n if (data.success && data.data) {\n // increase and set new offset\n var newOffset = currentOffset + 100;\n $this.data('offset', newOffset);\n\n // add html to the log\n jQuery('#spbcscan_results_log_body').append(data.data);\n\n // if no data - stop load\n if (data.data.length === 0 || data.data.trim() === '') {\n $this.data('stop-load', 1);\n }\n } else {\n $this.data('stop-load', 1);\n }\n logAppendPreloader.css('visibility', 'hidden');\n },\n error: function error(xhr, status, _error) {\n logAppendPreloader.css('visibility', 'hidden');\n $this.data('stop-load', 1);\n }\n });\n }\n });\n }\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, window.spbcSettings && window.spbcSettings.critical > 0 && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_NoticeBanner_NoticeBanner__WEBPACK_IMPORTED_MODULE_5__[\"default\"], null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n className: \"spbc_wrapper_field\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ScannerInfo_ScannerInfo__WEBPACK_IMPORTED_MODULE_6__[\"default\"], {\n sectionData: sectionData,\n scannerStatus: scannerStatus,\n currentStage: currentStage,\n completedStages: completedStages,\n currentStageProgress: currentStageProgress,\n onScanStarted: startPolling\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ScannerAccordions__WEBPACK_IMPORTED_MODULE_7__[\"default\"], {\n scanning: scannerStatus === 'in_progress' || scannerStatus === 'paused'\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_TabContentTypes_Html_TabContentHtml__WEBPACK_IMPORTED_MODULE_2__[\"default\"], {\n sectionId: sectionId,\n sectionData: sectionDataLegacy,\n isActive: isActive\n })));\n}\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabScanner/TabScanner.js?\n}");
+
+/***/ },
+
+/***/ "./js/src/react/components/TabContent/TabScanner/scannerActions.js"
+/*!*************************************************************************!*\
+ !*** ./js/src/react/components/TabContent/TabScanner/scannerActions.js ***!
+ \*************************************************************************/
+(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
+
+eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ runBulkAction: () => (/* binding */ runBulkAction),\n/* harmony export */ runRowAction: () => (/* binding */ runRowAction)\n/* harmony export */ });\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2)); }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\nfunction _typeof(o) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && \"function\" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? \"symbol\" : typeof o; }, _typeof(o); }\n/**\n * Scanner accordion actions. Same AJAX endpoints as the legacy table handlers.\n */\n\nfunction noConfirmConfig() {\n var empty = {\n any: [],\n restricted: [],\n defaults: []\n };\n try {\n if (typeof spbcPublic === 'undefined' || !spbcPublic.no_confirm_row_actions) {\n return empty;\n }\n var parsed = JSON.parse(spbcPublic.no_confirm_row_actions);\n if (!Array.isArray(parsed.any)) {\n return empty;\n }\n return parsed;\n } catch (e) {\n return empty;\n }\n}\nfunction textFromHtml(html) {\n var div = document.createElement('div');\n div.innerHTML = html || '';\n return (div.textContent || '').replace(/\\s+/g, ' ').trim();\n}\nfunction scannerIsBusy() {\n if (window.spbcScanner && window.spbcScanner.active) {\n var message = window.spbcTableLocalize && window.spbcTableLocalize.scannerIsActive || 'Scanner is active for now. Try later.';\n alert(message);\n return true;\n }\n return false;\n}\nfunction confirmAction(action, fileName) {\n var table = window.spbcTable || {};\n var header = table['warning_h_' + action] || table.warning_default || 'Do you want to proceed?';\n var text = table['warning_t_' + action] || '';\n var skips = noConfirmConfig().any.indexOf(action) !== -1;\n if (skips) {\n return Promise.resolve(true);\n }\n return new Promise(function (resolve) {\n if (window.spbcModal && window.spbcModal.open) {\n var yes = action === 'quarantine' ? 'Quarantine' : 'Yes';\n var no = action === 'quarantine' ? 'Cancel' : 'No';\n window.spbcModal.open().confirm(header, text, fileName, function (ok) {\n return resolve(!!ok);\n }, yes, no);\n } else {\n resolve(window.confirm(header + (text ? '\\n' + text : '')));\n }\n });\n}\n\n/**\n * Text from wp_send_json_error(). Structured cure/restore reports are left for their own handlers.\n *\n * @param {*} data Response data.\n * @return {string} Error text, or an empty string when the caller should handle the payload.\n */\nfunction wordpressErrorText(data) {\n if (typeof data === 'string') {\n return data;\n }\n if (!data || _typeof(data) !== 'object') {\n return '';\n }\n if (data.failed_to_cure || data.failed_to_restore) {\n return '';\n }\n if (typeof data.error === 'string') {\n return data.error;\n }\n if (typeof data.message === 'string') {\n return data.message;\n }\n return '';\n}\nfunction post(data, timeout) {\n return new Promise(function (resolve, reject) {\n jQuery.ajax({\n type: 'POST',\n url: spbcSettings.ajaxurl,\n data: Object.assign({\n security: spbcSettings.ajax_nonce\n }, data),\n timeout: timeout || 25000,\n success: function success(result) {\n if (result && result.error) {\n var comment = result.error_comment ? ' ' + result.error_comment : '';\n if (window.spbcModal && window.spbcModal.open) {\n window.spbcModal.open().putError(String(result.error) + comment);\n }\n reject(new Error('action error'));\n return;\n }\n if (result && result.success === false) {\n var message = wordpressErrorText(result.data);\n var structuredReport = result.data && _typeof(result.data) === 'object' && !message;\n if (!structuredReport) {\n if (window.spbcModal && window.spbcModal.open) {\n window.spbcModal.open().putError(message || 'Request failed');\n }\n reject(new Error('action error'));\n return;\n }\n }\n resolve(result);\n },\n error: function error() {\n if (window.spbcModal && window.spbcModal.open) {\n window.spbcModal.open().putError('Request failed');\n }\n reject(new Error('request failed'));\n }\n });\n });\n}\nfunction fakeActionNode(uid, pageId) {\n var wrap = document.createElement('div');\n wrap.className = 'row-actions';\n wrap.setAttribute('uid', uid || '');\n if (pageId) {\n wrap.setAttribute('page_id', pageId);\n }\n var el = document.createElement('span');\n el.className = 'tbl-row_action';\n wrap.appendChild(el);\n return el;\n}\nfunction customKind(handler) {\n if (!handler) {\n return null;\n }\n if (handler.indexOf('FileViewBad') !== -1) {\n return 'file_view_bad';\n }\n if (handler.indexOf('PageViewBad') !== -1) {\n return 'page_view_bad';\n }\n if (handler.indexOf('PageViewEvent') !== -1) {\n return 'page_view';\n }\n if (handler.indexOf('FileViewEvent') !== -1) {\n return 'file_view';\n }\n if (handler.indexOf('CureFile') !== -1) {\n return 'cure';\n }\n if (handler.indexOf('RestoreFromBackup') !== -1) {\n return 'restore_backup';\n }\n if (handler.indexOf('AnalysisLogDelete') !== -1) {\n return 'analysis_delete';\n }\n if (handler.indexOf('SwitchTable') !== -1) {\n return 'show_links';\n }\n return null;\n}\nfunction showActionResult(result) {\n if (!result) {\n return;\n }\n if (result.success === true && result.data && result.data.file_info) {\n var email = window.spbcSettings && window.spbcSettings.wl_support_email || 'support@cleantalk.org';\n window.prompt('Copy the file info below and send it to ' + email, result.data.file_info);\n return;\n }\n if (result.html && window.spbcModal && window.spbcModal.open) {\n window.spbcModal.open().put(result.html);\n }\n if (result.temp_html && window.spbcModal && window.spbcModal.open) {\n window.spbcModal.open().put(result.temp_html);\n }\n}\n\n/**\n * Run one scanner row action.\n *\n * @param {string} actionKey Action name.\n * @param {object} row Table row.\n * @param {object} actionDef Action definition from the table payload.\n * @param {object} options Optional flags. skipConfirm skips the confirm dialog.\n * @return {Promise} refresh, show_links, reload, or null.\n */\nfunction runRowAction(_x, _x2, _x3, _x4) {\n return _runRowAction.apply(this, arguments);\n}\nfunction _runRowAction() {\n _runRowAction = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(actionKey, row, actionDef, options) {\n var opts, kind, fileName, ok, node, handlers, timeout, result;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.n) {\n case 0:\n opts = options || {};\n if (!scannerIsBusy()) {\n _context.n = 1;\n break;\n }\n return _context.a(2, null);\n case 1:\n kind = customKind(actionDef && actionDef.handler);\n if (!(kind === 'show_links')) {\n _context.n = 2;\n break;\n }\n return _context.a(2, 'show_links');\n case 2:\n if (!(!kind && !opts.skipConfirm)) {\n _context.n = 4;\n break;\n }\n fileName = textFromHtml(row.cells && (row.cells.path || row.cells.url || row.cells.domain) || '');\n _context.n = 3;\n return confirmAction(actionKey, fileName);\n case 3:\n ok = _context.v;\n if (ok) {\n _context.n = 4;\n break;\n }\n return _context.a(2, null);\n case 4:\n if (!kind) {\n _context.n = 5;\n break;\n }\n node = fakeActionNode(row.uid, row.page_id);\n handlers = {\n file_view: window.spbcScannerButtonFileViewEvent,\n file_view_bad: window.spbcScannerButtonFileViewBadEvent,\n page_view: window.spbcScannerButtonPageViewEvent,\n page_view_bad: window.spbcScannerButtonPageViewBadEvent,\n cure: window.spbcScannerButtonCureFileAjaxHandler,\n restore_backup: window.spbcScannerButtonRestoreFromBackupAjaxHandler,\n analysis_delete: window.spbcScannerAnalysisLogDeleteFromLog\n };\n if (typeof handlers[kind] === 'function') {\n handlers[kind](node);\n }\n return _context.a(2, kind === 'file_view' || kind === 'file_view_bad' || kind === 'page_view' || kind === 'page_view_bad' ? null : 'reload');\n case 5:\n timeout = actionKey === 'delete' || actionKey === 'quarantine' ? 60000 : 25000;\n _context.n = 6;\n return post({\n action: 'spbc_tbl-action--row',\n add_action: actionKey,\n id: row.uid,\n cols: '1',\n page_url: row.uid,\n page_id: row.page_id || ''\n }, timeout);\n case 6:\n result = _context.v;\n showActionResult(result);\n return _context.a(2, 'refresh');\n }\n }, _callee);\n }));\n return _runRowAction.apply(this, arguments);\n}\nvar APPLY_TO_ALL = ['approve', 'disapprove', 'send', 'check_analysis_status', 'approve_page', 'disapprove_page', 'cure', 'delete_from_analysis_log', 'restore'];\n\n/**\n * Cure-log restore report. Same layout as the legacy bulk handler.\n *\n * @param {object} result AJAX response.\n */\nfunction showRestoreResult(result) {\n var displayData = result && result.data || {};\n var structured = displayData && _typeof(displayData) === 'object' && (Object.prototype.hasOwnProperty.call(displayData, 'failed_to_restore') || Object.prototype.hasOwnProperty.call(displayData, 'restored_on_request'));\n if (!structured) {\n if (result && result.success === false && window.spbcModal && window.spbcModal.open) {\n window.spbcModal.open().putError(typeof displayData === 'string' ? displayData : 'Request failed');\n } else if (displayData && displayData.message && window.spbcModal && window.spbcModal.open) {\n window.spbcModal.open().put(displayData.message);\n }\n return;\n }\n var failed = displayData.failed_to_restore || [];\n var html = '
' + (displayData.message || '') + '
' + '
Restored: ' + (displayData.restored_on_request || 0) + ', unchanged or already restored: ' + (displayData.skipped || 0) + ', failed to restore: ' + failed.length + '
' + (failed.length ? '
' + failed.join(' ') + '
' : '') + '
';\n if (window.spbcModal && window.spbcModal.open) {\n if (result && result.success === false) {\n window.spbcModal.open().putError(html);\n } else {\n window.spbcModal.open().put(html);\n }\n }\n}\nfunction showCureResult(result) {\n var displayData = result && result.data || {};\n var failed = displayData.failed_to_cure || [];\n var html = '' + (displayData.message || '') + '
' + '
Cured: ' + (displayData.cured_on_request || 0) + ', already cured: ' + (displayData.skipped || 0) + ', failed to cure: ' + failed.length + '
' + (failed.length ? '
' + failed.join(' ') + '
See more details in the threats log.
' : '') + '
';\n if (window.spbcModal && window.spbcModal.open) {\n if (result && result.success) {\n window.spbcModal.open().put(html);\n } else {\n window.spbcModal.open().putError(typeof displayData === 'string' ? displayData : html);\n }\n }\n}\n\n/**\n * @param {string} action\n * @param {object} table\n * @param {string[]} selectedIds\n * @param {boolean} allItems\n */\nfunction runBulkAction(_x5, _x6, _x7, _x8) {\n return _runBulkAction.apply(this, arguments);\n}\nfunction _runBulkAction() {\n _runBulkAction = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(action, table, selectedIds, allItems) {\n var rows, ids, config, warning, result, ajaxAction, _result, _result2, queue, i, def;\n return _regenerator().w(function (_context2) {\n while (1) switch (_context2.n) {\n case 0:\n if (!(!action || action === '-1' || scannerIsBusy())) {\n _context2.n = 1;\n break;\n }\n return _context2.a(2, null);\n case 1:\n rows = table.rows || [];\n ids = selectedIds;\n if (!(!allItems && !ids.length)) {\n _context2.n = 2;\n break;\n }\n alert('Please, select elements.');\n return _context2.a(2, null);\n case 2:\n if (!(allItems && APPLY_TO_ALL.indexOf(action) === -1)) {\n _context2.n = 3;\n break;\n }\n alert('This action is only permitted when you click the \"Apply to selected\" button. ' + 'Please be cautious in your selection.');\n return _context2.a(2, null);\n case 3:\n config = noConfirmConfig();\n if (!(config.any.indexOf(action) === -1)) {\n _context2.n = 4;\n break;\n }\n warning = window.spbcTable && window.spbcTable.warning_bulk || 'Are sure you want to perform these actions?';\n if (window.confirm(warning)) {\n _context2.n = 4;\n break;\n }\n return _context2.a(2, null);\n case 4:\n if (!(action === 'cure')) {\n _context2.n = 6;\n break;\n }\n _context2.n = 5;\n return post(allItems ? {\n action: 'spbc_cure_selected',\n apply_to_all: 1,\n status: table.type\n } : {\n action: 'spbc_cure_selected',\n selectedIds: ids\n }, 60000);\n case 5:\n result = _context2.v;\n showCureResult(result);\n return _context2.a(2, 'refresh');\n case 6:\n if (!(action === 'restore')) {\n _context2.n = 8;\n break;\n }\n ajaxAction = table.type === 'quarantined' ? 'spbc_restore_from_quarantine' : 'spbc_restore_selected';\n _context2.n = 7;\n return post(allItems ? {\n action: ajaxAction,\n apply_to_all: 1,\n status: table.type\n } : {\n action: ajaxAction,\n selectedIds: ids\n }, 60000);\n case 7:\n _result = _context2.v;\n showRestoreResult(_result);\n return _context2.a(2, 'refresh');\n case 8:\n if (!(action === 'delete_from_analysis_log')) {\n _context2.n = 10;\n break;\n }\n _context2.n = 9;\n return post(allItems ? {\n action: 'spbc_tbl-action--bulk',\n add_action: action,\n status: table.type,\n apply_to_all: 1\n } : {\n action: 'spbc_tbl-action--bulk',\n add_action: action,\n status: table.type,\n file_ids: ids\n }, 60000);\n case 9:\n return _context2.a(2, 'refresh');\n case 10:\n if (!allItems) {\n _context2.n = 12;\n break;\n }\n _context2.n = 11;\n return post({\n action: 'spbc_tbl-action--bulk',\n add_action: action,\n status: table.type\n }, 60000);\n case 11:\n _result2 = _context2.v;\n if (action === 'check_analysis_status' && _result2 && _result2.counters && window.spbcModal) {\n window.spbcModal.open().put(' Analysis status updated.' + ' Total: ' + _result2.counters.total + ', updated: ' + _result2.counters.updated + ', skipped: ' + _result2.counters.skipped + ', failed: ' + _result2.counters.failed + ', queued: ' + _result2.counters.queued + '
');\n }\n if (action === 'send' && _result2 && window.spbcModal) {\n window.spbcModal.open().put(' Files have been sent: ' + (_result2.files_sent_counter || 0) + '
');\n }\n return _context2.a(2, 'refresh');\n case 12:\n queue = rows.filter(function (row) {\n return ids.indexOf(row.cb) !== -1 && (row.actions || []).indexOf(action) !== -1;\n });\n if (config.restricted.indexOf(action) !== -1) {\n queue = queue.slice(0, 1);\n }\n i = 0;\n case 13:\n if (!(i < queue.length)) {\n _context2.n = 15;\n break;\n }\n def = table.actions && table.actions[action] || {};\n _context2.n = 14;\n return runRowAction(action, queue[i], def, {\n skipConfirm: true\n });\n case 14:\n i++;\n _context2.n = 13;\n break;\n case 15:\n return _context2.a(2, 'refresh');\n }\n }, _callee2);\n }));\n return _runBulkAction.apply(this, arguments);\n}\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabScanner/scannerActions.js?\n}");
/***/ },
@@ -1546,7 +1576,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
\************************************************************************/
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
-eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ TabContentHtml)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n\nfunction TabContentHtml(_ref) {\n var sectionId = _ref.sectionId,\n sectionData = _ref.sectionData;\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n prepareAccordion();\n }, [sectionId]);\n var prepareAccordion = function prepareAccordion() {\n // Preparing accordion\n jQuery('#spbc_scan_accordion').accordion({\n header: 'h3',\n heightStyle: 'content',\n collapsible: true,\n active: false,\n activate: function activate(event, ui) {\n jQuery('#spbc_scan_accordion').accordion('option', 'classes.ui-accordion-content', 'ui-corner-bottom spbc-overflow--visible');\n },\n beforeActivate: function beforeActivate(event, ui) {\n jQuery('#spbc_scan_accordion').accordion('option', 'classes.ui-accordion-content', 'ui-corner-bottom');\n }\n });\n };\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n dangerouslySetInnerHTML: {\n __html: sectionData\n },\n style: {\n display: 'contents'\n }\n }));\n}\n;\n\n//# sourceURL=webpack:///./js/src/react/components/TabContentTypes/Html/TabContentHtml.js?\n}");
+eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ TabContentHtml)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n\nfunction TabContentHtml(_ref) {\n var sectionId = _ref.sectionId,\n sectionData = _ref.sectionData;\n var rootRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n prepareAccordion();\n }, [sectionId, sectionData]);\n var prepareAccordion = function prepareAccordion() {\n var root = rootRef.current;\n if (!root) {\n return;\n }\n var accordion = root.querySelector('#spbc_scan_accordion');\n if (!accordion) {\n return;\n }\n jQuery(accordion).accordion({\n header: 'h3',\n heightStyle: 'content',\n collapsible: true,\n active: false,\n activate: function activate(event, ui) {\n jQuery('#spbc_scan_accordion').accordion('option', 'classes.ui-accordion-content', 'ui-corner-bottom spbc-overflow--visible');\n },\n beforeActivate: function beforeActivate(event, ui) {\n jQuery('#spbc_scan_accordion').accordion('option', 'classes.ui-accordion-content', 'ui-corner-bottom');\n }\n });\n };\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n ref: rootRef,\n dangerouslySetInnerHTML: {\n __html: sectionData\n },\n style: {\n display: 'contents'\n }\n }));\n}\n;\n\n//# sourceURL=webpack:///./js/src/react/components/TabContentTypes/Html/TabContentHtml.js?\n}");
/***/ },
@@ -1946,7 +1976,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
\********************************************************************************************************/
(module, __webpack_exports__, __webpack_require__) {
-eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../../../../node_modules/css-loader/dist/runtime/noSourceMaps.js */ \"./node_modules/css-loader/dist/runtime/noSourceMaps.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `#spbc_scaner_progress_bar {\n position: relative;\n overflow: hidden;\n}\n\n/* Indeterminate state of #spbc_scaner_progress_bar: shown while the backend can't compute a\n percent for the currently running stage (no known \"total\" for it), e.g. clean_results. */\n.spbc_progressbar--indeterminate .ui-progressbar-value {\n background-image: linear-gradient(\n 135deg,\n rgba(255, 255, 255, 0.3) 25%,\n transparent 25%,\n transparent 50%,\n rgba(255, 255, 255, 0.3) 50%,\n rgba(255, 255, 255, 0.3) 75%,\n transparent 75%,\n transparent\n );\n background-size: 30px 30px;\n animation: spbc_progressbar_indeterminate_stripes 1s linear infinite;\n}\n\n@keyframes spbc_progressbar_indeterminate_stripes {\n from {\n background-position: 30px 0;\n }\n to {\n background-position: 0 0;\n }\n}`, \"\"]);\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);\n\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabScanner/styles.css?./node_modules/css-loader/dist/cjs.js\n}");
+eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../../../../node_modules/css-loader/dist/runtime/noSourceMaps.js */ \"./node_modules/css-loader/dist/runtime/noSourceMaps.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `#spbc_scaner_progress_bar {\n position: relative;\n overflow: hidden;\n}\n\n/* Indeterminate state of #spbc_scaner_progress_bar: shown while the backend can't compute a\n percent for the currently running stage (no known \"total\" for it), e.g. clean_results. */\n.spbc_progressbar--indeterminate .ui-progressbar-value {\n background-image: linear-gradient(\n 135deg,\n rgba(255, 255, 255, 0.3) 25%,\n transparent 25%,\n transparent 50%,\n rgba(255, 255, 255, 0.3) 50%,\n rgba(255, 255, 255, 0.3) 75%,\n transparent 75%,\n transparent\n );\n background-size: 30px 30px;\n animation: spbc_progressbar_indeterminate_stripes 1s linear infinite;\n}\n\n.spbc-react-accordion__header {\n display: block;\n width: 100%;\n text-align: left;\n background: #f6f7f7;\n border: 1px solid #c3c4c7;\n margin: 0 0 2px;\n padding: 8px 12px;\n font-size: 14px;\n line-height: 1.4;\n cursor: pointer;\n color: #1d2327;\n}\n\n.spbc-react-accordion__header.is-open {\n background: #fff;\n border-bottom-color: transparent;\n}\n\n.spbc-react-accordion__body {\n border: 1px solid #c3c4c7;\n border-top: 0;\n padding: 8px 12px 12px;\n margin: -2px 0 8px;\n}\n\n.spbc-react-accordion__sort {\n background: none;\n border: 0;\n padding: 0;\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n\n.spbc-react-accordion__sort:disabled {\n cursor: default;\n}\n\n#spbc_scan_accordions .column-primary {\n position: initial;\n}\n\n#spbc_scan_accordions .row-actions button.tbl-row_action {\n background: none;\n border: 0;\n padding: 0;\n font: inherit;\n color: gray;\n cursor: pointer;\n}\n\n#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action {\n color: #0073aa;\n}\n\n#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action--delete,\n#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action--delete_quarantine,\n#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action--delete-trigger {\n color: #a00;\n}\n\n#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action--replace {\n color: #a00;\n}\n\n#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action--approve {\n color: #0a0;\n}\n\n#spbc_scan_accordions tbody tr {\n position: relative;\n}\n\n@keyframes spbc_progressbar_indeterminate_stripes {\n from {\n background-position: 30px 0;\n }\n to {\n background-position: 0 0;\n }\n}`, \"\"]);\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);\n\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabScanner/styles.css?./node_modules/css-loader/dist/cjs.js\n}");
/***/ },
diff --git a/js/src/react/components/TabContent/TabScanner/ScannerAccordions.js b/js/src/react/components/TabContent/TabScanner/ScannerAccordions.js
new file mode 100644
index 000000000..4d82a3415
--- /dev/null
+++ b/js/src/react/components/TabContent/TabScanner/ScannerAccordions.js
@@ -0,0 +1,230 @@
+import React, {useCallback, useEffect, useRef, useState} from 'react';
+import {sendAjaxRequest} from '../../../modules/Http/Ajax';
+import Preloader from '../../Preloader/Preloader';
+import ScannerTable from './ScannerTable';
+import {runBulkAction, runRowAction} from './scannerActions';
+
+export default function ScannerAccordions({scanning}) {
+ const [payload, setPayload] = useState(null);
+ const [tableState, setTableState] = useState({});
+ const [openPanel, setOpenPanel] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [selected, setSelected] = useState({});
+ const [busy, setBusy] = useState(false);
+ const [error, setError] = useState('');
+ const requestId = useRef(0);
+ const tableStateRef = useRef(tableState);
+ const openPanelRef = useRef(openPanel);
+ tableStateRef.current = tableState;
+ openPanelRef.current = openPanel;
+
+ const load = useCallback(async (states, openPanelId) => {
+ const id = ++requestId.current;
+ try {
+ const data = await sendAjaxRequest({
+ action: 'spbc_scanner__get_accordions',
+ tables: JSON.stringify(states || {}),
+ open_panel: openPanelId || '',
+ }, true);
+ if (id !== requestId.current) {
+ return;
+ }
+ if (data && data.success && data.data) {
+ setPayload(data.data);
+ setError('');
+ const categories = (data.data.objects && data.data.objects.categories) || [];
+ setTableState((prev) => {
+ let changed = false;
+ const next = Object.assign({}, prev);
+ categories.forEach((category) => {
+ (category.panels || []).forEach((panel) => {
+ const serverPage = panel.pagination && panel.pagination.page;
+ if (!serverPage) {
+ return;
+ }
+ const localPage = prev[panel.panel_id] && prev[panel.panel_id].page ?
+ Number(prev[panel.panel_id].page) :
+ 1;
+ if (localPage === Number(serverPage)) {
+ return;
+ }
+ next[panel.panel_id] = Object.assign({}, prev[panel.panel_id] || {}, {page: Number(serverPage)});
+ changed = true;
+ });
+ });
+ if (changed) {
+ tableStateRef.current = next;
+ }
+ return changed ? next : prev;
+ });
+ } else {
+ setError('Could not load scan results.');
+ }
+ } catch (loadError) {
+ if (id !== requestId.current) {
+ return;
+ }
+ console.error(loadError);
+ setError('Could not load scan results.');
+ } finally {
+ if (id === requestId.current) {
+ setLoading(false);
+ }
+ }
+ }, []);
+
+ useEffect(() => {
+ load(tableState, openPanel);
+ if (!scanning) {
+ return undefined;
+ }
+ const timer = setInterval(() => load(tableStateRef.current, openPanelRef.current), 5000);
+ return () => clearInterval(timer);
+ }, [scanning, tableState, openPanel, load]);
+
+ useEffect(() => {
+ if (scanning || openPanel !== 'analysis_log') {
+ return undefined;
+ }
+ const timer = setInterval(() => load(tableStateRef.current, openPanelRef.current), 6000);
+ return () => clearInterval(timer);
+ }, [scanning, openPanel, load]);
+
+ const patchState = (panelId, patch) => {
+ setTableState((prev) => Object.assign({}, prev, {
+ [panelId]: Object.assign({}, prev[panelId] || {}, patch),
+ }));
+ };
+
+ const clearSelection = (panelId) => {
+ setSelected((prev) => Object.assign({}, prev, {[panelId]: []}));
+ };
+
+ const onRowAction = async (panelId, actionKey, row, actionDef) => {
+ if (busy) {
+ return;
+ }
+ setBusy(true);
+ try {
+ const outcome = await runRowAction(actionKey, row, actionDef);
+ if (outcome === 'show_links') {
+ patchState(panelId, {view_type: 'links', domain: row.uid, page: 1});
+ } else if (outcome === 'refresh') {
+ setSelected((prev) => Object.assign({}, prev, {[panelId]: []}));
+ await load(tableStateRef.current, openPanelRef.current);
+ }
+ } catch (actionError) {
+ console.error(actionError);
+ } finally {
+ setBusy(false);
+ }
+ };
+
+ const onBulk = async (panel, action, allItems) => {
+ if (busy) {
+ return;
+ }
+ setBusy(true);
+ try {
+ const outcome = await runBulkAction(action, panel, selected[panel.panel_id] || [], allItems);
+ if (outcome === 'refresh') {
+ setSelected((prev) => Object.assign({}, prev, {[panel.panel_id]: []}));
+ await load(tableStateRef.current, openPanelRef.current);
+ }
+ } catch (actionError) {
+ console.error(actionError);
+ } finally {
+ setBusy(false);
+ }
+ };
+
+ if (loading && !payload) {
+ return ;
+ }
+ if (error && !payload) {
+ return {error}
;
+ }
+ if (!payload) {
+ return null;
+ }
+ const categories = (payload.objects && payload.objects.categories) || [];
+ if (payload.flags && payload.flags.restricted) {
+ return null;
+ }
+ if (!categories.length) {
+ return null;
+ }
+
+ return (
+
+ {categories.map((category) => (
+
+
{category.title}
+ {category.panels.map((panel) => {
+ const isOpen = openPanel === panel.panel_id;
+ const picked = selected[panel.panel_id] || [];
+ return (
+
+
setOpenPanel(isOpen ? null : panel.panel_id)}
+ >
+ {panel.title} ({panel.count} )
+ {panel.danger ? : null}
+
+ {isOpen ? (
+
+ {panel.description_html ? (
+
+ ) : null}
+ {panel.summary ?
: (
+
{
+ setSelected((prev) => {
+ const current = prev[panel.panel_id] || [];
+ const next = current.indexOf(id) === -1 ?
+ current.concat(id) :
+ current.filter((item) => item !== id);
+ return Object.assign({}, prev, {[panel.panel_id]: next});
+ });
+ }}
+ onToggleAll={(on) => {
+ setSelected((prev) => Object.assign({}, prev, {
+ [panel.panel_id]: on ? (panel.rows || []).map((row) => row.cb) : [],
+ }));
+ }}
+ onPage={(page) => {
+ clearSelection(panel.panel_id);
+ patchState(panel.panel_id, {page: page});
+ }}
+ onSort={(column) => {
+ const same = panel.order_by === column;
+ const order = same && panel.order !== 'desc' ? 'desc' : 'asc';
+ clearSelection(panel.panel_id);
+ patchState(panel.panel_id, {order_by: column, order: order, page: 1});
+ }}
+ onRowAction={(actionKey, row) => onRowAction(
+ panel.panel_id,
+ actionKey,
+ row,
+ panel.actions[actionKey],
+ )}
+ onBulk={(action, allItems) => onBulk(panel, action, allItems)}
+ onBack={() => patchState(panel.panel_id, {view_type: '', domain: '', page: 1})}
+ />
+ )}
+
+ ) : null}
+
+ );
+ })}
+
+ ))}
+
+ );
+}
diff --git a/js/src/react/components/TabContent/TabScanner/ScannerTable.js b/js/src/react/components/TabContent/TabScanner/ScannerTable.js
new file mode 100644
index 000000000..7248eea51
--- /dev/null
+++ b/js/src/react/components/TabContent/TabScanner/ScannerTable.js
@@ -0,0 +1,266 @@
+import React, {useEffect, useState} from 'react';
+const {__} = wp.i18n;
+
+export default function ScannerTable({
+ table,
+ selected,
+ busy,
+ onToggle,
+ onToggleAll,
+ onPage,
+ onSort,
+ onRowAction,
+ onBulk,
+ onBack,
+}) {
+ const [bulkAction, setBulkAction] = useState('-1');
+ const [pageInput, setPageInput] = useState(String(table.pagination?.page || 1));
+ useEffect(() => {
+ setPageInput(String(table.pagination?.page || 1));
+ }, [table.pagination?.page]);
+ useEffect(() => {
+ if (typeof spbcStartShowHide === 'function') {
+ spbcStartShowHide();
+ }
+ }, [table.rows]);
+ const rows = table.rows || [];
+ const columns = table.columns || [];
+ const allChecked = rows.length > 0 && rows.every((row) => selected.indexOf(row.cb) !== -1);
+ const pagination = table.pagination || {page: 1, pages: 1, per_page: 0};
+ const showPagination = pagination.per_page > 0 && table.items_total > pagination.per_page;
+ const bulkKeys = Object.keys(table.bulk_actions || {});
+
+ const go = (page) => {
+ const next = Math.min(Math.max(1, page), pagination.pages || 1);
+ onPage(next);
+ };
+
+ return (
+
+ {table.html_before ? (
+
{
+ if (table.type === 'links' && event.target.closest('a')) {
+ event.preventDefault();
+ event.stopPropagation();
+ onBack();
+ }
+ }}
+ dangerouslySetInnerHTML={{__html: table.html_before}}
+ />
+ ) : null}
+
+ {bulkKeys.length > 0 && (
+
+ setBulkAction(event.target.value)}>
+ {__('Bulk actions', 'security-malware-firewall')}
+ {bulkKeys.map((key) => (
+ {table.bulk_actions[key].name}
+ ))}
+
+ onBulk(bulkAction, false)}
+ >
+ {__('Apply to selected', 'security-malware-firewall')}
+
+ {table.bulk_actions_all ? (
+ onBulk(bulkAction, true)}
+ >
+ {__('Apply to all', 'security-malware-firewall')}
+
+ ) : null}
+
+ )}
+
+ {showPagination && (
+
+ {table.items_total} {__('Entries', 'security-malware-firewall')}
+ go(1)}
+ >
+
+
+ go(pagination.page - 1)}
+ >
+
+
+ setPageInput(event.target.value)}
+ />
+ {__(' of ', 'security-malware-firewall')}{pagination.pages}
+ go(parseInt(pageInput, 10) || 1)}
+ >
+ {__('Go', 'security-malware-firewall')}
+
+ go(pagination.page + 1)}
+ >
+
+
+ go(pagination.pages)}
+ >
+
+
+
+ )}
+
+ {table.empty_html ? (
+
+ ) : (
+
+ )}
+
+ );
+}
+
+function RowActions({row, table, onRowAction}) {
+ const keys = (row.actions || []).filter((key) => table.actions && table.actions[key]);
+ if (!keys.length) {
+ return null;
+ }
+ return (
+
+ {keys.map((key, index) => {
+ const def = table.actions[key];
+ const tip = def.tip || '';
+ if (def.type === 'link') {
+ const href = (row.links && row.links[key]) || '#';
+ return (
+
+ {index > 0 ? ' | ' : ''}
+
+ {def.name}
+
+
+ );
+ }
+ return (
+
+ {index > 0 ? ' | ' : ''}
+ onRowAction(key, row)}
+ >
+ {def.name}
+
+
+ );
+ })}
+
+ );
+}
diff --git a/js/src/react/components/TabContent/TabScanner/TabScanner.js b/js/src/react/components/TabContent/TabScanner/TabScanner.js
index 0902f50d7..5ccdd84b8 100644
--- a/js/src/react/components/TabContent/TabScanner/TabScanner.js
+++ b/js/src/react/components/TabContent/TabScanner/TabScanner.js
@@ -5,6 +5,7 @@ import {sendAjaxRequest} from '../../../modules/Http/Ajax';
import Preloader from '../../Preloader/Preloader';
import NoticeBanner from '../NoticeBanner/NoticeBanner';
import ScannerInfo from './ScannerInfo/ScannerInfo';
+import ScannerAccordions from './ScannerAccordions';
export default function TabScanner({sectionId, sectionData, isActive}) {
const [scannerStatus, setScannerStatus] = useState(null);
@@ -994,6 +995,7 @@ export default function TabScanner({sectionId, sectionData, isActive}) {
currentStageProgress={currentStageProgress}
onScanStarted={startPolling}
/>
+
>;
diff --git a/js/src/react/components/TabContent/TabScanner/scannerActions.js b/js/src/react/components/TabContent/TabScanner/scannerActions.js
new file mode 100644
index 000000000..5a421bb85
--- /dev/null
+++ b/js/src/react/components/TabContent/TabScanner/scannerActions.js
@@ -0,0 +1,416 @@
+/**
+ * Scanner accordion actions. Same AJAX endpoints as the legacy table handlers.
+ */
+
+function noConfirmConfig() {
+ const empty = {any: [], restricted: [], defaults: []};
+ try {
+ if (typeof spbcPublic === 'undefined' || !spbcPublic.no_confirm_row_actions) {
+ return empty;
+ }
+ const parsed = JSON.parse(spbcPublic.no_confirm_row_actions);
+ if (!Array.isArray(parsed.any)) {
+ return empty;
+ }
+ return parsed;
+ } catch (e) {
+ return empty;
+ }
+}
+
+function textFromHtml(html) {
+ const div = document.createElement('div');
+ div.innerHTML = html || '';
+ return (div.textContent || '').replace(/\s+/g, ' ').trim();
+}
+
+function scannerIsBusy() {
+ if (window.spbcScanner && window.spbcScanner.active) {
+ const message = (window.spbcTableLocalize && window.spbcTableLocalize.scannerIsActive) ||
+ 'Scanner is active for now. Try later.';
+ alert(message);
+ return true;
+ }
+ return false;
+}
+
+function confirmAction(action, fileName) {
+ const table = window.spbcTable || {};
+ const header = table['warning_h_' + action] || table.warning_default || 'Do you want to proceed?';
+ const text = table['warning_t_' + action] || '';
+ const skips = noConfirmConfig().any.indexOf(action) !== -1;
+ if (skips) {
+ return Promise.resolve(true);
+ }
+ return new Promise((resolve) => {
+ if (window.spbcModal && window.spbcModal.open) {
+ const yes = action === 'quarantine' ? 'Quarantine' : 'Yes';
+ const no = action === 'quarantine' ? 'Cancel' : 'No';
+ window.spbcModal.open().confirm(header, text, fileName, (ok) => resolve(!!ok), yes, no);
+ } else {
+ resolve(window.confirm(header + (text ? '\n' + text : '')));
+ }
+ });
+}
+
+/**
+ * Text from wp_send_json_error(). Structured cure/restore reports are left for their own handlers.
+ *
+ * @param {*} data Response data.
+ * @return {string} Error text, or an empty string when the caller should handle the payload.
+ */
+function wordpressErrorText(data) {
+ if (typeof data === 'string') {
+ return data;
+ }
+ if (!data || typeof data !== 'object') {
+ return '';
+ }
+ if (data.failed_to_cure || data.failed_to_restore) {
+ return '';
+ }
+ if (typeof data.error === 'string') {
+ return data.error;
+ }
+ if (typeof data.message === 'string') {
+ return data.message;
+ }
+ return '';
+}
+
+function post(data, timeout) {
+ return new Promise((resolve, reject) => {
+ jQuery.ajax({
+ type: 'POST',
+ url: spbcSettings.ajaxurl,
+ data: Object.assign({security: spbcSettings.ajax_nonce}, data),
+ timeout: timeout || 25000,
+ success: (result) => {
+ if (result && result.error) {
+ const comment = result.error_comment ? ' ' + result.error_comment : '';
+ if (window.spbcModal && window.spbcModal.open) {
+ window.spbcModal.open().putError(String(result.error) + comment);
+ }
+ reject(new Error('action error'));
+ return;
+ }
+ if (result && result.success === false) {
+ const message = wordpressErrorText(result.data);
+ const structuredReport = result.data && typeof result.data === 'object' && !message;
+ if (!structuredReport) {
+ if (window.spbcModal && window.spbcModal.open) {
+ window.spbcModal.open().putError(message || 'Request failed');
+ }
+ reject(new Error('action error'));
+ return;
+ }
+ }
+ resolve(result);
+ },
+ error: () => {
+ if (window.spbcModal && window.spbcModal.open) {
+ window.spbcModal.open().putError('Request failed');
+ }
+ reject(new Error('request failed'));
+ },
+ });
+ });
+}
+
+function fakeActionNode(uid, pageId) {
+ const wrap = document.createElement('div');
+ wrap.className = 'row-actions';
+ wrap.setAttribute('uid', uid || '');
+ if (pageId) {
+ wrap.setAttribute('page_id', pageId);
+ }
+ const el = document.createElement('span');
+ el.className = 'tbl-row_action';
+ wrap.appendChild(el);
+ return el;
+}
+
+function customKind(handler) {
+ if (!handler) {
+ return null;
+ }
+ if (handler.indexOf('FileViewBad') !== -1) {
+ return 'file_view_bad';
+ }
+ if (handler.indexOf('PageViewBad') !== -1) {
+ return 'page_view_bad';
+ }
+ if (handler.indexOf('PageViewEvent') !== -1) {
+ return 'page_view';
+ }
+ if (handler.indexOf('FileViewEvent') !== -1) {
+ return 'file_view';
+ }
+ if (handler.indexOf('CureFile') !== -1) {
+ return 'cure';
+ }
+ if (handler.indexOf('RestoreFromBackup') !== -1) {
+ return 'restore_backup';
+ }
+ if (handler.indexOf('AnalysisLogDelete') !== -1) {
+ return 'analysis_delete';
+ }
+ if (handler.indexOf('SwitchTable') !== -1) {
+ return 'show_links';
+ }
+ return null;
+}
+
+function showActionResult(result) {
+ if (!result) {
+ return;
+ }
+ if (result.success === true && result.data && result.data.file_info) {
+ const email = (window.spbcSettings && window.spbcSettings.wl_support_email) || 'support@cleantalk.org';
+ window.prompt('Copy the file info below and send it to ' + email, result.data.file_info);
+ return;
+ }
+ if (result.html && window.spbcModal && window.spbcModal.open) {
+ window.spbcModal.open().put(result.html);
+ }
+ if (result.temp_html && window.spbcModal && window.spbcModal.open) {
+ window.spbcModal.open().put(result.temp_html);
+ }
+}
+
+/**
+ * Run one scanner row action.
+ *
+ * @param {string} actionKey Action name.
+ * @param {object} row Table row.
+ * @param {object} actionDef Action definition from the table payload.
+ * @param {object} options Optional flags. skipConfirm skips the confirm dialog.
+ * @return {Promise} refresh, show_links, reload, or null.
+ */
+export async function runRowAction(actionKey, row, actionDef, options) {
+ const opts = options || {};
+ if (scannerIsBusy()) {
+ return null;
+ }
+
+ const kind = customKind(actionDef && actionDef.handler);
+ if (kind === 'show_links') {
+ return 'show_links';
+ }
+
+ // Custom handlers (view, cure, …) confirm inside their own UI, same as the old onclick path.
+ // The generic confirm is only for ajax row actions (.tbl-row_action--ajax).
+ if (!kind && !opts.skipConfirm) {
+ const fileName = textFromHtml((row.cells && (row.cells.path || row.cells.url || row.cells.domain)) || '');
+ const ok = await confirmAction(actionKey, fileName);
+ if (!ok) {
+ return null;
+ }
+ }
+
+ if (kind) {
+ const node = fakeActionNode(row.uid, row.page_id);
+ const handlers = {
+ file_view: window.spbcScannerButtonFileViewEvent,
+ file_view_bad: window.spbcScannerButtonFileViewBadEvent,
+ page_view: window.spbcScannerButtonPageViewEvent,
+ page_view_bad: window.spbcScannerButtonPageViewBadEvent,
+ cure: window.spbcScannerButtonCureFileAjaxHandler,
+ restore_backup: window.spbcScannerButtonRestoreFromBackupAjaxHandler,
+ analysis_delete: window.spbcScannerAnalysisLogDeleteFromLog,
+ };
+ if (typeof handlers[kind] === 'function') {
+ handlers[kind](node);
+ }
+ return kind === 'file_view' || kind === 'file_view_bad' || kind === 'page_view' || kind === 'page_view_bad' ?
+ null :
+ 'reload';
+ }
+
+ const timeout = (actionKey === 'delete' || actionKey === 'quarantine') ? 60000 : 25000;
+ const result = await post({
+ action: 'spbc_tbl-action--row',
+ add_action: actionKey,
+ id: row.uid,
+ cols: '1',
+ page_url: row.uid,
+ page_id: row.page_id || '',
+ }, timeout);
+ showActionResult(result);
+ return 'refresh';
+}
+
+const APPLY_TO_ALL = [
+ 'approve',
+ 'disapprove',
+ 'send',
+ 'check_analysis_status',
+ 'approve_page',
+ 'disapprove_page',
+ 'cure',
+ 'delete_from_analysis_log',
+ 'restore',
+];
+
+/**
+ * Cure-log restore report. Same layout as the legacy bulk handler.
+ *
+ * @param {object} result AJAX response.
+ */
+function showRestoreResult(result) {
+ const displayData = (result && result.data) || {};
+ const structured = displayData && typeof displayData === 'object' &&
+ (Object.prototype.hasOwnProperty.call(displayData, 'failed_to_restore') ||
+ Object.prototype.hasOwnProperty.call(displayData, 'restored_on_request'));
+ if (!structured) {
+ if (result && result.success === false && window.spbcModal && window.spbcModal.open) {
+ window.spbcModal.open().putError(typeof displayData === 'string' ? displayData : 'Request failed');
+ } else if (displayData && displayData.message && window.spbcModal && window.spbcModal.open) {
+ window.spbcModal.open().put(displayData.message);
+ }
+ return;
+ }
+ const failed = displayData.failed_to_restore || [];
+ const html = '' + (displayData.message || '') + '
' +
+ '
Restored: ' + (displayData.restored_on_request || 0) +
+ ', unchanged or already restored: ' + (displayData.skipped || 0) +
+ ', failed to restore: ' + failed.length + '
' +
+ (failed.length ? '
' + failed.join(' ') + '
' : '') +
+ '
';
+ if (window.spbcModal && window.spbcModal.open) {
+ if (result && result.success === false) {
+ window.spbcModal.open().putError(html);
+ } else {
+ window.spbcModal.open().put(html);
+ }
+ }
+}
+
+function showCureResult(result) {
+ const displayData = (result && result.data) || {};
+ const failed = displayData.failed_to_cure || [];
+ const html = '' + (displayData.message || '') + '
' +
+ '
Cured: ' + (displayData.cured_on_request || 0) +
+ ', already cured: ' + (displayData.skipped || 0) +
+ ', failed to cure: ' + failed.length + '
' +
+ (failed.length ? '
' + failed.join(' ') + '
See more details in the threats log.
' : '') +
+ '
';
+ if (window.spbcModal && window.spbcModal.open) {
+ if (result && result.success) {
+ window.spbcModal.open().put(html);
+ } else {
+ window.spbcModal.open().putError(typeof displayData === 'string' ? displayData : html);
+ }
+ }
+}
+
+/**
+ * @param {string} action
+ * @param {object} table
+ * @param {string[]} selectedIds
+ * @param {boolean} allItems
+ */
+export async function runBulkAction(action, table, selectedIds, allItems) {
+ if (!action || action === '-1' || scannerIsBusy()) {
+ return null;
+ }
+
+ const rows = table.rows || [];
+ const ids = selectedIds;
+ if (!allItems && !ids.length) {
+ alert('Please, select elements.');
+ return null;
+ }
+
+ if (allItems && APPLY_TO_ALL.indexOf(action) === -1) {
+ alert('This action is only permitted when you click the "Apply to selected" button. ' +
+ 'Please be cautious in your selection.');
+ return null;
+ }
+
+ const config = noConfirmConfig();
+ if (config.any.indexOf(action) === -1) {
+ const warning = (window.spbcTable && window.spbcTable.warning_bulk) ||
+ 'Are sure you want to perform these actions?';
+ if (!window.confirm(warning)) {
+ return null;
+ }
+ }
+
+ if (action === 'cure') {
+ const result = await post(allItems ? {
+ action: 'spbc_cure_selected',
+ apply_to_all: 1,
+ status: table.type,
+ } : {
+ action: 'spbc_cure_selected',
+ selectedIds: ids,
+ }, 60000);
+ showCureResult(result);
+ return 'refresh';
+ }
+
+ if (action === 'restore') {
+ const ajaxAction = table.type === 'quarantined' ?
+ 'spbc_restore_from_quarantine' :
+ 'spbc_restore_selected';
+ const result = await post(allItems ? {
+ action: ajaxAction,
+ apply_to_all: 1,
+ status: table.type,
+ } : {
+ action: ajaxAction,
+ selectedIds: ids,
+ }, 60000);
+ showRestoreResult(result);
+ return 'refresh';
+ }
+
+ if (action === 'delete_from_analysis_log') {
+ await post(allItems ? {
+ action: 'spbc_tbl-action--bulk',
+ add_action: action,
+ status: table.type,
+ apply_to_all: 1,
+ } : {
+ action: 'spbc_tbl-action--bulk',
+ add_action: action,
+ status: table.type,
+ file_ids: ids,
+ }, 60000);
+ return 'refresh';
+ }
+
+ if (allItems) {
+ const result = await post({
+ action: 'spbc_tbl-action--bulk',
+ add_action: action,
+ status: table.type,
+ }, 60000);
+ if (action === 'check_analysis_status' && result && result.counters && window.spbcModal) {
+ window.spbcModal.open().put(
+ ' Analysis status updated.' +
+ ' Total: ' + result.counters.total +
+ ', updated: ' + result.counters.updated +
+ ', skipped: ' + result.counters.skipped +
+ ', failed: ' + result.counters.failed +
+ ', queued: ' + result.counters.queued +
+ '
',
+ );
+ }
+ if (action === 'send' && result && window.spbcModal) {
+ window.spbcModal.open().put(' Files have been sent: ' + (result.files_sent_counter || 0) + '
');
+ }
+ return 'refresh';
+ }
+
+ let queue = rows.filter((row) => ids.indexOf(row.cb) !== -1 && (row.actions || []).indexOf(action) !== -1);
+ if (config.restricted.indexOf(action) !== -1) {
+ queue = queue.slice(0, 1);
+ }
+ for (let i = 0; i < queue.length; i++) {
+ const def = (table.actions && table.actions[action]) || {};
+ await runRowAction(action, queue[i], def, {skipConfirm: true});
+ }
+ return 'refresh';
+}
diff --git a/js/src/react/components/TabContent/TabScanner/styles.css b/js/src/react/components/TabContent/TabScanner/styles.css
index 63eeded32..368bd196b 100644
--- a/js/src/react/components/TabContent/TabScanner/styles.css
+++ b/js/src/react/components/TabContent/TabScanner/styles.css
@@ -20,6 +20,80 @@
animation: spbc_progressbar_indeterminate_stripes 1s linear infinite;
}
+.spbc-react-accordion__header {
+ display: block;
+ width: 100%;
+ text-align: left;
+ background: #f6f7f7;
+ border: 1px solid #c3c4c7;
+ margin: 0 0 2px;
+ padding: 8px 12px;
+ font-size: 14px;
+ line-height: 1.4;
+ cursor: pointer;
+ color: #1d2327;
+}
+
+.spbc-react-accordion__header.is-open {
+ background: #fff;
+ border-bottom-color: transparent;
+}
+
+.spbc-react-accordion__body {
+ border: 1px solid #c3c4c7;
+ border-top: 0;
+ padding: 8px 12px 12px;
+ margin: -2px 0 8px;
+}
+
+.spbc-react-accordion__sort {
+ background: none;
+ border: 0;
+ padding: 0;
+ color: inherit;
+ font: inherit;
+ cursor: pointer;
+}
+
+.spbc-react-accordion__sort:disabled {
+ cursor: default;
+}
+
+#spbc_scan_accordions .column-primary {
+ position: initial;
+}
+
+#spbc_scan_accordions .row-actions button.tbl-row_action {
+ background: none;
+ border: 0;
+ padding: 0;
+ font: inherit;
+ color: gray;
+ cursor: pointer;
+}
+
+#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action {
+ color: #0073aa;
+}
+
+#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action--delete,
+#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action--delete_quarantine,
+#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action--delete-trigger {
+ color: #a00;
+}
+
+#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action--replace {
+ color: #a00;
+}
+
+#spbc_scan_accordions tr:hover .row-actions button.tbl-row_action--approve {
+ color: #0a0;
+}
+
+#spbc_scan_accordions tbody tr {
+ position: relative;
+}
+
@keyframes spbc_progressbar_indeterminate_stripes {
from {
background-position: 30px 0;
diff --git a/js/src/react/components/TabContentTypes/Html/TabContentHtml.js b/js/src/react/components/TabContentTypes/Html/TabContentHtml.js
index 99a8b47b7..9a02a0392 100644
--- a/js/src/react/components/TabContentTypes/Html/TabContentHtml.js
+++ b/js/src/react/components/TabContentTypes/Html/TabContentHtml.js
@@ -1,13 +1,22 @@
-import React, {useEffect} from 'react';
+import React, {useEffect, useRef} from 'react';
export default function TabContentHtml({sectionId, sectionData}) {
+ const rootRef = useRef(null);
+
useEffect(() => {
prepareAccordion();
- }, [sectionId]);
+ }, [sectionId, sectionData]);
const prepareAccordion = () => {
- // Preparing accordion
- jQuery('#spbc_scan_accordion').accordion({
+ const root = rootRef.current;
+ if (!root) {
+ return;
+ }
+ const accordion = root.querySelector('#spbc_scan_accordion');
+ if (!accordion) {
+ return;
+ }
+ jQuery(accordion).accordion({
header: 'h3',
heightStyle: 'content',
collapsible: true,
@@ -24,7 +33,7 @@ export default function TabContentHtml({sectionId, sectionData}) {
return (
<>
-
+
>
);
};
diff --git a/lib/CleantalkSP/Common/Helpers/CSV.php b/lib/CleantalkSP/Common/Helpers/CSV.php
index 72b3279c6..5d91b9635 100644
--- a/lib/CleantalkSP/Common/Helpers/CSV.php
+++ b/lib/CleantalkSP/Common/Helpers/CSV.php
@@ -41,6 +41,7 @@ public static function sanitizeFromEmptyLines(&$buffer)
* @param string $buffer
*
* @return string[]
+ * @psalm-suppress PossiblyUnusedMethod
*/
public static function parseCSVLite($buffer)
{
diff --git a/lib/CleantalkSP/SpbctWP/ListTable.php b/lib/CleantalkSP/SpbctWP/ListTable.php
index 95599f693..bec47674b 100644
--- a/lib/CleantalkSP/SpbctWP/ListTable.php
+++ b/lib/CleantalkSP/SpbctWP/ListTable.php
@@ -139,7 +139,12 @@ public function __construct($args = array())
$this->bulk_actions_all = isset($args['bulk_actions_all']) ? $args['bulk_actions_all'] : $this->bulk_actions_all;
}
- public function getData()
+ /**
+ * Load the table total and, unless $with_rows is false, the current page of rows.
+ *
+ * @param bool $with_rows Closed panels during a scan poll only need the total.
+ */
+ public function getData($with_rows = true)
{
global $wpdb;
@@ -169,6 +174,15 @@ public function getData()
$this->items_total = key($total);
}
+ $this->clampPagination();
+
+ if ( ! $with_rows ) {
+ $this->rows = array();
+ $this->items = array();
+ $this->items_count = 0;
+ return;
+ }
+
// Getting data
// by using given function ('function_name') of method (['Class', 'method'])
if (
@@ -281,6 +295,232 @@ private function prepareDataDefault()
}
}
+ /**
+ * Keep the requested page inside the range the current total can show.
+ * A delete on the last page would otherwise return no rows and hide pagination.
+ */
+ private function clampPagination()
+ {
+ if ( empty($this->pagination['per_page']) ) {
+ return;
+ }
+
+ $per_page = (int) $this->pagination['per_page'];
+ if ( $per_page < 1 ) {
+ return;
+ }
+
+ $pages = max(1, (int) ceil((int) $this->items_total / $per_page));
+ $page = isset($this->pagination['page']) ? (int) $this->pagination['page'] : 1;
+ if ( $page < 1 ) {
+ $page = 1;
+ }
+ if ( $page > $pages ) {
+ $page = $pages;
+ }
+
+ $this->pagination['page'] = $page;
+ $this->sql['offset'] = ($page - 1) * $per_page;
+ // Keep sql.limit. The constructor already applied per_page or an explicit limit_force.
+ // Cure log reads the page from the request, not from the SQL offset.
+ $_POST['page'] = $page;
+ }
+
+ /**
+ * Table payload for the React scanner accordions.
+ * Cell HTML stays as prepared by the existing data callbacks.
+ * Row actions are returned separately so React can run them.
+ *
+ * @return array
+ */
+ public function exportForReact()
+ {
+ $columns = array();
+ foreach ( $this->columns as $key => $column ) {
+ $heading = isset($column['heading']) ? $column['heading'] : $key;
+ $help_setting = '';
+ if ( is_string($heading) && preg_match('/setting="([A-Za-z0-9_]+)"/', $heading, $help_match) ) {
+ $help_setting = $help_match[1];
+ }
+ if ( $key === 'cb' ) {
+ $heading = '';
+ }
+ $columns[] = array(
+ 'key' => $key,
+ 'heading' => wp_strip_all_tags($heading),
+ 'help_setting' => $help_setting,
+ 'primary' => ! empty($column['primary']),
+ 'class' => isset($column['class']) ? $column['class'] : '',
+ 'width_percent' => isset($column['width_percent']) ? (int) $column['width_percent'] : 0,
+ 'sortable' => in_array($key, (array) $this->sortable, true),
+ 'hint' => isset($column['hint']) ? $column['hint'] : '',
+ );
+ }
+
+ $naming = self::getRowActionsElementNamingData();
+ $action_defs = array();
+ foreach ( $this->actions as $key => $action ) {
+ $action_defs[$key] = array(
+ 'name' => self::translatedActionName($key, $action, $naming),
+ 'type' => isset($action['type']) ? $action['type'] : 'button',
+ 'handler' => isset($action['handler']) ? $action['handler'] : '',
+ 'tip' => isset($naming[$key]['tip'])
+ ? $naming[$key]['tip']
+ : (isset($action['tip']) ? $action['tip'] : ''),
+ 'target' => isset($action['target']) ? $action['target'] : '_self',
+ );
+ }
+
+ $bulk = array();
+ foreach ( $this->bulk_actions as $key => $action ) {
+ $bulk[$key] = array(
+ 'name' => self::translatedActionName($key, $action, $naming),
+ );
+ }
+
+ $scheduled_ids = function_exists('spbc_get_list_of_scheduled_suspicious_files_to_send')
+ ? spbc_get_list_of_scheduled_suspicious_files_to_send()
+ : array();
+
+ $rows = array();
+ foreach ( $this->items as $item ) {
+ $item = (array) $item;
+ $cells = array();
+ foreach ( array_keys($this->columns) as $key ) {
+ if ( $key === 'cb' ) {
+ continue;
+ }
+ $cells[$key] = isset($item[$key]) ? (string) $item[$key] : '';
+ }
+
+ $action_keys = array();
+ if ( ! empty($item['actions']) && is_array($item['actions']) ) {
+ $action_keys = array_values(array_keys($item['actions']));
+ }
+
+ $uid = isset($item['uid']) ? (string) $item['uid'] : '';
+ if ( $uid === '' && isset($item['cb']) ) {
+ $uid = (string) $item['cb'];
+ }
+ if ( $uid === '' ) {
+ foreach ( $this->columns as $column_key => $column ) {
+ if ( ! empty($column['primary']) && isset($item[$column_key]) && (string) $item[$column_key] !== '' ) {
+ $uid = (string) $item[$column_key];
+ break;
+ }
+ }
+ }
+ $links = array();
+ foreach ( $action_keys as $action_key ) {
+ if ( ! isset($this->actions[$action_key]['type']) || $this->actions[$action_key]['type'] !== 'link' ) {
+ continue;
+ }
+ $links[$action_key] = $this->exportActionHref($this->actions[$action_key], $uid, $item);
+ }
+
+ $rows[] = array(
+ 'uid' => $uid,
+ 'cb' => isset($item['cb']) ? (string) $item['cb'] : $uid,
+ 'page_id' => isset($item['page_id']) ? (string) $item['page_id'] : '',
+ 'cells' => $cells,
+ 'actions' => $action_keys,
+ 'links' => $links,
+ 'scheduled' => $uid !== '' && in_array($uid, $scheduled_ids, true),
+ );
+ }
+
+ $per_page = isset($this->pagination['per_page']) ? (int) $this->pagination['per_page'] : 0;
+ $page = isset($this->pagination['page']) ? (int) $this->pagination['page'] : 1;
+ $total = (int) $this->items_total;
+ $pages = ($per_page > 0) ? (int) ceil($total / $per_page) : 1;
+
+ $order_by = '';
+ $order = 'asc';
+ if ( ! empty($this->order_by) && is_array($this->order_by) ) {
+ $order_by = (string) key($this->order_by);
+ $order = (string) current($this->order_by);
+ }
+
+ return array(
+ 'id' => $this->id,
+ 'type' => $this->type,
+ 'columns' => $columns,
+ 'rows' => $rows,
+ 'actions' => $action_defs,
+ 'bulk_actions' => $bulk,
+ 'bulk_actions_all' => (bool) $this->bulk_actions_all,
+ 'items_total' => $total,
+ 'pagination' => array(
+ 'page' => $page,
+ 'per_page' => $per_page,
+ 'pages' => max(1, $pages),
+ ),
+ 'order_by' => $order_by,
+ 'order' => $order,
+ 'html_before' => (string) $this->html_before,
+ 'empty_html' => $this->items_count == 0 ? (string) $this->if_empty_items : '',
+ );
+ }
+
+ /**
+ * @param array $action
+ * @param string $uid
+ * @param array $item
+ * @return string
+ */
+ private function exportActionHref($action, $uid, $item)
+ {
+ $href = get_option('home') . '/';
+
+ if ( ! empty($action['local']) && ! empty($action['href']) ) {
+ $href .= $action['href'];
+ }
+
+ if ( ! empty($action['uid']) ) {
+ $href .= $uid;
+ }
+
+ if ( ! empty($action['edit_post_link']) && ! empty($item['page_url']) && preg_match('/=(\d+)$/', $item['page_url'], $matches) ) {
+ $href .= 'post.php?post=' . $matches[1] . '&action=edit';
+ }
+
+ return $href;
+ }
+
+ /**
+ * Use the translated action title when the table stores the same English label.
+ *
+ * @param string $key Action key.
+ * @param array $action Action definition.
+ * @param array $naming Translated titles from getRowActionsElementNamingData().
+ * @return string
+ */
+ private static function translatedActionName($key, $action, $naming)
+ {
+ $name = isset($action['name']) ? (string) $action['name'] : (string) $key;
+ $sources = array(
+ 'delete' => 'Delete',
+ 'delete_quarantine' => 'Delete from quarantine',
+ 'view' => 'View',
+ 'send' => 'Send for Analysis',
+ 'approve' => 'Approve',
+ 'quarantine' => 'Quarantine',
+ 'download' => 'Download',
+ 'replace' => 'Replace',
+ 'view_bad' => 'View Malicious Code',
+ 'cure' => 'Cure',
+ 'restore' => 'Restore',
+ 'copy_file_info' => 'Copy file info',
+ 'check_analysis_status' => 'Refresh the analysis status',
+ );
+
+ if ( isset($sources[$key], $naming[$key]['title']) && $name === $sources[$key] ) {
+ return (string) $naming[$key]['title'];
+ }
+
+ return $name;
+ }
+
public function display()
{
if ( $this->items_count == 0 ) {
@@ -392,13 +632,13 @@ public function displayBulkActionControls()
}
echo '';
echo ''
- . __('Apply to selected')
+ . __('Apply to selected', 'security-malware-firewall')
. ' '
. ' ';
if ( $this->bulk_actions_all ) {
echo ''
- . __('Apply to all')
+ . __('Apply to all', 'security-malware-firewall')
. ' '
. ' ';
}
@@ -429,7 +669,7 @@ public function displayPaginationControls()
echo '';
echo "";
echo '';
- echo '';
+ echo '';
echo '
';
echo '';
echo '';
diff --git a/lib/CleantalkSP/SpbctWP/Scanner/Cure.php b/lib/CleantalkSP/SpbctWP/Scanner/Cure.php
index ec9745e3e..45152045f 100644
--- a/lib/CleantalkSP/SpbctWP/Scanner/Cure.php
+++ b/lib/CleantalkSP/SpbctWP/Scanner/Cure.php
@@ -271,6 +271,40 @@ public function actionPerformWithCode($object, $actions, $_file, $signature_id)
return $result;
}
+ /**
+ * Selected row ids, or every id of the panel when Apply to all is set.
+ *
+ * @param string[] $allowed_statuses Panel types that may use Apply to all.
+ * @return list
+ */
+ private static function idsForBulkRequest($allowed_statuses)
+ {
+ $raw = array();
+ if (!empty($_POST['apply_to_all'])) {
+ $status = isset($_POST['status']) ? sanitize_key(wp_unslash($_POST['status'])) : '';
+ if (in_array($status, $allowed_statuses, true)) {
+ if ($status === 'cure_log') {
+ $log = new CureLog();
+ $raw = $log->getAllFastHashes();
+ } else {
+ $raw = ScanResultsTableActions::getFilesByCategory($status);
+ }
+ }
+ } else {
+ $posted = isset($_POST['selectedIds']) ? $_POST['selectedIds'] : array();
+ $raw = is_array($posted) ? $posted : array();
+ }
+
+ $ids = array();
+ foreach ($raw as $id) {
+ if (is_string($id) && $id !== '') {
+ $ids[] = $id;
+ }
+ }
+
+ return $ids;
+ }
+
/**
* Handles the AJAX request to cure selected files.
*
@@ -299,7 +333,7 @@ public static function cureSelectedAction()
{
spbc_check_ajax_referer('spbc_secret_nonce', 'security');
- $ids = isset($_POST['selectedIds']) ? $_POST['selectedIds'] : null;
+ $ids = self::idsForBulkRequest(array('critical', 'cure_log'));
if (empty($ids)) {
$response_data['message'] = esc_html__('No items to restore', 'security-malware-firewall');
@@ -357,7 +391,7 @@ public static function restoreSelectedAction()
{
spbc_check_ajax_referer('spbc_secret_nonce', 'security');
- $ids = isset($_POST['selectedIds']) ? $_POST['selectedIds'] : null;
+ $ids = self::idsForBulkRequest(array('cure_log'));
if (empty($ids)) {
$response_data['message'] = esc_html__('No items to restore', 'security-malware-firewall');
diff --git a/lib/CleantalkSP/SpbctWP/Scanner/CureLog/CureLog.php b/lib/CleantalkSP/SpbctWP/Scanner/CureLog/CureLog.php
index ab957644f..3ba4b07ee 100644
--- a/lib/CleantalkSP/SpbctWP/Scanner/CureLog/CureLog.php
+++ b/lib/CleantalkSP/SpbctWP/Scanner/CureLog/CureLog.php
@@ -264,6 +264,35 @@ public static function parseWeakSpotsFromCureResult($weak_spots_result)
return array('cured' => $cured, 'uncured' => $uncured);
}
+ /**
+ * Every cure-log id, including restored rows. Used by Apply to all.
+ *
+ * @return list
+ */
+ public function getAllFastHashes()
+ {
+ $query = 'SELECT fast_hash FROM ' . SPBC_TBL_CURE_LOG;
+ $result = $this->db->fetchAll($query);
+ if (!is_array($result)) {
+ return array();
+ }
+
+ $ids = array();
+ foreach ($result as $row) {
+ $hash = '';
+ if (is_object($row) && !empty($row->fast_hash)) {
+ $hash = (string) $row->fast_hash;
+ } elseif (is_array($row) && !empty($row['fast_hash'])) {
+ $hash = (string) $row['fast_hash'];
+ }
+ if ($hash !== '') {
+ $ids[] = $hash;
+ }
+ }
+
+ return $ids;
+ }
+
/**
* @return array
*/
diff --git a/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerView.php b/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerView.php
index 2e72f4c2e..a47e0a331 100644
--- a/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerView.php
+++ b/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerView.php
@@ -17,15 +17,18 @@ final public static function prepareTableData($table)
$table->items_count = DBTriggerService::countTriggersStorage();
$table->columns = [
'cb' => array('heading' => ' ', 'class' => 'check-column', 'width_percent' => 2),
- 'about_trigger' => array( 'heading' => 'About trigger', 'width_percent' => 30 ), // name, table, time, action
+ 'about_trigger' => array( 'heading' => 'About trigger', 'primary' => true, 'width_percent' => 30 ), // name, table, time, action
'code' => array( 'heading' => 'Code', 'width_percent' => 43 ),
'signature' => array( 'heading' => 'Signature', 'width_percent' => 10 ),
'analysis_status' => array( 'heading' => 'Verdict', 'width_percent' => 15 ),
];
- $table->actions = [
- 'delete' => array('name' => 'Delete',),
- ];
+ $delete_action = array(
+ 'delete-trigger' => array('name' => __('Delete', 'security-malware-firewall')),
+ );
+ $table->actions = $delete_action;
+ $table->bulk_actions = $delete_action;
foreach ($table->items as $key => $item) {
+ $uid = isset($item['name']) ? (string) $item['name'] : (string) $key;
$table->items[$key]['about_trigger'] = sprintf(
'%s %s %s %s',
__('Name', 'security-malware-firewall') . ': ' . $item['name'],
@@ -33,16 +36,12 @@ final public static function prepareTableData($table)
__('Time', 'security-malware-firewall') . ': ' . $item['time'],
__('Action', 'security-malware-firewall') . ': ' . $item['action']
);
- $table->items[$key]['about_trigger'] .= sprintf(
- '%s
',
- $item['name'],
- $item['name'],
- __('Delete', 'security-malware-firewall')
- );
$table->items[$key]['code'] = $item['code'];
$table->items[$key]['signature'] = $item['signature'];
$table->items[$key]['analysis_status'] = $item['status'];
- $table->items[$key]['cb'] = 1;
+ $table->items[$key]['uid'] = $uid;
+ $table->items[$key]['cb'] = $uid;
+ $table->items[$key]['actions'] = array('delete-trigger' => true);
}
return $table;
}
diff --git a/lib/CleantalkSP/SpbctWP/Scanner/ScannerAjaxEndpoints.php b/lib/CleantalkSP/SpbctWP/Scanner/ScannerAjaxEndpoints.php
index 00c06256e..588296764 100644
--- a/lib/CleantalkSP/SpbctWP/Scanner/ScannerAjaxEndpoints.php
+++ b/lib/CleantalkSP/SpbctWP/Scanner/ScannerAjaxEndpoints.php
@@ -193,7 +193,12 @@ public static function deleteFileFromAnalysisLog($direct_call = false)
spbc_check_ajax_referer('spbc_secret_nonce', 'security');
}
- $output = ScanResultsTableActions::deleteFileFromAnalysisLog(Post::getArray('file_ids'));
+ $file_ids = Post::getArray('file_ids');
+ if (!empty($_POST['apply_to_all'])) {
+ $file_ids = array_values(array_filter(ScanResultsTableActions::getFilesByCategory('analysis_log')));
+ }
+
+ $output = ScanResultsTableActions::deleteFileFromAnalysisLog($file_ids);
if ( ! $direct_call ) {
wp_send_json($output);
@@ -220,6 +225,9 @@ public static function restoreFromQuarantine($direct_call = false, $file_id = nu
if (!$file_id) {
$file_ids = Post::getArray('selectedIds');
+ if (!$file_ids && !empty($_POST['apply_to_all']) && Post::getString('status') === 'quarantined') {
+ $file_ids = array_values(array_filter(ScanResultsTableActions::getFilesByCategory('quarantined')));
+ }
if (!$file_ids) {
return array('error' => 'WRONG_FILE_IDS');
}
diff --git a/spec/__tests__/scan_work.test.js b/spec/__tests__/scan_work.test.js
index 674597319..6fbbf080b 100644
--- a/spec/__tests__/scan_work.test.js
+++ b/spec/__tests__/scan_work.test.js
@@ -12,20 +12,16 @@ export async function __scanWork() {
await jestState.page.waitForSelector(field);
await jestState.page.click(field);
- await jestState.page.waitForSelector('#spbc_scan_accordion');
+ await jestState.page.waitForSelector('#spbc_scan_accordions');
- await jestState.page.waitForSelector('div[refresh_control_tab="critical"]');
- await jestState.page.click('div[refresh_control_tab="critical"]');
- await jestState.page.waitForSelector('div[refresh_control_tab="cure_log"]');
- await jestState.page.click('div[refresh_control_tab="cure_log"]');
- await jestState.page.waitForSelector('div[refresh_control_tab="unsafe_permissions"]');
- await jestState.page.click('div[refresh_control_tab="unsafe_permissions"]');
- await jestState.page.waitForSelector('div[refresh_control_tab="files_listing"]');
- await jestState.page.click('div[refresh_control_tab="files_listing"]');
- await jestState.page.waitForSelector('div[refresh_control_tab="oscron"]');
- await jestState.page.click('div[refresh_control_tab="oscron"]');
+ const panels = ['critical', 'cure_log', 'unsafe_permissions', 'files_listing', 'oscron'];
+ for (const panel of panels) {
+ const header = '#spbc_scan_accordions [data-refresh-tab="' + panel + '"] .spbc-react-accordion__header';
+ await jestState.page.waitForSelector(header);
+ await jestState.page.click(header);
+ }
- await new Promise(resolve => setTimeout(resolve, 1000));
+ await new Promise((resolve) => setTimeout(resolve, 1000));
}
/**