diff --git a/inc/spbc-settings.php b/inc/spbc-settings.php index 6d8c8600b..c2bfa224c 100644 --- a/inc/spbc-settings.php +++ b/inc/spbc-settings.php @@ -1090,7 +1090,8 @@ function spbc_settings__register() ), ), ), - 'display' => $spbc->settings['scanner__fs_watcher'] + 'display' => $spbc->settings['scanner__fs_watcher'], + 'js_after' => 'settings_tab--fswatcher.min.js', ), // Debug 'debug' => array( diff --git a/js/spbc-settings_tab--fswatcher.min.js b/js/spbc-settings_tab--fswatcher.min.js new file mode 100644 index 000000000..c490eb54b --- /dev/null +++ b/js/spbc-settings_tab--fswatcher.min.js @@ -0,0 +1,2 @@ +let noFSWChangesDetected=!0;const firstFSWSelector=document.getElementById("fswatcher__first_date"),secondFSWSelector=document.getElementById("fswatcher__second_date"),fsWatcherTableBody=document.getElementById("spbc-table-fs_watcher-comparison"),availableFSWDataSetNames=["added","changed","deleted"];function FSWOnload(){document.querySelector("#fswatcher__first_date").selectedIndex=0;var e=document.querySelector("#fswatcher__second_date");e.selectedIndex=e.options.length-1,FSWCompare(new Event({}))}function FSWCompare(e){if(e.preventDefault(),null!==e.currentTarget&&(document.querySelector("#fsw_preloader_compare").style.display="inline"),void 0===document.getElementById("fswatcher__first_date")||void 0===document.getElementById("fswatcher__second_date"))return!1;var e=document.getElementById("fswatcher__first_date").value,t=document.getElementById("fswatcher__second_date").value;spbcSendAJAXRequest({action:"spbct_fswatcher_compare",fswatcher__first_date:e,fswatcher__second_date:t},{callback:function(e){let t={};if(noFSWChangesDetected=!0,"object"==typeof e)t=e;else if("string"==typeof e&&(t=FSWDecodeJSON(e)).hasOwnProperty("error"))return alert(fswatcherTranslations.fs_err_parse_json),void console.log("File System watcher JSON parse error: "+t.error);e=validateFSWResponse(t);!0===e?(renderFSWatcherTableContent(t),noFSWChangesDetected&&renderFSWTableRow("","no_changes","")):(alert(e+" "+fswatcherTranslations.fs_err_valid_result+" support@cleantalk.org"),console.log("File System watcher response validating error: "+e)),resetFSWSelectors(),toggleFSWSelectorsInfo(!0),document.querySelector("#fsw_preloader_compare").style.display="none"}})}function FSWCreate(e){e.preventDefault(),null!==e.currentTarget&&(document.querySelector("#fsw_preloader_create").style.display="inline");e={callback:function(e){"string"==typeof e&&FSWDecodeJSON(e),document.querySelector("#fsw_preloader_create").style.display="none"},button:e.target,timeout:3e4};spbcSendAJAXRequest({action:"spbct_fswatcher_create_snapshot"},e)}function FSWViewFile(e){var t=jQuery("#wpwrap"),r=jQuery("#spbc_dialog"),n=(r.dialog({modal:!0,title:fswatcherTranslations.fs_modal+" "+e.dataset.path,position:{my:"center top",at:"center top+100px",of:window},width:t.width()/100*90,show:{effect:"blind",duration:500},draggable:!1,resizable:!1,closeText:"X",classes:{"ui-dialog":"spbc---top"},open:function(e,t){e.target.style.overflow="auto",jQuery("#spbc_dialog").height(document.documentElement.clientHeight/100*25)},beforeClose:function(e,t){document.body.style.overflow="auto",jQuery("#spbc_dialog").empty()}}),r.append('Wait for downloading'),jQuery("#spbc_file_view_preloader")),t=4e-4*t.width(),n=(n.height(128*t),n.width(128*t),n.css({left:r.width()/2-128*t/2}),n.css({top:r.height()/2-128*t/2}),jQuery("#fswatcher__first_date").val()),r=jQuery("#fswatcher__second_date").val(),t={action:"spbct_fswatcher_view_file",fswatcher_file_path:e.dataset.path,fswatcher__first_date:n,fswatcher__second_date:r};spbcSendAJAXRequest(t,{callback:function(e){let t="",r=(t=(t=void 0!==e.error?e.error:void 0!==e.data?e.data:"Unknown error on reading file. Data is empty.").split("\n"),jQuery("#spbc_dialog"));r.empty(),jQuery("#spbc_file_view_preloader").css({display:"none"});t.forEach((e,t)=>{r.append('
%s

%s


