' +\n '
%s' +\n '
%s
' +\n '
<' +\n '/div>';\n\n content.forEach((line, index) => {\n dialogWindow.append(rowTemplate.printf(index + 1, line));\n });\n\n let contentHeight = Object.keys(content).length * 19 < 76 ? 76 : Object.keys(content).length * 19;\n let visibleHeight = (document.documentElement.clientHeight) / 100 * 75;\n let overflow = contentHeight < visibleHeight ? 'hidden' : 'scroll';\n let height = overflow === 'scroll' ? visibleHeight : contentHeight;\n\n dialogWindow.css({\n height: height,\n overflow: overflow,\n });\n };\n\n spbcSendAJAXRequest(data, {callback: callback});\n}\n\n/**\n * Try to decode JSON string from site response.\n * @param {string} response\n * @return {obj} Json parsed obj or error obj.\n */\nfunction FSWDecodeJSON(response) {\n try {\n return JSON.parse(response);\n } catch (e) {\n return {'error': e};\n }\n}\n\n/**\n * Run rendering comparison table in dependence of response object\n * @param {{}} responseDataObj\n */\nfunction renderFSWatcherTableContent(responseDataObj) {\n fsWatcherTableBody.innerHTML = '';\n for (const dataSetName of availableFSWDataSetNames) {\n if (handleFSWDataObject(responseDataObj, dataSetName)) {\n noFSWChangesDetected = false;\n }\n }\n}\n\n/**\n * Validate response object from site.\n * @param {obj} responseDataObj\n * @return {*|boolean}\n */\nfunction validateFSWResponse(responseDataObj) {\n if (\n !responseDataObj ||\n typeof responseDataObj !== 'object'\n ) {\n return fswatcherTranslations['fs_err_resp_obj'];\n }\n\n if (typeof responseDataObj.error !== 'undefined') {\n return responseDataObj.error;\n }\n\n for (const dataSetName of availableFSWDataSetNames) {\n if (\n !responseDataObj.hasOwnProperty(dataSetName)\n ) {\n return fswatcherTranslations['fs_err_property'];\n }\n }\n\n return true;\n}\n\n/**\n * @param {object} responseDataObj\n * @param {string|number} eventType\n * @return {boolean}\n */\nfunction handleFSWDataObject(responseDataObj, eventType) {\n const eventsArray = responseDataObj[eventType];\n if (eventsArray.length > 0) {\n for (let i = 0; i < eventsArray.length; i++) {\n const row = convertFSWEventToRow(eventsArray[i], eventType);\n renderFSWTableRow(row.path, row.event_type, row.date);\n }\n } else {\n return false;\n }\n return true;\n}\n\n/**\n * Convert a row of site response to the formatted data.\n * @param {object} event contains the date and the file path\n * @param {string} eventType contains event type\n * @return {{path: string, event_type: string, date: string}} row of the table\n */\nfunction convertFSWEventToRow(event, eventType) {\n let row = {\n 'path': 'unknown',\n 'event_type': eventType.toUpperCase(),\n 'date': 'unknown',\n };\n\n if (event.length === 2) {\n if (typeof event[0] === 'string') {\n row.path = event[0];\n if (row.event_type !== 'DELETED') {\n row.path += '
View'; // eslint-disable-line max-len\n }\n }\n if (typeof event[1] === 'string') {\n let d = new Date(Number(event[1]) * 1000);\n shortMonthName = new Intl.DateTimeFormat('en-US', {month: 'short'}).format;\n let minutes = String(d.getMinutes()).padStart(2, '0');\n let seconds = String(d.getSeconds()).padStart(2, '0');\n row.date = shortMonthName(d) +\n ' ' +\n d.getDate() +\n ' ' +\n d.getFullYear() +\n ' ' +\n d.getHours() +\n ':' +\n minutes +\n ':' +\n seconds;\n }\n }\n\n return row;\n}\n\n/**\n * Render the row of FSW table.\n * @param {string} path the file path\n * @param {string} eventType the event type\n * @param {string} date the date of event\n */\nfunction renderFSWTableRow(path, eventType, date) {\n if (eventType === 'no_changes') {\n let tr = document.createElement('tr');\n let td = document.createElement('td');\n td.setAttribute('name', 'fswatcher-event-no-changes');\n td.setAttribute('colspan', '3');\n td.innerText = fswatcherTranslations['fs_no_changes'];\n tr.appendChild(td);\n fsWatcherTableBody.appendChild(tr);\n return;\n }\n\n let tr = document.createElement('tr');\n\n let tdPath = document.createElement('td');\n tdPath.setAttribute('name', 'fswatcher-event-path');\n tdPath.setAttribute('data-before', 'Path');\n tdPath.innerHTML = path;\n tr.appendChild(tdPath);\n\n let tdType = document.createElement('td');\n tdType.setAttribute('name', 'fswatcher-event-type');\n tdType.setAttribute('data-before', 'Event');\n tdType.innerText = eventType;\n tr.appendChild(tdType);\n\n let tdDate = document.createElement('td');\n tdDate.setAttribute('name', 'fswatcher-event-date');\n tdDate.setAttribute('data-before', 'Changed on date');\n tdDate.innerText = date;\n tr.appendChild(tdDate);\n\n fsWatcherTableBody.appendChild(tr);\n}\n\n/**\n * Filter options for the first selector and disable it to keep it from changes.\n */\nfunction filterFSWSecondSelector() {\n toggleFSWSelectorsInfo(false);\n}\n\n/**\n * Filter options for the second selector and disable it to keep it from changes.\n */\nfunction filterFSWFirstSelector() {\n toggleFSWSelectorsInfo(false);\n}\n\n/**\n * Reset selectors to its initial statements.\n */\nfunction resetFSWSelectors() {\n for (let i = 0; i < firstFSWSelector.options.length; i++) {\n firstFSWSelector.options[i].style.display = 'inherit';\n }\n for (let i = 0; i < secondFSWSelector.options.length; i++) {\n secondFSWSelector.options[i].style.display = 'inherit';\n }\n secondFSWSelector.removeAttribute('disabled');\n firstFSWSelector.removeAttribute('disabled');\n}\n\n/**\n * Toggle info string.\n * @param {boolean} enable Set logs names if true, disable content if false.\n */\nfunction toggleFSWSelectorsInfo(enable) {\n let infoTag = document.getElementById('spbc--fs-watcher-table-handling-selects-info');\n if (\n enable &&\n typeof firstFSWSelector.options[firstFSWSelector.selectedIndex] !== 'undefined' &&\n typeof secondFSWSelector.options[secondFSWSelector.selectedIndex] !== 'undefined'\n ) {\n const changesCountOnTRS = document.querySelectorAll('#spbc-table-fs_watcher-comparison > tr').length;\n const hasNoChangesTD = document.getElementsByName('fswatcher-event-no-changes').length;\n const changesCount = hasNoChangesTD > 0 ? 0 : changesCountOnTRS;\n\n infoTag.style.display = 'inherit';\n infoTag.innerHTML= fswatcherTranslations['fs_comparing'] +\n '
' + firstFSWSelector.options[firstFSWSelector.selectedIndex].text + ' ' +\n fswatcherTranslations['fs_with'] +\n '
' + secondFSWSelector.options[secondFSWSelector.selectedIndex].text + ' ' +\n fswatcherTranslations['fs_total'] +\n '
' + changesCount + '';\n } else {\n infoTag.innerText = '';\n infoTag.style.display = 'none';\n }\n}\n\n\n"],"names":["let","noFSWChangesDetected","firstFSWSelector","document","getElementById","secondFSWSelector","fsWatcherTableBody","availableFSWDataSetNames","FSWOnload","querySelector","selectedIndex","secondDate","options","length","FSWCompare","Event","e","preventDefault","currentTarget","style","display","firstDate","value","spbcSendAJAXRequest","action","fswatcher__first_date","fswatcher__second_date","callback","response","responseDataObj","FSWDecodeJSON","hasOwnProperty","alert","fswatcherTranslations","console","log","error","validateResult","validateFSWResponse","renderFSWatcherTableContent","renderFSWTableRow","resetFSWSelectors","toggleFSWSelectorsInfo","FSWCreate","params","button","target","timeout","FSWViewFile","el","wpWrap","jQuery","dialogWindow","spinner","dialog","modal","title","dataset","path","position","my","at","of","window","width","show","effect","duration","draggable","resizable","closeText","classes","ui-dialog","open","event","ui","overflow","height","documentElement","beforeClose","body","empty","append","sizeMultiplier","firstSelectorId","css","left","top","val","secondSelectorId","data","fswatcher_file_path","content","split","forEach","line","index","printf","contentHeight","Object","keys","visibleHeight","JSON","parse","innerHTML","dataSetName","handleFSWDataObject","eventType","eventsArray","i","row","convertFSWEventToRow","event_type","date","minutes","seconds","toUpperCase","d","Date","Number","shortMonthName","Intl","DateTimeFormat","month","format","String","getMinutes","padStart","getSeconds","getDate","getFullYear","getHours","tr","tdDate","createElement","td","setAttribute","innerText","appendChild","tdPath","tdType","filterFSWSecondSelector","filterFSWFirstSelector","removeAttribute","enable","infoTag","changesCountOnTRS","querySelectorAll","changesCount","getElementsByName","text","readyState","addEventListener"],"mappings":"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"}
\ No newline at end of file
diff --git a/js/src/spbc-settings_tab--fswatcher.js b/js/src/spbc-settings_tab--fswatcher.js
new file mode 100644
index 000000000..4213cd019
--- /dev/null
+++ b/js/src/spbc-settings_tab--fswatcher.js
@@ -0,0 +1,425 @@
+// if no changes detected after comparison
+let noFSWChangesDetected = true;
+// first selector elem
+const firstFSWSelector = document.getElementById('fswatcher__first_date');
+// second selector elem
+const secondFSWSelector = document.getElementById('fswatcher__second_date');
+// FSW table body
+const fsWatcherTableBody = document.getElementById('spbc-table-fs_watcher-comparison');
+// available types of events
+const availableFSWDataSetNames = ['added', 'changed', 'deleted'];
+
+if ( document.readyState !== 'loading' ) {
+ FSWOnload(); // eslint-disable-line new-cap
+} else {
+ document.addEventListener('DOMContentLoaded', FSWOnload);
+}
+
+// listeners
+document.getElementById('fswatcher__compare').addEventListener('click', FSWCompare);
+document.getElementById('fswatcher__create_snapshot').addEventListener('click', FSWCreate);
+firstFSWSelector.addEventListener('change', filterFSWSecondSelector);
+secondFSWSelector.addEventListener('change', filterFSWFirstSelector);
+
+// eslint-disable-next-line require-jsdoc
+function FSWOnload() {
+ document.querySelector('#fswatcher__first_date').selectedIndex = 0;
+ let secondDate = document.querySelector('#fswatcher__second_date');
+ secondDate.selectedIndex = secondDate.options.length - 1;
+ FSWCompare(new Event({})); // eslint-disable-line new-cap
+}
+
+/**
+ * Main handler function. Run this on the button click.
+ * @param {Event} e click event.
+ * @return {void|false}
+ */
+function FSWCompare(e) {
+ e.preventDefault();
+
+ if (e.currentTarget !== null) {
+ document.querySelector('#fsw_preloader_compare').style.display = 'inline';
+ }
+
+ if ( typeof document.getElementById('fswatcher__first_date') === 'undefined' ||
+ typeof document.getElementById('fswatcher__second_date') === 'undefined' ) {
+ return false;
+ }
+
+ let firstDate = document.getElementById('fswatcher__first_date').value;
+ let secondDate = document.getElementById('fswatcher__second_date').value;
+
+ let data = {
+ action: 'spbct_fswatcher_compare',
+ fswatcher__first_date: firstDate,
+ fswatcher__second_date: secondDate,
+ };
+
+ let params = {
+ callback: function(response) {
+ let responseDataObj = {};
+ noFSWChangesDetected = true;
+ if (typeof response === 'object') {
+ responseDataObj = response;
+ } else if (typeof response === 'string') {
+ responseDataObj = FSWDecodeJSON(response); // eslint-disable-line new-cap
+ if (responseDataObj.hasOwnProperty('error')) {
+ alert(fswatcherTranslations['fs_err_parse_json']);
+ console.log('File System watcher JSON parse error: ' + responseDataObj.error);
+ return;
+ }
+ }
+
+ const validateResult = validateFSWResponse(responseDataObj);
+ if (true === validateResult) {
+ renderFSWatcherTableContent(responseDataObj);
+ if (noFSWChangesDetected) {
+ renderFSWTableRow( '', 'no_changes', '');
+ }
+ } else {
+ alert(validateResult + ' ' + fswatcherTranslations['fs_err_valid_result'] + ' support@cleantalk.org');
+ console.log('File System watcher response validating error: ' + validateResult);
+ }
+ resetFSWSelectors();
+ toggleFSWSelectorsInfo(true);
+ document.querySelector('#fsw_preloader_compare').style.display = 'none';
+ },
+ };
+
+ spbcSendAJAXRequest(data, params);
+}
+
+/**
+ * Create snapshot handler.
+ * @param {Event} e
+ * @constructor
+ */
+function FSWCreate(e) {
+ e.preventDefault();
+
+ if (e.currentTarget !== null) {
+ document.querySelector('#fsw_preloader_create').style.display = 'inline';
+ }
+
+ let data = {
+ action: 'spbct_fswatcher_create_snapshot',
+ };
+
+ let params = {
+ callback: function(response) {
+ if (typeof response === 'string') {
+ FSWDecodeJSON(response); // eslint-disable-line new-cap
+ }
+ document.querySelector('#fsw_preloader_create').style.display = 'none';
+ },
+ button: e.target,
+ timeout: 30000,
+ };
+
+ spbcSendAJAXRequest(data, params);
+}
+
+/**
+ * View file content in modal window.
+ * @param {HTMLElement} el
+ * @constructor
+ */
+function FSWViewFile(el) { // eslint-disable-line no-unused-vars
+ let wpWrap = jQuery('#wpwrap');
+ let dialogWindow = jQuery('#spbc_dialog');
+
+ dialogWindow.dialog({
+ modal: true,
+ title: fswatcherTranslations['fs_modal'] + ' ' + el.dataset.path,
+ position: {my: 'center top', at: 'center top+100px', of: window},
+ width: +(wpWrap.width() / 100 * 90),
+ show: {effect: 'blind', duration: 500},
+ draggable: false,
+ resizable: false,
+ closeText: 'X',
+ classes: {'ui-dialog': 'spbc---top'},
+ open: function(event, ui) {
+ event.target.style.overflow = 'auto';
+ jQuery('#spbc_dialog').height((document.documentElement.clientHeight) / 100 * 25);
+ },
+ beforeClose: function(event, ui) {
+ document.body.style.overflow = 'auto';
+ jQuery('#spbc_dialog').empty();
+ },
+ });
+
+ dialogWindow.append('
');
+
+ let spinner = jQuery('#spbc_file_view_preloader');
+ let sizeMultiplier = (wpWrap.width() * 0.0004);
+
+ spinner.height(128 * sizeMultiplier);
+ spinner.width(128 * sizeMultiplier);
+ spinner.css({left: dialogWindow.width()/2 - (128 * sizeMultiplier / 2)});
+ spinner.css({top: dialogWindow.height()/2 - (128 * sizeMultiplier / 2)});
+
+ const firstSelectorId = jQuery('#fswatcher__first_date').val();
+ const secondSelectorId = jQuery('#fswatcher__second_date').val();
+
+ let data = {
+ action: 'spbct_fswatcher_view_file',
+ fswatcher_file_path: el.dataset.path,
+ fswatcher__first_date: firstSelectorId,
+ fswatcher__second_date: secondSelectorId,
+ };
+
+ let callback = function(response) {
+ let content = '';
+ if (typeof response.error !== 'undefined') {
+ content = response.error;
+ } else if (typeof response.data !== 'undefined') {
+ content = response.data;
+ } else {
+ content = 'Unknown error on reading file. Data is empty.';
+ }
+ content = content.split('\n');
+ let dialogWindow = jQuery('#spbc_dialog');
+ dialogWindow.empty();
+ jQuery('#spbc_file_view_preloader').css({display: 'none'});
+ let rowTemplate = '
' +
+ '
%s' +
+ '
%s
' +
+ '
<' +
+ '/div>';
+
+ content.forEach((line, index) => {
+ dialogWindow.append(rowTemplate.printf(index + 1, line));
+ });
+
+ let contentHeight = Object.keys(content).length * 19 < 76 ? 76 : Object.keys(content).length * 19;
+ let visibleHeight = (document.documentElement.clientHeight) / 100 * 75;
+ let overflow = contentHeight < visibleHeight ? 'hidden' : 'scroll';
+ let height = overflow === 'scroll' ? visibleHeight : contentHeight;
+
+ dialogWindow.css({
+ height: height,
+ overflow: overflow,
+ });
+ };
+
+ spbcSendAJAXRequest(data, {callback: callback});
+}
+
+/**
+ * Try to decode JSON string from site response.
+ * @param {string} response
+ * @return {obj} Json parsed obj or error obj.
+ */
+function FSWDecodeJSON(response) {
+ try {
+ return JSON.parse(response);
+ } catch (e) {
+ return {'error': e};
+ }
+}
+
+/**
+ * Run rendering comparison table in dependence of response object
+ * @param {{}} responseDataObj
+ */
+function renderFSWatcherTableContent(responseDataObj) {
+ fsWatcherTableBody.innerHTML = '';
+ for (const dataSetName of availableFSWDataSetNames) {
+ if (handleFSWDataObject(responseDataObj, dataSetName)) {
+ noFSWChangesDetected = false;
+ }
+ }
+}
+
+/**
+ * Validate response object from site.
+ * @param {obj} responseDataObj
+ * @return {*|boolean}
+ */
+function validateFSWResponse(responseDataObj) {
+ if (
+ !responseDataObj ||
+ typeof responseDataObj !== 'object'
+ ) {
+ return fswatcherTranslations['fs_err_resp_obj'];
+ }
+
+ if (typeof responseDataObj.error !== 'undefined') {
+ return responseDataObj.error;
+ }
+
+ for (const dataSetName of availableFSWDataSetNames) {
+ if (
+ !responseDataObj.hasOwnProperty(dataSetName)
+ ) {
+ return fswatcherTranslations['fs_err_property'];
+ }
+ }
+
+ return true;
+}
+
+/**
+ * @param {object} responseDataObj
+ * @param {string|number} eventType
+ * @return {boolean}
+ */
+function handleFSWDataObject(responseDataObj, eventType) {
+ const eventsArray = responseDataObj[eventType];
+ if (eventsArray.length > 0) {
+ for (let i = 0; i < eventsArray.length; i++) {
+ const row = convertFSWEventToRow(eventsArray[i], eventType);
+ renderFSWTableRow(row.path, row.event_type, row.date);
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Convert a row of site response to the formatted data.
+ * @param {object} event contains the date and the file path
+ * @param {string} eventType contains event type
+ * @return {{path: string, event_type: string, date: string}} row of the table
+ */
+function convertFSWEventToRow(event, eventType) {
+ let row = {
+ 'path': 'unknown',
+ 'event_type': eventType.toUpperCase(),
+ 'date': 'unknown',
+ };
+
+ if (event.length === 2) {
+ if (typeof event[0] === 'string') {
+ row.path = event[0];
+ if (row.event_type !== 'DELETED') {
+ row.path += '
View'; // eslint-disable-line max-len
+ }
+ }
+ if (typeof event[1] === 'string') {
+ let d = new Date(Number(event[1]) * 1000);
+ shortMonthName = new Intl.DateTimeFormat('en-US', {month: 'short'}).format;
+ let minutes = String(d.getMinutes()).padStart(2, '0');
+ let seconds = String(d.getSeconds()).padStart(2, '0');
+ row.date = shortMonthName(d) +
+ ' ' +
+ d.getDate() +
+ ' ' +
+ d.getFullYear() +
+ ' ' +
+ d.getHours() +
+ ':' +
+ minutes +
+ ':' +
+ seconds;
+ }
+ }
+
+ return row;
+}
+
+/**
+ * Render the row of FSW table.
+ * @param {string} path the file path
+ * @param {string} eventType the event type
+ * @param {string} date the date of event
+ */
+function renderFSWTableRow(path, eventType, date) {
+ if (eventType === 'no_changes') {
+ let tr = document.createElement('tr');
+ let td = document.createElement('td');
+ td.setAttribute('name', 'fswatcher-event-no-changes');
+ td.setAttribute('colspan', '3');
+ td.innerText = fswatcherTranslations['fs_no_changes'];
+ tr.appendChild(td);
+ fsWatcherTableBody.appendChild(tr);
+ return;
+ }
+
+ let tr = document.createElement('tr');
+
+ let tdPath = document.createElement('td');
+ tdPath.setAttribute('name', 'fswatcher-event-path');
+ tdPath.setAttribute('data-before', 'Path');
+ tdPath.innerHTML = path;
+ tr.appendChild(tdPath);
+
+ let tdType = document.createElement('td');
+ tdType.setAttribute('name', 'fswatcher-event-type');
+ tdType.setAttribute('data-before', 'Event');
+ tdType.innerText = eventType;
+ tr.appendChild(tdType);
+
+ let tdDate = document.createElement('td');
+ tdDate.setAttribute('name', 'fswatcher-event-date');
+ tdDate.setAttribute('data-before', 'Changed on date');
+ tdDate.innerText = date;
+ tr.appendChild(tdDate);
+
+ fsWatcherTableBody.appendChild(tr);
+}
+
+/**
+ * Filter options for the first selector and disable it to keep it from changes.
+ */
+function filterFSWSecondSelector() {
+ toggleFSWSelectorsInfo(false);
+}
+
+/**
+ * Filter options for the second selector and disable it to keep it from changes.
+ */
+function filterFSWFirstSelector() {
+ toggleFSWSelectorsInfo(false);
+}
+
+/**
+ * Reset selectors to its initial statements.
+ */
+function resetFSWSelectors() {
+ for (let i = 0; i < firstFSWSelector.options.length; i++) {
+ firstFSWSelector.options[i].style.display = 'inherit';
+ }
+ for (let i = 0; i < secondFSWSelector.options.length; i++) {
+ secondFSWSelector.options[i].style.display = 'inherit';
+ }
+ secondFSWSelector.removeAttribute('disabled');
+ firstFSWSelector.removeAttribute('disabled');
+}
+
+/**
+ * Toggle info string.
+ * @param {boolean} enable Set logs names if true, disable content if false.
+ */
+function toggleFSWSelectorsInfo(enable) {
+ let infoTag = document.getElementById('spbc--fs-watcher-table-handling-selects-info');
+ if (
+ enable &&
+ typeof firstFSWSelector.options[firstFSWSelector.selectedIndex] !== 'undefined' &&
+ typeof secondFSWSelector.options[secondFSWSelector.selectedIndex] !== 'undefined'
+ ) {
+ const changesCountOnTRS = document.querySelectorAll('#spbc-table-fs_watcher-comparison > tr').length;
+ const hasNoChangesTD = document.getElementsByName('fswatcher-event-no-changes').length;
+ const changesCount = hasNoChangesTD > 0 ? 0 : changesCountOnTRS;
+
+ infoTag.style.display = 'inherit';
+ infoTag.innerHTML= fswatcherTranslations['fs_comparing'] +
+ '
' + firstFSWSelector.options[firstFSWSelector.selectedIndex].text + ' ' +
+ fswatcherTranslations['fs_with'] +
+ '
' + secondFSWSelector.options[secondFSWSelector.selectedIndex].text + ' ' +
+ fswatcherTranslations['fs_total'] +
+ '
' + changesCount + '';
+ } else {
+ infoTag.innerText = '';
+ infoTag.style.display = 'none';
+ }
+}
+
+
diff --git a/lib/CleantalkSP/Common/FSWatcher/Analyzer/index.php b/lib/CleantalkSP/Common/FSWatcher/Analyzer/index.php
deleted file mode 100644
index b3d9bbc7f..000000000
--- a/lib/CleantalkSP/Common/FSWatcher/Analyzer/index.php
+++ /dev/null
@@ -1 +0,0 @@
- 'Can not compare logs'));
- } else {
- echo json_encode($compare_result);
- }
- die();
- }
-
- if (Service::isCreateSnapshotRequest()) {
- if (self::$debug) {
- Logger::log('run scan file system');
- }
- self::run($params);
- die(json_encode('OK'));
- }
- }
-
- /**
- * Scanning file system handler
- *
- * @param $params
- * @return void
- */
- protected static function run($params)
- {
- self::$status = self::STATUS_RUNNING;
- Scan::run($params);
- self::stop();
- }
-
- /**
- * Scanning file system stop trigger
- *
- * @return void
- */
- private static function stop()
- {
- self::$status = self::STATUS_STOPPED;
- Service::setAllJournalsAsCompleted();
- }
-
- /**
- * Checking status of the scanning process
- *
- * @return string
- */
- private static function status()
- {
- $is_exist = Service::getProcessingJournal();
- if (!is_null($is_exist)) {
- self::$status = self::STATUS_RUNNING;
- }
-
- return self::$status;
- }
-}
diff --git a/lib/CleantalkSP/Common/FSWatcher/Repository/FileRepository.php b/lib/CleantalkSP/Common/FSWatcher/Repository/FileRepository.php
deleted file mode 100755
index df6f9cc2c..000000000
--- a/lib/CleantalkSP/Common/FSWatcher/Repository/FileRepository.php
+++ /dev/null
@@ -1,20 +0,0 @@
- $interval;
- }
-
- /**
- * Attach JS file for start ajax call
- *
- * @param $buffer string
- * @return string
- *
- * @psalm-suppress PossiblyUnusedReturnValue
- */
- public static function attachJS($buffer, $file_to_get_md5 = null)
- {
- if (empty($file_to_get_md5)) {
- $file_to_get_md5 = __FILE__;
- }
- $is_ajax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
- $is_html = preg_match('/^\s*(var fswatcherToken = "' . md5((string)filemtime($file_to_get_md5)) . '";'
- . ''
- . '';
-
- $buffer = preg_replace(
- '/<\/body>(\s|<.*>)*<\/html>\s*$/i',
- $addition . '