'.printf(t+1,e))});var e=19*Object.keys(t).length<76?76:19*Object.keys(t).length,n=document.documentElement.clientHeight/100*75,o=eView'),"string"==typeof e[1])&&(e=new Date(1e3*Number(e[1])),shortMonthName=new Intl.DateTimeFormat("en-US",{month:"short"}).format,r=String(e.getMinutes()).padStart(2,"0"),n=String(e.getSeconds()).padStart(2,"0"),t.date=shortMonthName(e)+" "+e.getDate()+" "+e.getFullYear()+" "+e.getHours()+":"+r+":"+n),t}function renderFSWTableRow(e,t,r){var n,o;"no_changes"===t?(o=document.createElement("tr"),(n=document.createElement("td")).setAttribute("name","fswatcher-event-no-changes"),n.setAttribute("colspan","3"),n.innerText=fswatcherTranslations.fs_no_changes,o.appendChild(n),fsWatcherTableBody.appendChild(o)):(n=document.createElement("tr"),(o=document.createElement("td")).setAttribute("name","fswatcher-event-path"),o.setAttribute("data-before","Path"),o.innerHTML=e,n.appendChild(o),(e=document.createElement("td")).setAttribute("name","fswatcher-event-type"),e.setAttribute("data-before","Event"),e.innerText=t,n.appendChild(e),(o=document.createElement("td")).setAttribute("name","fswatcher-event-date"),o.setAttribute("data-before","Changed on date"),o.innerText=r,n.appendChild(o),fsWatcherTableBody.appendChild(n))}function filterFSWSecondSelector(){toggleFSWSelectorsInfo(!1)}function filterFSWFirstSelector(){toggleFSWSelectorsInfo(!1)}function resetFSWSelectors(){for(let e=0;e tr").length,e=0"+firstFSWSelector.options[firstFSWSelector.selectedIndex].text+" "+fswatcherTranslations.fs_with+" "+secondFSWSelector.options[secondFSWSelector.selectedIndex].text+" "+fswatcherTranslations.fs_total+" "+e+""):(t.innerText="",t.style.display="none")}"loading"!==document.readyState?FSWOnload():document.addEventListener("DOMContentLoaded",FSWOnload),document.getElementById("fswatcher__compare").addEventListener("click",FSWCompare),document.getElementById("fswatcher__create_snapshot").addEventListener("click",FSWCreate),firstFSWSelector.addEventListener("change",filterFSWSecondSelector),secondFSWSelector.addEventListener("change",filterFSWFirstSelector); +//# sourceMappingURL=spbc-settings_tab--fswatcher.min.js.map diff --git a/js/spbc-settings_tab--fswatcher.min.js.map b/js/spbc-settings_tab--fswatcher.min.js.map new file mode 100644 index 000000000..b79524f5c --- /dev/null +++ b/js/spbc-settings_tab--fswatcher.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"spbc-settings_tab--fswatcher.min.js","sources":["spbc-settings_tab--fswatcher.js"],"sourcesContent":["// if no changes detected after comparison\nlet noFSWChangesDetected = true;\n// first selector elem\nconst firstFSWSelector = document.getElementById('fswatcher__first_date');\n// second selector elem\nconst secondFSWSelector = document.getElementById('fswatcher__second_date');\n// FSW table body\nconst fsWatcherTableBody = document.getElementById('spbc-table-fs_watcher-comparison');\n// available types of events\nconst availableFSWDataSetNames = ['added', 'changed', 'deleted'];\n\nif ( document.readyState !== 'loading' ) {\n FSWOnload(); // eslint-disable-line new-cap\n} else {\n document.addEventListener('DOMContentLoaded', FSWOnload);\n}\n\n// listeners\ndocument.getElementById('fswatcher__compare').addEventListener('click', FSWCompare);\ndocument.getElementById('fswatcher__create_snapshot').addEventListener('click', FSWCreate);\nfirstFSWSelector.addEventListener('change', filterFSWSecondSelector);\nsecondFSWSelector.addEventListener('change', filterFSWFirstSelector);\n\n// eslint-disable-next-line require-jsdoc\nfunction FSWOnload() {\n document.querySelector('#fswatcher__first_date').selectedIndex = 0;\n let secondDate = document.querySelector('#fswatcher__second_date');\n secondDate.selectedIndex = secondDate.options.length - 1;\n FSWCompare(new Event({})); // eslint-disable-line new-cap\n}\n\n/**\n * Main handler function. Run this on the button click.\n * @param {Event} e click event.\n * @return {void|false}\n */\nfunction FSWCompare(e) {\n e.preventDefault();\n\n if (e.currentTarget !== null) {\n document.querySelector('#fsw_preloader_compare').style.display = 'inline';\n }\n\n if ( typeof document.getElementById('fswatcher__first_date') === 'undefined' ||\n typeof document.getElementById('fswatcher__second_date') === 'undefined' ) {\n return false;\n }\n\n let firstDate = document.getElementById('fswatcher__first_date').value;\n let secondDate = document.getElementById('fswatcher__second_date').value;\n\n let data = {\n action: 'spbct_fswatcher_compare',\n fswatcher__first_date: firstDate,\n fswatcher__second_date: secondDate,\n };\n\n let params = {\n callback: function(response) {\n let responseDataObj = {};\n noFSWChangesDetected = true;\n if (typeof response === 'object') {\n responseDataObj = response;\n } else if (typeof response === 'string') {\n responseDataObj = FSWDecodeJSON(response); // eslint-disable-line new-cap\n if (responseDataObj.hasOwnProperty('error')) {\n alert(fswatcherTranslations['fs_err_parse_json']);\n console.log('File System watcher JSON parse error: ' + responseDataObj.error);\n return;\n }\n }\n\n const validateResult = validateFSWResponse(responseDataObj);\n if (true === validateResult) {\n renderFSWatcherTableContent(responseDataObj);\n if (noFSWChangesDetected) {\n renderFSWTableRow( '', 'no_changes', '');\n }\n } else {\n alert(validateResult + ' ' + fswatcherTranslations['fs_err_valid_result'] + ' support@cleantalk.org');\n console.log('File System watcher response validating error: ' + validateResult);\n }\n resetFSWSelectors();\n toggleFSWSelectorsInfo(true);\n document.querySelector('#fsw_preloader_compare').style.display = 'none';\n },\n };\n\n spbcSendAJAXRequest(data, params);\n}\n\n/**\n * Create snapshot handler.\n * @param {Event} e\n * @constructor\n */\nfunction FSWCreate(e) {\n e.preventDefault();\n\n if (e.currentTarget !== null) {\n document.querySelector('#fsw_preloader_create').style.display = 'inline';\n }\n\n let data = {\n action: 'spbct_fswatcher_create_snapshot',\n };\n\n let params = {\n callback: function(response) {\n if (typeof response === 'string') {\n FSWDecodeJSON(response); // eslint-disable-line new-cap\n }\n document.querySelector('#fsw_preloader_create').style.display = 'none';\n },\n button: e.target,\n timeout: 30000,\n };\n\n spbcSendAJAXRequest(data, params);\n}\n\n/**\n * View file content in modal window.\n * @param {HTMLElement} el\n * @constructor\n */\nfunction FSWViewFile(el) { // eslint-disable-line no-unused-vars\n let wpWrap = jQuery('#wpwrap');\n let dialogWindow = jQuery('#spbc_dialog');\n\n dialogWindow.dialog({\n modal: true,\n title: fswatcherTranslations['fs_modal'] + ' ' + el.dataset.path,\n position: {my: 'center top', at: 'center top+100px', of: window},\n width: +(wpWrap.width() / 100 * 90),\n show: {effect: 'blind', duration: 500},\n draggable: false,\n resizable: false,\n closeText: 'X',\n classes: {'ui-dialog': 'spbc---top'},\n open: function(event, ui) {\n event.target.style.overflow = 'auto';\n jQuery('#spbc_dialog').height((document.documentElement.clientHeight) / 100 * 25);\n },\n beforeClose: function(event, ui) {\n document.body.style.overflow = 'auto';\n jQuery('#spbc_dialog').empty();\n },\n });\n\n dialogWindow.append('\"Wait');\n\n let spinner = jQuery('#spbc_file_view_preloader');\n let sizeMultiplier = (wpWrap.width() * 0.0004);\n\n spinner.height(128 * sizeMultiplier);\n spinner.width(128 * sizeMultiplier);\n spinner.css({left: dialogWindow.width()/2 - (128 * sizeMultiplier / 2)});\n spinner.css({top: dialogWindow.height()/2 - (128 * sizeMultiplier / 2)});\n\n const firstSelectorId = jQuery('#fswatcher__first_date').val();\n const secondSelectorId = jQuery('#fswatcher__second_date').val();\n\n let data = {\n action: 'spbct_fswatcher_view_file',\n fswatcher_file_path: el.dataset.path,\n fswatcher__first_date: firstSelectorId,\n fswatcher__second_date: secondSelectorId,\n };\n\n let callback = function(response) {\n let content = '';\n if (typeof response.error !== 'undefined') {\n content = response.error;\n } else if (typeof response.data !== 'undefined') {\n content = response.data;\n } else {\n content = 'Unknown error on reading file. Data is empty.';\n }\n content = content.split('\\n');\n let dialogWindow = jQuery('#spbc_dialog');\n dialogWindow.empty();\n jQuery('#spbc_file_view_preloader').css({display: 'none'});\n let rowTemplate = '
' +\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('Wait for downloading'); + + 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 . '', - $buffer, - 1 - ); - } - - return $buffer; - } - - /** - * Is ajax call is in process - * - * @return bool - */ - public static function isRC() - { - return static::validateFsWatcherToken(); - } - - /** - * Checking request validity: view file - * - * @return bool - */ - public static function isViewFileRequest() - { - if (isset($_POST['fswatcher_view_file']) && $_POST['fswatcher_view_file'] == true && - isset($_POST['fswatcher_file_path']) && strlen($_POST['fswatcher_file_path']) > 1 - ) { - return true; - } - - return false; - } - - /** - * Checking request validity: Comparing logs - * - * @return bool - */ - public static function isCompareRequest() - { - if (isset($_POST['fswatcher_compare']) && $_POST['fswatcher_compare'] == true && - isset($_POST['fswatcher__first_date']) && filter_var($_POST['fswatcher__first_date'], FILTER_VALIDATE_INT) !== false && - isset($_POST['fswatcher__second_date']) && filter_var($_POST['fswatcher__second_date'], FILTER_VALIDATE_INT) !== false - ) { - return true; - } - - return false; - } - - /** - * Checking request validity: Creating Snapshot - * - * @return bool - */ - public static function isCreateSnapshotRequest() - { - return isset($_POST['fswatcher_create_snapshot']) && $_POST['fswatcher_create_snapshot'] == true; - } - - /** - * Set snapshots to completed status - * - * @return void - */ - public static function setAllJournalsAsCompleted() - { - $storage = Controller::$storage; - $storage::setAllJournalsAsCompleted(); - } - - /** - * Get snapshots which is in process - * - * @return string|null - */ - public static function getProcessingJournal() - { - $storage = Controller::$storage; - return $storage::getProcessingJournal(); - } - - /** - * Generates token (aka nonce). - * The $salt must be used obligatorily - * - * @param $salt - * - * @return string - */ - public static function generateFsWatcherToken($salt = '') - { - return md5(filemtime(__FILE__) . $salt); - } - - public static function validateFsWatcherToken() - { - return isset($_POST['fswatcher_token']) && $_POST['fswatcher_token'] === static::generateFsWatcherToken(); - } - - /** - * Is rate limit pass - * Must be overridden in child class - * - * @return bool - */ - public static function isRateLimitPass() - { - return true; - } -} diff --git a/lib/CleantalkSP/Common/FSWatcher/Storage/FileStorage.php b/lib/CleantalkSP/Common/FSWatcher/Storage/FileStorage.php deleted file mode 100755 index c14b4d7b2..000000000 --- a/lib/CleantalkSP/Common/FSWatcher/Storage/FileStorage.php +++ /dev/null @@ -1,188 +0,0 @@ - $dir) { - if ($dir->isDir() && !in_array($dir->getFilename(), $exclude_dirs)) { - $iterator->next(); - } else { - if (in_array($dir->getExtension(), $extensions_to_watch)) { - $mtime = @filemtime((string)$path); - if ( empty($mtime) ) { - clearstatcache($path); - $mtime = @filemtime((string)$path); - if ( empty($mtime) ) { - $mtime = @filectime((string)$path); - if ( empty($mtime) ) { - $mtime = time(); - } - } - } - - fputcsv($fp, [$path, $mtime]); - } - } - } - fclose($fp); - } - - /** - * @inheritDoc - */ - public static function getJournal($journal) - { - $journals_path = self::getJournalsPath(); - $path = $journals_path . DIRECTORY_SEPARATOR . $journal . self::STATUS_COMPLETED . '.csv'; - if (function_exists('gzopen')) { - $path = $journals_path . DIRECTORY_SEPARATOR . $journal . self::STATUS_COMPLETED . '.csv.gz'; - } - - if ( ! file_exists($path)) { - return null; - } - - return $path; - } - - /** - * Get snapshots files directory - * - * @return string - */ - private static function getJournalsPath() - { - $dir_name = __DIR__ . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR; - if ( ! is_dir($dir_name)) { - mkdir($dir_name); - file_put_contents($dir_name . 'index.php', 'getDescription(); - } - - public static function renderSelectors(Phrases $phrases) - { - $html = '
'; - $html .= '
'; - $html .= '

' . $phrases->getTitle() . '

'; - $html .= '
'; - - $html .= '
'; - $html .= '

' . self::getFSWatcherDescription($phrases) . '

'; - - $repository = Controller::$repository; - $dates = $repository::getAvailableJournals(); - - $html .= self::manualSnapshotButton($phrases); - - if ( ! static::snapshotsAreReady($dates) ) { - // Snapshots were not ready, do not render selectors - $html .= '

' . $phrases->featureNotReady1() . '

'; - $html .= '

' . $phrases->featureNotReady2() . '

'; - } - - $display_selectors = static::snapshotsAreReady($dates) ? 'block' : 'none'; - $html .= '
'; - $html .= '

' . $phrases->getCompareButtonDescription() . '

'; - $html .= ''; - $html .= ''; - $html .= '
'; - $html .= '
'; - - $html .= ''; - $html .= ''; - $html .= '
'; - $html .= '
'; - - $html .= '
'; - $html .= ''; - $html .= '
'; - $html .= '
'; - $html .= '
'; - - $html .= '
'; - - $html .= ''; - $html .= '
'; - - $html .= '
'; - - $html .= self::renderTableTemplate($phrases); - - return $html; - } - - protected static function renderSelectorOptions($dates) - { - Logger::log($dates); - - $html = ''; - foreach ($dates as $date) { - $formated_date = date('M d Y H:i:s', $date); - $html .= ''; - } - - return $html; - } - - protected static function manualSnapshotButton(Phrases $phrases) - { - $html = '
'; - $html .= ''; - $html .= '
'; - - return $html; - } - - protected static function renderTableTemplate(Phrases $phrases) - { - $html = '
'; - $html .= ''; - $html .= ''; - - $html .= ''; - $html .= ''; - $html .= ''; - $html .= ''; - $html .= ''; - $html .= ''; - $html .= ''; - - $html .= ''; - $html .= ''; - $html .= ''; - $html .= ''; - $html .= ''; - - $html .= '
'; - $html .= $phrases->getTableHeadPath(); - $html .= ''; - $html .= $phrases->getTableHeadEvent(); - $html .= ''; - $html .= $phrases->getTableHeadChangeOn(); - $html .= '
'; - $html .= $phrases->getTableNoLogs(); - $html .= '
'; - $html .= '
'; - - return $html; - } - - protected static function snapshotsAreReady($dates) - { - return is_array($dates) && count($dates) > 1; - } -} diff --git a/lib/CleantalkSP/Common/FSWatcher/View/index.php b/lib/CleantalkSP/Common/FSWatcher/View/index.php deleted file mode 100644 index b3d9bbc7f..000000000 --- a/lib/CleantalkSP/Common/FSWatcher/View/index.php +++ /dev/null @@ -1 +0,0 @@ - 0) { - for (let i = 0; i < events_array.length; i++) { - const row = convertFSWEventToRow(events_array[i], event_type); - 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} event_type contains event type - */ -function convertFSWEventToRow(event, event_type) { - let row = { - 'path': 'unknown', - 'event_type': event_type.toUpperCase(), - 'date': 'unknown' - } - - if (event.length === 2) { - if (typeof event[0] === 'string') { - row.path = event[0]; - if (row.event_type !== 'DELETED') { - row.path += '
View'; - } - } - 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; -} - -/** - * Show file view. - * @param {Node} el. - */ -function FSWViewFile(el) { - let wp_wrap = jQuery('#wpwrap') - let dialog_window = jQuery('#spbc_dialog') - - dialog_window.dialog({ - modal:true, - title: fswatcherTranslations['fs_modal'] + ' ' + el.dataset.path, - position: { my: "center top", at: "center top+100px" , of: window }, - width: +(wp_wrap.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(); - }, - }); - - dialog_window.append('Wait for downloading'); - - let spinner = jQuery('#spbc_file_view_preloader'); - let size_multiplier = (wp_wrap.width() * 0.0004); - - spinner.height(128 * size_multiplier); - spinner.width(128 * size_multiplier); - spinner.css({left: dialog_window.width()/2 - (128 * size_multiplier / 2)}); - spinner.css({top: dialog_window.height()/2 - (128 * size_multiplier / 2)}); - - if (typeof fswatcherToken !== 'undefined') { - const firstSelectorId = jQuery('#fswatcher__first_date').val() - const secondSelectorId = jQuery('#fswatcher__second_date').val() - let data = [ - 'fswatcher_token=' + fswatcherToken, - 'fswatcher_view_file=1', - '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 dialog_window = jQuery('#spbc_dialog'); - dialog_window.empty(); - jQuery('#spbc_file_view_preloader').css({display:'none'}) - let row_template = '
%s

%s


'; - for (let row in content) { - dialog_window.append(row_template.printf(+row + 1, content[row])); - } - - let content_height = Object.keys(content).length * 19 < 76 ? 76 : Object.keys(content).length * 19, - visible_height = (document.documentElement.clientHeight) / 100 * 75, - overflow = content_height < visible_height ? 'hidden' : 'scroll', - height = overflow === 'scroll' ? visible_height : content_height; - - dialog_window.css({ - height: height, - overflow: overflow - }); - }; - FSWrequest(data, callback); - } - - return false; -} - - -/** - * Render the row of FSW table. - * @param {string} path the file path - * @param {string} event_type the event type - * @param {string} date the date of event - */ -function renderFSWTableRow(path, event_type, date) { - - if (event_type === '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 td_path = document.createElement('td'); - td_path.setAttribute('name', 'fswatcher-event-path'); - td_path.setAttribute('data-before', 'Path'); - td_path.innerHTML = path; - tr.appendChild(td_path); - - let td_type = document.createElement('td'); - td_type.setAttribute('name', 'fswatcher-event-type'); - td_type.setAttribute('data-before', 'Event'); - td_type.innerText = event_type; - tr.appendChild(td_type); - - let td_date = document.createElement('td'); - td_date.setAttribute('name', 'fswatcher-event-date'); - td_date.setAttribute('data-before', 'Changed on date'); - td_date.innerText = date; - tr.appendChild(td_date); - - 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'; - } -} - -/** - * Wrapper for xhr - * @param {Array} data - * @param {Function} callback - */ -function FSWrequest(data, callback) { - let xhr = new XMLHttpRequest(); - xhr.open("POST", fswatcherWebsiteUrl + '/'); - xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - xhr.send(data.join('&')); - xhr.onreadystatechange = function() { - if( xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200 ) { - let response = FSWDecodeJSON(xhr.response); - callback(response); - } - }; -} - -// listeners -document.getElementById('fswatcher__compare').addEventListener('click', FSWCompare); -document.getElementById('fswatcher__create_snapshot').addEventListener('click', FSWCreate); -firstFSWSelector.addEventListener('change', filterFSWSecondSelector); -secondFSWSelector.addEventListener('change', filterFSWFirstSelector); - - diff --git a/lib/CleantalkSP/Common/FSWatcher/assets/fswatcher.js b/lib/CleantalkSP/Common/FSWatcher/assets/fswatcher.js deleted file mode 100755 index 802b34332..000000000 --- a/lib/CleantalkSP/Common/FSWatcher/assets/fswatcher.js +++ /dev/null @@ -1,8 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - if (typeof fswatcherToken !== 'undefined' && fswatcherWebsiteUrl !== 'undefined') { - let xhr = new XMLHttpRequest(); - xhr.open("POST", fswatcherWebsiteUrl + '/'); - xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - xhr.send('fswatcher_token=' + fswatcherToken); - } -}); diff --git a/lib/CleantalkSP/Common/FSWatcher/assets/index.php b/lib/CleantalkSP/Common/FSWatcher/assets/index.php deleted file mode 100644 index b3d9bbc7f..000000000 --- a/lib/CleantalkSP/Common/FSWatcher/assets/index.php +++ /dev/null @@ -1 +0,0 @@ -getJournal($journal_id); + $analyzer = new Analyzer(); + $journal_parsed = $analyzer->uncompress($journal_parsed, true); + if (strpos($journal_parsed, $path) !== false) { + return true; + } + return false; + } + + /** + * @param $path + * @return bool + */ + private static function isFileOfFSWJournalAfterCompareJournals($path) + { + $journal_result_compare = self::getCompareResult(); + $journal_result_string = ''; + foreach ($journal_result_compare as $journal_result) { + if (count($journal_result) > 0) { + foreach ($journal_result as $value) { + $journal_result_string .= implode(' , ', $value); + } + } + } + + if (strpos($journal_result_string, $path) !== false) { + return true; + } + + return false; + } + /** * @param $first_journal * @param $second_journal * @return array|false */ - protected static function compare($first_journal, $second_journal) + private static function compare($first_journal, $second_journal) { $result = array( 'added' => array(), @@ -110,9 +191,11 @@ protected static function compare($first_journal, $second_journal) /** * @param $file + * @param bool $do_return_uncompressed_content + * * @return false|string */ - protected static function uncompress($file, $do_return_uncompressed_content = false) + private static function uncompress($file, $do_return_uncompressed_content = false) { if ( substr($file, -3) === '.gz' ) { $content = @gzopen($file, 'r'); diff --git a/lib/CleantalkSP/SpbctWP/FSWatcher/Analyzer/SpbctWpFSWAnalyzer.php b/lib/CleantalkSP/SpbctWP/FSWatcher/Analyzer/SpbctWpFSWAnalyzer.php deleted file mode 100644 index ff3e2f935..000000000 --- a/lib/CleantalkSP/SpbctWP/FSWatcher/Analyzer/SpbctWpFSWAnalyzer.php +++ /dev/null @@ -1,120 +0,0 @@ - $second) { - $tmp = $first; - $first = $second; - $second = $tmp; - } - - $storage = SpbctWpFSWController::$storage; - - $first_journal = $storage::getJournal($first); - $second_journal = $storage::getJournal($second); - - if (!$first_journal || !$second_journal) { - return false; - } - - if (SpbctWpFSWController::$debug) { - Logger::log('first journal ' . $first_journal); - Logger::log('second journal ' . $second_journal); - } - - return parent::compare($first_journal, $second_journal); - } - - /** - * @return string|false - */ - public static function getViewFile() - { - $path = isset($_POST['fswatcher_file_path']) ? $_POST['fswatcher_file_path'] : false; - - $journals_first = isset($_POST['fswatcher__first_date']) ? $_POST['fswatcher__first_date'] : false; - $journals_second = isset($_POST['fswatcher__second_date']) ? $_POST['fswatcher__second_date'] : false; - $journals = array($journals_first, $journals_second); - - if (!$path || !is_file($path)) { - throw new \Exception('File path is incorrect.'); - } - - if (!$journals[0] || !$journals[1]) { - throw new \Exception('Provided journals paths are incorrect.'); - } - - $path_found_in_journal = false; - - foreach ($journals as $journal_id) { - if ( - self::isFileOfFSWJournal($path, $journal_id) && - self::isFileOfFSWJournalAfterCompareJournals($path) - ) { - $path_found_in_journal = true; - break; - } - } - - if (!$path_found_in_journal) { - throw new \Exception('The file is out of FSWatcher journals.'); - } - - return esc_html__(file_get_contents($path)); - } - - /** - * @param $path - * @param $journal_id - * @return bool - */ - private static function isFileOfFSWJournal($path, $journal_id) - { - $storage = new SpbctWpFSWFileStorage(); - $journal_parsed = $storage->getJournal($journal_id); - $analyzer = new Analyzer(); - $journal_parsed = $analyzer->uncompress($journal_parsed, true); - if (strpos($journal_parsed, $path) !== false) { - return true; - } - return false; - } - - /** - * @param $path - * @return bool - */ - private static function isFileOfFSWJournalAfterCompareJournals($path) - { - $journal_result_compare = self::getCompareResult(); - $journal_result_string = ''; - foreach ($journal_result_compare as $journal_result) { - if (count($journal_result) > 0) { - foreach ($journal_result as $value) { - $journal_result_string .= implode(' , ', $value); - } - } - } - - if (strpos($journal_result_string, $path) !== false) { - return true; - } - - return false; - } -} diff --git a/lib/CleantalkSP/SpbctWP/FSWatcher/Controller.php b/lib/CleantalkSP/SpbctWP/FSWatcher/Controller.php new file mode 100644 index 000000000..e2094d7ba --- /dev/null +++ b/lib/CleantalkSP/SpbctWP/FSWatcher/Controller.php @@ -0,0 +1,161 @@ +checkRateLimit(); + } + add_action('wp_ajax_spbct_fswatcher_compare', [$this, 'compare']); + add_action('wp_ajax_spbct_fswatcher_view_file', [$this, 'viewFile']); + add_action('wp_ajax_spbct_fswatcher_create_snapshot', [$this, 'createSnapshot']); + } + + /** + * Initialize the `$debug` property false|true + * + * @return void + */ + private static function getDebugState() + { + if ( defined('SPBC_FSWATCHER_DEBUG') ) { + self::$debug = (bool) SPBC_FSWATCHER_DEBUG; + } + } + + /** + * Scanning file system handler + * + * @param $params + * @return void + */ + protected static function run($params) + { + self::$status = self::STATUS_RUNNING; + Scan::run($params); + self::stop(); + } + + /** + * This is the init method. + * + * Making initialize the `$debug` property + * + * Contains Ajax handler for requests: + * 1) Comparing logs + * 2) Scanning file system + * 3) Automatically making ajax requests for 2) + * + * @param $params + * @return void + */ + public static function work() + { + global $spbc; + self::getDebugState(); + + if (self::$debug) { + Logger::setSaltValue($spbc->data['salt']); + } + + Service::setStorage(self::$params->storage); + } + + /** + * Scanning file system stop trigger + * + * @return void + */ + private static function stop() + { + self::$status = self::STATUS_STOPPED; + Service::setAllJournalsAsCompleted(); + } + + private function checkRateLimit() + { + if ( ! Service::isRateLimitPass() ) { + echo json_encode(array('error' => 'Rate limit exceeded. Protected - Security by CleanTalk.')); + die(); + } + } + + public static function compare() + { + spbc_check_ajax_referer('spbc_secret_nonce', 'security'); + if (self::$debug) { + Logger::log('run compare file system logs'); + } + $compare_result = Analyzer::getCompareResult(); + header('Content-Type: application/json'); + if (false === $compare_result) { + if (self::$debug) { + Logger::log('Can not compare logs'); + } + echo json_encode(array('error' => 'Can not compare logs')); + } else { + echo json_encode($compare_result); + } + die(); + } + + public function viewFile() + { + spbc_check_ajax_referer('spbc_secret_nonce', 'security'); + if (self::$debug) { + Logger::log('run view file method'); + } + + header('Content-Type: application/json'); + try { + $view_file = Analyzer::getViewFile(); + echo json_encode(array("data" => $view_file)); + } catch (\Exception $e) { + if (self::$debug) { + Logger::log('Can not view file'); + } + echo json_encode(array('error' => 'Can not view file. ' . $e->getMessage())); + } + die(); + } + + public function createSnapshot() + { + spbc_check_ajax_referer('spbc_secret_nonce', 'security'); + if (self::$debug) { + Logger::log('run scan file system'); + } + self::run(self::$params); + header('Content-Type: application/json'); + die(json_encode(['OK'])); + } +} diff --git a/lib/CleantalkSP/SpbctWP/FSWatcher/Dto/FSWatcherParams.php b/lib/CleantalkSP/SpbctWP/FSWatcher/Dto/FSWatcherParams.php new file mode 100644 index 000000000..11bba25d7 --- /dev/null +++ b/lib/CleantalkSP/SpbctWP/FSWatcher/Dto/FSWatcherParams.php @@ -0,0 +1,11 @@ +dir_to_watch, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::SELF_FIRST, + \RecursiveIteratorIterator::CATCH_GET_CHILD + ); + $storage = Controller::$storage; + $storage::writeJournal($iterator, self::$params->extensions_to_watch, self::$params->exclude_dirs); + } +} diff --git a/lib/CleantalkSP/SpbctWP/FSWatcher/Scan/SpbctWpFSWScan.php b/lib/CleantalkSP/SpbctWP/FSWatcher/Scan/SpbctWpFSWScan.php deleted file mode 100644 index eaa5666a0..000000000 --- a/lib/CleantalkSP/SpbctWP/FSWatcher/Scan/SpbctWpFSWScan.php +++ /dev/null @@ -1,33 +0,0 @@ - 30, + 'expires_in' => $time + 60, + 'attempts' => 0, + ]); + + if ($rateLimit['expires_in'] <= $time) { + $rateLimit['expires_in'] = $time + 60; + $rateLimit['attempts'] = 0; + } + + if ($rateLimit['expires_in'] > $time) { + $rateLimit['attempts']++; + } + + if ($rateLimit['attempts'] >= $rateLimit['limit']) { + return false; + } + + update_option('spbc_rate_limit_fswatcher', $rateLimit); + + return true; + } +} diff --git a/lib/CleantalkSP/SpbctWP/FSWatcher/SpbctWpFSWController.php b/lib/CleantalkSP/SpbctWP/FSWatcher/SpbctWpFSWController.php deleted file mode 100644 index e45931e88..000000000 --- a/lib/CleantalkSP/SpbctWP/FSWatcher/SpbctWpFSWController.php +++ /dev/null @@ -1,179 +0,0 @@ -data['salt']); - Logger::log('check remote call = ' . (int)SpbctWpFSWService::isRC()); - } - - SpbctWpFSWService::setStorage(isset($params['storage']) ? $params['storage'] : 'file'); - - if (self::status() !== self::STATUS_STOPPED) { - return; - } - - if (!SpbctWpFSWService::isRC()) { - if (Request::get('page') === 'sendinblue' || - Request::get('page') === 'notifierforphone-main-menu' || - in_array('RapidLoad_Buffer::maybe_process_buffer', ob_list_handlers()) || - in_array('GFForms::ensure_hook_js_output', ob_list_handlers()) || - (spbc_is_plugin_active('listingpro-plugin/plugin.php') && Server::inUri('listing')) || - // WP_Estimation_Form - ( - spbc_is_plugin_active('WP_Estimation_Form/estimation-form.php') && - ( - strpos(Request::get('screen_id'), 'lfb') !== false || - strpos(Request::get('page'), 'lfb') !== false - ) - ) || - count(ob_list_handlers()) > 1 || - (defined('CT_SPBCT_RUN_FSW_ONLY_ON_ADMIN') && !is_admin()) - ) { - return; - } - - $min_exec_time = $spbc->settings['scanner__fs_watcher__snapshots_period'] ?: parent::EXECUTION_MIN_INTERVAL; - if (SpbctWpFSWService::isMinIntervalPassed($min_exec_time)) { - if (self::$debug) { - Logger::log('attach js to make remote request'); - } - ob_start(['CleantalkSP\SpbctWP\FSWatcher\SpbctWpFSWService', 'attachJS']); - } - - return; - } - - if (!SpbctWpFSWService::isRateLimitPass()) { - echo json_encode(array('error' => 'Rate limit exceeded. Protected - Security by CleanTalk.')); - die(); - } - - if (SpbctWpFSWService::isCompareRequest()) { - if (self::$debug) { - Logger::log('run compare file system logs'); - } - $compare_result = SpbctWpFSWAnalyzer::getCompareResult(); - if (false === $compare_result) { - Logger::log('Can not compare logs'); - echo json_encode(array('error' => 'Can not compare logs')); - } else { - echo json_encode($compare_result); - } - die(); - } - - if (SpbctWpFSWService::isViewFileRequest()) { - if (self::$debug) { - Logger::log('run view file method'); - } - - try { - $view_file = SpbctWpFSWAnalyzer::getViewFile(); - echo json_encode(array("data" => $view_file)); - } catch (\Exception $e) { - Logger::log('Can not view file'); - echo json_encode(array('error' => 'Can not view file. ' . $e->getMessage())); - } - die(); - } - - if (SpbctWpFSWService::isCreateSnapshotRequest()) { - if (self::$debug) { - Logger::log('run scan file system'); - } - self::run($params); - die(json_encode('OK')); - } - } - - /** - * Scanning file system stop trigger - * - * @return void - */ - private static function stop() - { - self::$status = self::STATUS_STOPPED; - SpbctWpFSWService::setAllJournalsAsCompleted(); - } - - /** - * Checking status of the scanning process - * - * @return string - */ - private static function status() - { - $is_exist = SpbctWpFSWService::getProcessingJournal(); - if (!is_null($is_exist)) { - self::$status = self::STATUS_RUNNING; - } - - return self::$status; - } -} diff --git a/lib/CleantalkSP/SpbctWP/FSWatcher/SpbctWpFSWService.php b/lib/CleantalkSP/SpbctWP/FSWatcher/SpbctWpFSWService.php deleted file mode 100644 index 7c329ee51..000000000 --- a/lib/CleantalkSP/SpbctWP/FSWatcher/SpbctWpFSWService.php +++ /dev/null @@ -1,112 +0,0 @@ - $interval; - } - - /** - * Get snapshots which is in process - * - * @return string|null - */ - public static function getProcessingJournal() - { - $storage = SpbctWpFSWController::$storage; - return $storage::getProcessingJournal(); - } - - // - - /** - * Set snapshots to completed status - * - * @return void - */ - public static function setAllJournalsAsCompleted() - { - $storage = SpbctWpFSWController::$storage; - $storage::setAllJournalsAsCompleted(); - } - - public static function attachJS($buffer, $file_to_get_md5 = null) - { - return parent::attachJS($buffer, __FILE__); - } - - public static function generateFsWatcherToken($salt = '') - { - return wp_create_nonce('spbc_secret_fs_watcher_token'); - } - - public static function validateFsWatcherToken() - { - return isset($_POST['fswatcher_token']) && spbc_check_ajax_referer('spbc_secret_fs_watcher_token', 'fswatcher_token'); - } - - public static function isRateLimitPass() - { - $time = time(); - - $rateLimit = get_option('spbc_rate_limit_fswatcher', [ - 'limit' => 30, - 'expires_in' => $time + 60, - 'attempts' => 0, - ]); - - if ($rateLimit['expires_in'] <= $time) { - $rateLimit['expires_in'] = $time + 60; - $rateLimit['attempts'] = 0; - } - - if ($rateLimit['expires_in'] > $time) { - $rateLimit['attempts']++; - } - - if ($rateLimit['attempts'] >= $rateLimit['limit']) { - return false; - } - - update_option('spbc_rate_limit_fswatcher', $rateLimit); - - return true; - } -} diff --git a/lib/CleantalkSP/SpbctWP/FSWatcher/Storage/SpbctWpFSWFileStorage.php b/lib/CleantalkSP/SpbctWP/FSWatcher/Storage/FileStorage.php similarity index 86% rename from lib/CleantalkSP/SpbctWP/FSWatcher/Storage/SpbctWpFSWFileStorage.php rename to lib/CleantalkSP/SpbctWP/FSWatcher/Storage/FileStorage.php index 8623836ad..98013a2cf 100644 --- a/lib/CleantalkSP/SpbctWP/FSWatcher/Storage/SpbctWpFSWFileStorage.php +++ b/lib/CleantalkSP/SpbctWP/FSWatcher/Storage/FileStorage.php @@ -2,8 +2,12 @@ namespace CleantalkSP\SpbctWP\FSWatcher\Storage; -class SpbctWpFSWFileStorage extends \CleantalkSP\Common\FSWatcher\Storage\FileStorage +class FileStorage implements Storage { + const STATUS_PROCESSING = '__processing'; + + const STATUS_COMPLETED = '__completed'; + public static function getJournalsPath() { $wp_upload_dir = wp_get_upload_dir(); @@ -41,25 +45,6 @@ public static function getProcessingJournal() return null; } - /** - * @inheritDoc - */ - public static function getLastJournalTime() - { - $pattern = self::getJournalsPath() . '*' . self::STATUS_COMPLETED . '.csv'; - if (function_exists('gzopen')) { - $pattern = self::getJournalsPath() . '*' . self::STATUS_COMPLETED . '.csv.gz'; - } - - $last_journal = glob($pattern); - if ( ! empty($last_journal)) { - $journal = $last_journal[count($last_journal) - 1]; - return (int)explode('__', basename($journal))[0]; - } - - return null; - } - /** * @inheritDoc */ @@ -69,7 +54,7 @@ public static function setAllJournalsAsCompleted() foreach ($journals as $journal) { $new_name = str_replace(self::STATUS_PROCESSING, self::STATUS_COMPLETED, $journal); rename($journal, $new_name); - parent::compressJournalGZ($new_name); + self::compressJournalGZ($new_name); } } @@ -162,11 +147,6 @@ public static function getJournal($journal) return $path; } - public static function getAssetsPath() - { - return __DIR__ . '/../assets/fswatcher-logic.js'; - } - private static function removeOldJournals() { global $spbc; @@ -190,4 +170,23 @@ private static function removeOldJournals() } } } + + /** + * Archive the snapshot file + * + * @param $journal string + * @return void + */ + private static function compressJournalGZ($journal) + { + if ( ! function_exists('gzopen')) { + return; + } + + $gz = gzopen($journal . '.gz', 'w9'); + gzwrite($gz, file_get_contents($journal)); + gzclose($gz); + + unlink($journal); + } } diff --git a/lib/CleantalkSP/Common/FSWatcher/Storage/Storage.php b/lib/CleantalkSP/SpbctWP/FSWatcher/Storage/Storage.php old mode 100755 new mode 100644 similarity index 79% rename from lib/CleantalkSP/Common/FSWatcher/Storage/Storage.php rename to lib/CleantalkSP/SpbctWP/FSWatcher/Storage/Storage.php index fdf7b8ffc..7b7964030 --- a/lib/CleantalkSP/Common/FSWatcher/Storage/Storage.php +++ b/lib/CleantalkSP/SpbctWP/FSWatcher/Storage/Storage.php @@ -1,10 +1,7 @@ '; $html .= '
'; @@ -19,8 +18,8 @@ public static function renderSelectors(\CleantalkSP\Common\FSWatcher\View\Phrase $html .= '

' . self::getFSWatcherDescription($phrases) . '

'; $html .= '

' . self::getExtendedTabDescription($phrases) . '

'; - $repository = SpbctWpFSWController::$storage; - $dates = $repository::getAvailableJournals(); + $storage = Controller::$storage; + $dates = $storage::getAvailableJournals(); $html .= self::manualSnapshotButton($phrases); @@ -30,7 +29,7 @@ public static function renderSelectors(\CleantalkSP\Common\FSWatcher\View\Phrase $html .= '

' . $phrases->featureNotReady2() . '

'; } - $display_selectors = static::snapshotsAreReady($dates) ? 'block' : 'none'; + $display_selectors = self::snapshotsAreReady($dates) ? 'block' : 'none'; $html .= '
'; $html .= '

' . $phrases->getCompareButtonDescription() . '

'; $html .= '
'; @@ -38,14 +37,14 @@ public static function renderSelectors(\CleantalkSP\Common\FSWatcher\View\Phrase $html .= '
'; $html .= ''; $html .= ''; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; $html .= '
'; @@ -54,7 +53,7 @@ public static function renderSelectors(\CleantalkSP\Common\FSWatcher\View\Phrase $html .= '
'; $html .= '
'; - $html .= ''; + $html .= ''; $html .= 'getTranslations()) . ';'; $html .= ''; $html .= '
'; $html .= '
'; - $html .= parent::renderTableTemplate($phrases); + $html .= self::renderTableTemplate($phrases); $html .= '
'; @@ -96,13 +92,67 @@ public static function getExtendedTabDescription(Phrases $phrases) return $phrases->getExtendedTabDescription(); } + protected static function renderSelectorOptions($dates) + { + if (Controller::$debug) { + Logger::log($dates); + } + + $html = ''; + foreach ($dates as $date) { + $formated_date = date('M d Y H:i:s', $date); + $html .= ''; + } + + return $html; + } + protected static function manualSnapshotButton(Phrases $phrases) { $html = '
'; - $html .= ''; + $html .= ''; $html .= 'getTableHeadPath(); + $html .= ''; + $html .= ''; + $html .= $phrases->getTableHeadEvent(); + $html .= ''; + $html .= ''; + $html .= $phrases->getTableHeadChangeOn(); + $html .= ''; + $html .= ''; + $html .= ''; + + $html .= ''; + $html .= ''; + $html .= ''; + $html .= $phrases->getTableNoLogs(); + $html .= ''; + $html .= ''; + $html .= ''; + + $html .= ''; + $html .= '
'; + + return $html; + } + + private static function snapshotsAreReady($dates) + { + return is_array($dates) && count($dates) > 1; + } } diff --git a/security-malware-firewall.php b/security-malware-firewall.php index aae373996..a7f37df35 100644 --- a/security-malware-firewall.php +++ b/security-malware-firewall.php @@ -15,7 +15,7 @@ use CleantalkSP\SpbctWP\AdjustToEnvironmentModule\AdjustToEnvironmentHandler; use CleantalkSP\SpbctWP\DTO\SecurityLogsDataRowDTO; use CleantalkSP\SpbctWP\DTO\SecurityLogsDTO; -use CleantalkSP\SpbctWP\FSWatcher\SpbctWpFSWController as FSWatcherController; +use CleantalkSP\SpbctWP\FSWatcher\Controller as FSWatcherController; use CleantalkSP\SpbctWP\DB; use CleantalkSP\SpbctWP\Firewall\BFP; use CleantalkSP\SpbctWP\Firewall\FW; @@ -414,12 +414,12 @@ function spbc_change_author_name($link, $_author_id, $_author_nicename) add_action('init', function () use ($spbc) { if ( $spbc->feature_restrictions->getState($spbc, 'fswatcher')->is_active && $spbc->settings['scanner__fs_watcher'] ) { - $fswatch_params = array( - 'dir_to_watch' => ABSPATH, - 'exclude_dirs' => array(), - 'extensions_to_watch' => array('php'), - ); - FSWatcherController::work($fswatch_params); + $fswatcher_params = new \CleantalkSP\SpbctWP\FSWatcher\Dto\FSWatcherParams(); + $fswatcher_params->dir_to_watch = ABSPATH; + $fswatcher_params->exclude_dirs = []; + $fswatcher_params->extensions_to_watch = ['php']; + $fswatcher = new FSWatcherController($fswatcher_params); + $fswatcher::work(); } });