diff --git a/core/js/files/module.js b/core/js/files/module.js index 53568b73f..a6a025a48 100644 --- a/core/js/files/module.js +++ b/core/js/files/module.js @@ -13,7 +13,7 @@ fpcm.filemanager = { tabsObj: {}, init: function() { - + fpcm.ui_tabs.render('#files', { reload: true, onRenderHtmlAfter: function (_event, _result) { @@ -34,16 +34,16 @@ fpcm.filemanager = { fpcm.filemanager.initDeleteMultipleButton(); }, - + initAfter: function() { if (fpcm.vars.jsvars.fmgrMode === 1) { - + fpcm.ui.selectmenu('select[data-user_setting]', { change: function (_ev, _ui) { document.getElementById('pageSelect').selectedIndex = 0; - + fpcm.ajax.post('setconfig', { data: { var: _ui.dataset.user_setting, @@ -52,14 +52,14 @@ fpcm.filemanager = { execDone: fpcm.filemanager.reloadFiles }); } - }); - + }); + } fpcm.dom.fromId('btnFmgrUploadBack').click(function () { fpcm.ui_tabs.show('#files', 0); }); - + }, initListActions: function () { @@ -72,21 +72,22 @@ fpcm.filemanager = { fpcm.filemanager.initDeleteButtons(); fpcm.filemanager.initAltTextButtons(); fpcm.filemanager.initPropertiesButton(); + fpcm.filemanager.initCopyButton(); }, - + initInsertButtons: function () { if (fpcm.vars.jsvars.fmgrMode === 2) { - fpcm.dom.bindClick('.fpcm-filelist-tinymce-thumb', function (_e, _ui) { + fpcm.dom.bindClick('.fpcm-filelist-tinymce-thumb', function (_e, _ui) { parent.fpcm.editor.insertThumbByEditor(_ui.href, _ui.dataset.imgtext); fpcm.ui_dialogs.close('editor-html-filemanager', true); }); - + fpcm.dom.bindClick('.fpcm-filelist-tinymce-full', function (_e, _ui) { parent.fpcm.editor.insertFullByEditor(_ui.href, _ui.dataset.imgtext); fpcm.ui_dialogs.close('editor-html-filemanager', true); - }); + }); fpcm.dom.bindClick('#btnInsertGallery', function () { @@ -102,12 +103,12 @@ fpcm.filemanager = { parent.fpcm.editor.insertGalleryByEditor(values); return false; }); - + return false; } - + if (fpcm.vars.jsvars.fmgrMode === 3) { - + fpcm.dom.bindClick('.fpcm-filelist-articleimage', function (_e, _ui) { parent.document.getElementById('articleimagepath').value = _ui.href; fpcm.ui_dialogs.close('editor-html-filemanager', true); @@ -115,14 +116,14 @@ fpcm.filemanager = { return false; } - + if (fpcm.vars.jsvars.fmgrMode === 4) { - + fpcm.dom.bindClick('.fpcm-filelist-tinymce-thumb', function (_e, _ui) { parent.document.getElementById('mediaposter').value = _ui.href; fpcm.ui_dialogs.close('editor-html-filemanager', true); }); - + fpcm.dom.bindClick('.fpcm-filelist-tinymce-full', function (_e, _ui) { parent.document.getElementById('mediaposter').value = _ui.href; fpcm.ui_dialogs.close('editor-html-filemanager', true); @@ -139,7 +140,7 @@ fpcm.filemanager = { console.log('FILE_LIST_RENAME_NEWNAME'); return false; } - + var _docname = this.dataset.file; var _input = new fpcm.ui.forms.input(); @@ -174,7 +175,7 @@ fpcm.filemanager = { return; } - + fpcm.ajax.post('files/rename', { data: { newName: _inputEl.value, @@ -192,11 +193,11 @@ fpcm.filemanager = { document.getElementById('fpcm-id-new-filename-dialog').focus(); } }); - + return false; }); }, - + initEditButtons: function() { fpcm.dom.bindClick('.fpcm-filelist-link-edit', function (_e, _ui) { fpcm.imageEditor.initEditorDialog({ @@ -210,7 +211,7 @@ fpcm.filemanager = { initAltTextButtons: function() { fpcm.dom.bindClick('.fpcm-filelist-link-alttext', function (_e, _ui) { - + var _input = new fpcm.ui.forms.input(); _input.name = 'alt-text-dialog'; _input.label = fpcm.ui.translate('FILE_LIST_ALTTEXT'); @@ -245,7 +246,7 @@ fpcm.filemanager = { dlOnOpenAfter: function () { document.getElementById('fpcm-id-alt-text-dialog').focus(); } - }); + }); @@ -277,24 +278,24 @@ fpcm.filemanager = { } }); - return false; + return false; } }); - return false; + return false; }); - + }, initNewThumbButton: function() { fpcm.dom.bindClick('#btnCreateThumbs', function (event, ui) { - + var items = fpcm.dom.getCheckboxCheckedValues('.fpcm-ui-list-checkbox'); if (!items || !items.length) { return false; } - + ui.disabled = true; fpcm.ajax.post('files/createthumbs', { @@ -322,7 +323,7 @@ fpcm.filemanager = { initDeleteMultipleButton: function() { fpcm.dom.bindClick('#btnDeleteFiles', function () { - + var items = fpcm.dom.getCheckboxCheckedValues('.fpcm-ui-list-checkbox'); if (!items || !items.length) { return false; @@ -348,17 +349,25 @@ fpcm.filemanager = { } }); - return false; + return false; } - }); + }); return false; }); }, + initCopyButton: function() { + + fpcm.dom.bindClick('a.dropdown-item[data-fn="system.createCopy"]', function (_e) { + fpcm.system.createCopy(_e); + }); + + }, + initPropertiesButton: function() { - + let _form = [ { prop: 'filetime', @@ -406,7 +415,7 @@ fpcm.filemanager = { ]; fpcm.dom.bindClick('.fpcm-filelist-properties', function (_e, _ui) { - + let _titleTxt = ''; let _titleHtml = ''; @@ -445,8 +454,8 @@ fpcm.filemanager = { _row.appendChild(_colValue); _dlgContent.appendChild(_row); - } - + } + fpcm.ui_dialogs.create({ id: 'files-properties', title: fpcm.ui.translate('GLOBAL_PROPERTIES'), @@ -458,14 +467,14 @@ fpcm.filemanager = { }); }, - + initPagination: function() { fpcm.ui.initPager({ backAction: function() { fpcm.filemanager.reloadFiles(fpcm.vars.jsvars.pager.showBackButton); }, - + nextAction: function() { fpcm.filemanager.reloadFiles(fpcm.vars.jsvars.pager.showNextButton); }, @@ -483,7 +492,7 @@ fpcm.filemanager = { } fpcm.dom.assignHtml('#fpcm-tab-files-list-pane', fpcm.vars.jsvars.loaderTpl.replace(/\{\$thumbsize\}/g, fpcm.vars.jsvars.thumbsize)); - + if (_filter) { fpcm.vars.jsvars.filesLastSearch = (new Date()).getTime(); } @@ -500,39 +509,39 @@ fpcm.filemanager = { if (_result.data && _result.data.pager) { fpcm.vars.jsvars.pager = _result.data.pager; - } + } fpcm.filemanager.initListActions(); fpcm.dom.fromClass('fpcm-select-all').prop('checked', false); } }); - + return false; }, - + initFilesSearch: function() { fpcm.dom.bindClick('#btnOpenSearch', function () { let _formData = fpcm.vars.jsvars.searchForm; - + let _form = document.createElement('div'); - + for (var _fieldName in _formData.fields) { - + let _field = _formData.fields[_fieldName]; - + let _tmp = new fpcm.ui.forms[_field.call]; _tmp.name = _fieldName; _field.class += ' ' + _tmp.class; _tmp.wrapper = 'form-floating'; - + if (!_tmp.assignFormObject) { continue; } - _tmp.assignFormObject(_field); - + _tmp.assignFormObject(_field); + let _row = document.createElement('div'); _row.className = 'row mb-3'; @@ -573,22 +582,22 @@ fpcm.filemanager = { icon: "check", primary: true, clickClose: true, - click: function(_ui, _bsObj) { - - var sParams = fpcm.dom.getValuesByClass('fpcm-files-search-input'); + click: function(_ui, _bsObj) { + + var sParams = fpcm.dom.getValuesByClass('fpcm-files-search-input'); sParams.combinations = fpcm.dom.getValuesByClass('fpcm-ui-input-select-filessearch-combination'); fpcm.filemanager.startFilesSearch(sParams); } - }, + }, { text: fpcm.ui.translate('GLOBAL_RESET'), - icon: "filter-circle-xmark" , + icon: "filter-circle-xmark" , clickClose: true, click: function() { fpcm.ui.relocate('self'); } - } + } ], dlOnOpenAfter: function () { document.getElementById('fpcm-id-filename').focus(); @@ -612,7 +621,7 @@ fpcm.filemanager = { fpcm.filemanager.reloadFiles(1, sParams); }, - + runFileIndexUpdate: function () { fpcm.ajax.get('crons/exec', { @@ -625,14 +634,14 @@ fpcm.filemanager = { } }); }, - + getCurrentPage: function () { - + let _el = document.getElementById('pageSelect'); if (!_el || _el.options.length < 2 || _el.value === undefined) { return 1; } - - return _el.value; + + return _el.value; } }; diff --git a/core/js/system.js b/core/js/system.js index e514b6c7e..47cfb596b 100644 --- a/core/js/system.js +++ b/core/js/system.js @@ -1,7 +1,7 @@ /** * FanPress CM system javascript functions * @article Stefan Seehafer - * @copyright (c) 2015-2018, Stefan Seehafer + * @copyright (c) 2015-2024, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ @@ -27,9 +27,9 @@ fpcm.system = { fpcm.dom.bindClick('#fpcm-clear-cache', function () { return fpcm.system.clearCache(); }); - + fpcm.dom.bindClick('#btnMinifyMenu', function (_el) { - + let _descr = [ { text: 'GLOBAL_HIDE', @@ -43,7 +43,7 @@ fpcm.system = { fpcm.dom.fromTag('a.fpcm.ui-nav-link.nav-link > span.fpcm.nav-text').toggleClass('d-lg-none'); fpcm.dom.fromTag('a.fpcm.ui-nav-link.nav-link').toggleClass('text-center'); - + _el.currentTarget.dataset.navhidden = parseInt(_el.currentTarget.dataset.navhidden) ? 0 : 1; let _current = _descr[_el.currentTarget.dataset.navhidden]; @@ -63,7 +63,7 @@ fpcm.system = { password: 'text', text: 'password', } - + _el.type = _map[_el.type] ? _map[_el.type] : 'password'; }, @@ -79,7 +79,7 @@ fpcm.system = { txt: fpcm.ui.translate('SAVE_FAILED_PASSWORD_MATCH') }, true); } - + fpcm.ui.showCurrentPasswordConfirmation(); fpcm.ajax.post('passcheck', { @@ -104,7 +104,7 @@ fpcm.system = { return false; }); - + fpcm.dom.bindClick('#genPasswd', function () { fpcm.system.generatePasswdString(); fpcm.ui.showCurrentPasswordConfirmation(); @@ -167,12 +167,12 @@ fpcm.system = { articleId: fpcm.vars.jsvars.articleId }, execDone: function (result) { - + fpcm.worker.postMessage({ cmd: 'remove', id: 'system.refresh' - }); - + }); + if (fpcm.vars.jsvars.articleId > 0 && fpcm.editor && fpcm.editor.showInEditDialog) { fpcm.editor.showInEditDialog(result); @@ -212,8 +212,8 @@ fpcm.system = { for (var _i in _params.fields) { _content += _params.fields[_i]; } - - + + _content = '
' + _content + '
'; } @@ -229,7 +229,7 @@ fpcm.system = { }, true); return false; } - + _ajaxParams.ids = fpcm.ajax.toJSON(_ajaxParams.ids); fpcm.ui_dialogs.create({ @@ -250,7 +250,7 @@ fpcm.system = { var catEl = fpcm.dom.fromId('categories'); if (catEl.length) { list.massEditCategories = catEl.val(); - } + } if (_params.multipleSelect) { _ajaxParams.fields[_params.multipleSelectField] = fpcm.dom.fromId(_params.multipleSelect).val(); @@ -293,10 +293,10 @@ fpcm.system = { }, execMassEdit: function (func, params) { - + if (!params.onSuccess) { params.onSuccess = function () { - + if (fpcm.vars.jsvars.massEdit === undefined || !fpcm.vars.jsvars.massEdit.relocateParams === undefined) { setTimeout(function () { window.location.reload(); @@ -307,7 +307,7 @@ fpcm.system = { fpcm.ui.relocate(window.location.href + fpcm.vars.jsvars.massEdit.relocateParams); }; } - + fpcm.ajax.post(func, { data: params, dataType: 'json', @@ -350,7 +350,7 @@ fpcm.system = { fpcm.ui_dialogs.confirm({ clickYes: function () { - + fpcm.ajax.execFunction('clearTrash', _params.fn, { pageToken: 'ajax/clearTrash', data: { @@ -387,7 +387,7 @@ fpcm.system = { fpcm.dom.bindClick('.fpcm.ui-help-dialog', function (_event, _ui) { fpcm.ajax.get('help', { - + quiet: true, data: { ref: _ui.dataset.ref, @@ -408,14 +408,14 @@ fpcm.system = { }); } }); - + return false; }); }, - + addAjaxNotifications: function(_nstring, _count) { - + let _idStr = '#fpcm-id-notifications'; if (!fpcm.dom.fromId(_idStr).length) { return false; @@ -423,44 +423,44 @@ fpcm.system = { fpcm.dom.assignHtml(_idStr, _nstring); let _el = fpcm.dom.fromId('notificationsCount').html(_count); - + if (_count) { _el.removeClass('d-none'); return true; } - + _el.addClass('d-none'); }, checkForUpdates: function () { - + fpcm.dom.bindClick('#checkUpdate', function () { fpcm.ajax.get('crons/exec', { - data: { + data: { cjId: 'updateCheck' } }); - + }); return true; }, - + mergeToVars: function (_newvalue) { if (!_newvalue) { _newvalue = []; } - + return jQuery.extend(fpcm, _newvalue); }, - + parseUrlQuery: function (_url) { - + if (!_url) { return {}; } - + var urlItems = _url.replace(/.*\?/, '').split('&'); var returnValues = {}; for (var i = 0; i < urlItems.length; i++) { @@ -474,6 +474,47 @@ fpcm.system = { } return returnValues; + }, + + createCopy: function (_e) { + + let _params = _e.delegateTarget.dataset.fnArg.split(':'); + + fpcm.ui_dialogs.confirm({ + clickYes: function () { + fpcm.ajax.execFunction('copy', _params[0], { + data: { + id: _params[1] + }, + execDone: function(_result) { + + if (!_result.result) { + fpcm.ui.addMessage(_result.message); + return true; + } + + if (_result.destination) { + fpcm.ui.relocate(_result.destination); + return true; + } + + if (_result.callback) { + + let _fn = _result.callback.split('.'); + if (! typeof fpcm[_fn[0]][_fn[1]] == 'function') { + return false; + } + + return fpcm[_fn[0]][_fn[1]](); + + } + + return true; + } + }); + } + }); + } }; \ No newline at end of file diff --git a/core/js/ui/base.js b/core/js/ui/base.js index b192e99f5..94bea0916 100644 --- a/core/js/ui/base.js +++ b/core/js/ui/base.js @@ -24,20 +24,37 @@ fpcm.ui = { if (! typeof fpcm[_fn[0]][_fn[1]] == 'function') { return false; } - + let _args = null; if (_callee.dataset.fnArg) { _args = _callee.dataset.fnArg; } - + fpcm[_fn[0]][_fn[1]](_event, _callee, _args); return false; - }) + }); + + fpcm.dom.bindClick('.fpcm.ui-button[data-ui-confirm]', function(_event, _callee) { + + if (!_callee.dataset.uiConfirm) { + return true; + } + + fpcm.ui_dialogs.confirm({ + clickYes: function () { + _callee.dataset.uiConfirm = ''; + _callee.click(); + } + }); + + return false; + + }, false, true); fpcm.ui.showMessages(); fpcm.ui.initJqUiWidgets(); - fpcm.ui.initDateTimeMasks(); + fpcm.ui.initDateTimeMasks(); fpcm.dom.bindEvent('#fpcm-ui-form', 'submit', function () { fpcm.ui_loader.show(); @@ -46,7 +63,7 @@ fpcm.ui = { fpcm.ui.initShorthelpTooltips(); fpcm.ui.initLightbox(); - + fpcm.ui.tabs('.fpcm-ui-tabs-general'); }, @@ -56,12 +73,12 @@ fpcm.ui = { if (!_domEl.length) { return true; } - + for (var i = 0; i < _domEl.length; i++) { new bootstrap.Tooltip(_domEl[i], { placement: 'auto' }); - } + } }, @@ -74,18 +91,18 @@ fpcm.ui = { fpcm.ui_loader.hide(); return false; } - + if (!fpcm.dom.fromTag(this).data('hidespinner')) { fpcm.ui_loader.show(); } - + return true; }, false, true); fpcm.ui.initPager(); }, - + initLightbox: function() { if (jQuery.fancybox == undefined) { @@ -102,9 +119,9 @@ fpcm.ui = { }); }, - + showMessages: function() { - + if (window.fpcm.vars.ui.messages === undefined || !fpcm.vars.ui.messages.length) { return false; } @@ -121,7 +138,7 @@ fpcm.ui = { fpcm.ui.appendMessageToBody(_boxes); }, - + addMessage: function(value, _clear) { if (_clear === undefined) { @@ -132,9 +149,9 @@ fpcm.ui = { fpcm.vars.ui.messages = []; fpcm.dom.fromClass('fpcm.ui-message').remove(); } - + if (!value.icon) { - switch (value.type) { + switch (value.type) { case 'error' : value.icon = 'exclamation-triangle'; break; @@ -146,11 +163,11 @@ fpcm.ui = { break; } } - + if (!value.id) { value.id = fpcm.ui.getUniqueID(); } - + if (fpcm.ui.langvarExists(value.txt)) { value.txt = fpcm.ui.translate(value.txt); } @@ -160,11 +177,11 @@ fpcm.ui = { fpcm.ui.appendMessageToBody(fpcm.ui.createMessageBox(value)); }, - + createMessageBox: function(_msg) { var _css = ' toast'; - + _mbxId = 'msgbox-' + _msg.id; if (fpcm.dom.fromId(_mbxId).length > 0) { return true; @@ -177,7 +194,7 @@ fpcm.ui = { return true; } - + _msg.cbtn = ''; _msg.bstm = 'light'; @@ -207,7 +224,7 @@ fpcm.ui = { return _msgCode; }, - + appendMessageToBody: function(_boxes) { fpcm.dom.appendHtml('#fpcm-body', '
' + _boxes + '
'); @@ -215,18 +232,18 @@ fpcm.ui = { let _el = document.getElementsByClassName('fpcm ui-message'); for (var i = 0; i < _el.length; i++) { var toast = new bootstrap.Toast(_el[i]); - toast.show(); + toast.show(); } }, - + translate: function(langVar) { return fpcm.vars.ui.lang[langVar] === undefined ? langVar : fpcm.vars.ui.lang[langVar]; }, - + langvarExists: function(langVar) { return fpcm.vars.ui.lang[langVar] === undefined ? false : true; }, - + assignCheckboxes: function() { if (!fpcm.dom.fromClass('fpcm-select-all').length) { @@ -259,7 +276,7 @@ fpcm.ui = { if (_params.change) { _el.bind('change', function (_ev) { - + _ev.preventDefault(); try { _params.change(_ev, this); @@ -278,7 +295,7 @@ fpcm.ui = { } }, - + progressbar: function(_cid, _params){ if (_params === undefined) { @@ -311,7 +328,7 @@ fpcm.ui = { _el.setAttribute('ariaValuemin', _params.min); _el.innerText = _params.label; _el.style.width = (_params.value * 100 / _params.max) + '%'; - + if (_el.label && !_el.classList.contains('p-2')) { _el.classList.add('p-2'); } @@ -320,7 +337,7 @@ fpcm.ui = { _el.classList.add('progress-bar-animated'); } }, - + autocomplete: function(_elemClassId, _params) { if (fpcm.ui._autocompletes[_elemClassId]) { @@ -332,7 +349,7 @@ fpcm.ui = { if (_params.source === undefined) { _params.source = []; } - + _opt.data = _params.source === undefined || !_params.source instanceof Array ? [] : _params.source; _opt.highlightTyped = false; @@ -345,15 +362,15 @@ fpcm.ui = { _opt.onSelectItem = function (_el) { _acDdEl.value = _el.value; }; - + if (_params.minLength !== undefined) { _opt.treshold = _params.minLength; } - + if (_params.onRenderItems !== undefined) { _opt.onRenderItems = _params.onRenderItems; } - + if (_params.showValue !== undefined) { _opt.showValue = _params.showValue; } @@ -369,28 +386,28 @@ fpcm.ui = { fpcm.ui._autocompletes[_elemClassId].setData([]); return false; } - + fpcm.ui._autocompletes[_elemClassId].setData([]); fpcm.ajax.get(_params.source + '&term=' + _val, { quiet: true, execDone: function (_result) { - + if (!_result instanceof Array) { _result = []; } - + fpcm.ui._autocompletes[_elemClassId].setData(_result); } }); - + return false; }; fpcm.ui._autocompletes[_elemClassId] = new Autocomplete(_acDdEl, _opt); fpcm.ui._autocompletes[_elemClassId].setData([]); }, - + multiselect: function(_id, _params) { if (TomSelect === undefined) { @@ -401,19 +418,19 @@ fpcm.ui = { if (!_params) { _params = {}; } - + if (!_params.searchField) { _params.searchField = ['text', 'value']; } - + if (!_params.plugins) { _params.plugins = []; } - + if (!_params.placeholder) { _params.placeholder = 'EDITOR_CATEGORIES_SEARCH'; } - + if (!_params.hidePlaceholder) { _params.hidePlaceholder = true; } @@ -430,20 +447,20 @@ fpcm.ui = { }, createIFrame: function(params) { - + if (!params.src) { console.warn('fpcm.ui.createIFrame requires a non-empty value.'); return ''; } - + if (!params.classes) { params.classes = 'w-100'; } - + if (!params.id) { params.id = fpcm.ui.getUniqueID(); } - + if (!params.options) { params.options = []; } @@ -460,7 +477,7 @@ fpcm.ui = { console.warn('Invalid icon class given!'); return ''; } - + if (!_params) { _params = {}; } @@ -506,8 +523,8 @@ fpcm.ui = { .replace('{{size}}', _params.size) .replace('{{text}}', _params.text) .replace('{{prefix}}', ( _params.prefix ? _params.prefix : fpcm.vars.ui.components.icon.defaultPrefix )); - - + + }, getTextInput: function(_params) { @@ -559,27 +576,27 @@ fpcm.ui = { .replace('{{placeholder}}', _params.placeholder) .replace('maxlength=\"255\" ', _params.maxlenght ? 'maxlength=\"' + _params.maxlenght + '\" ' : ''); }, - + initDateTimeMasks: function() { - + if (!fpcm.vars.jsvars.dtMasks) { return false; } - + if (fpcm.dom.fromId('system_dtmask').length) { fpcm.ui.autocomplete('#system_dtmask', { source: fpcm.vars.jsvars.dtMasks, minLength: 1 }); } - + if (fpcm.dom.fromId('datasystem_dtmask').length) { fpcm.ui.autocomplete('#datasystem_dtmask', { source: fpcm.vars.jsvars.dtMasks, minLength: 1 }); } - + if (fpcm.dom.fromId('usermetasystem_dtmask').length) { fpcm.ui.autocomplete('#usermetasystem_dtmask', { source: fpcm.vars.jsvars.dtMasks, @@ -587,7 +604,7 @@ fpcm.ui = { }); } }, - + relocate: function (url) { if (url === 'self') { @@ -600,7 +617,7 @@ fpcm.ui = { openWindow: function (url) { return window.open(url); }, - + showCurrentPasswordConfirmation: function () { var el = fpcm.dom.fromId('fpcm-ui-currentpass-box'); if (!el.length) { @@ -616,26 +633,26 @@ fpcm.ui = { if (!(element instanceof Object)) { element = fpcm.dom.fromTag(element); } - + if (element.hasClass('fpcm ui-hidden')) { return true; } return element.hasClass('fpcm-ui-hidden') ? true : false; }, - + getUniqueID: function (_descr) { return (new Date()).getMilliseconds() + Math.random().toString(36).substr(2, 9) + (_descr ? _descr : ''); }, - + replaceIcon: function (_id, _haystack, _needle) { fpcm.dom.fromId(_id).find('span.fpcm-ui-icon').removeClass('fa-' + _haystack).addClass('fa-' + _needle); }, - + darkModeEnabled: function () { return document.documentElement.dataset.bsTheme == 'dark'; }, - + prepareId: function(_id, _skipHash) { if (_id.startsWith('#' + fpcm.vars.ui.idPrefix)) { diff --git a/core/views/filemanager/buttons.php b/core/views/filemanager/buttons.php index 6d005c185..759aba5db 100644 --- a/core/views/filemanager/buttons.php +++ b/core/views/filemanager/buttons.php @@ -12,7 +12,7 @@ - + @@ -39,9 +39,12 @@ dropdownItem(uniqid('edit'))->setText('FILE_LIST_EDIT')->setIcon('magic')->setClass('fpcm-filelist-link-edit')->setData(['url' => $file->getImageUrl(), 'filename' => $file->getFilename(), 'mime' => $file->getMimetype()]); ?>
  • - dropdownItem(uniqid('alttext'))->setText('FILE_LIST_ALTTEXT')->setIcon('keyboard')->setClass('fpcm-filelist-link-alttext')->setData(['file' => base64_encode($file->getFilename()), 'alttext' => $file->getAltText()]); ?> + dropdownItem(uniqid('copyfile'))->setText('GLOBAL_COPY')->setIcon('copy')->setOnClick('system.createCopy', sprintf( "file:%s", $file->getCryptFileName())); ?>
  • +
  • + dropdownItem(uniqid('alttext'))->setText('FILE_LIST_ALTTEXT')->setIcon('keyboard')->setClass('fpcm-filelist-link-alttext')->setData(['file' => base64_encode($file->getFilename()), 'alttext' => $file->getAltText()]); ?> +
  • permissions->uploads->rename || $theView->permissions->uploads->add) : ?>
  • diff --git a/core/views/system/twitter.php b/core/views/system/twitter.php index c237b28d5..cffc05399 100644 --- a/core/views/system/twitter.php +++ b/core/views/system/twitter.php @@ -18,7 +18,7 @@ twitter_data->consumer_key || !$globalConfig->twitter_data->consumer_secret || !$twitterIsActive) : ?> linkButton('twitterConnect')->setText('SYSTEM_OPTIONS_TWITTER_CONNECT')->setUrl(fpcm\classes\tools::getControllerLink('system/twitter'))->setTarget('_blank')->setIcon('twitter', 'fab'); ?> twitter_data->user_token && $globalConfig->twitter_data->user_secret && $twitterIsActive) : ?> - submitButton('twitterDisconnect')->setText('SYSTEM_OPTIONS_TWITTER_DISCONNECT')->setClass('fpcm ui-button-confirm')->setIcon('trash'); ?> + submitButton('twitterDisconnect')->setText('SYSTEM_OPTIONS_TWITTER_DISCONNECT')->setClickConfirm()->setIcon('trash'); ?> diff --git a/core/views/users/usereditor_extended.php b/core/views/users/usereditor_extended.php index 0a33e7d08..268406ddb 100644 --- a/core/views/users/usereditor_extended.php +++ b/core/views/users/usereditor_extended.php @@ -23,7 +23,7 @@ class="card-img-top rounded-top overflow-hidden p-4"> diff --git a/data/config/ajaxControllers.yml b/data/config/ajaxControllers.yml index ebc0937d0..13784d391 100644 --- a/data/config/ajaxControllers.yml +++ b/data/config/ajaxControllers.yml @@ -1,6 +1,7 @@ ## FanPress CM ajax controller file --- ajax/cache: ajax/common/cache +ajax/copy: ajax/common/copy ajax/autocomplete: ajax/common/autocomplete ajax/autocompleteCleanup: ajax/common/autocompleteCleanup ajax/searchall: ajax/common/searchall diff --git a/inc/classes/crypt.php b/inc/classes/crypt.php index 29def8d12..b27039da3 100644 --- a/inc/classes/crypt.php +++ b/inc/classes/crypt.php @@ -16,7 +16,7 @@ * @license http://www.gnu.org/licenses/gpl.txt GPLv3 * @since 3.5 */ -final class crypt { +final class crypt implements \fpcm\model\interfaces\isObjectInstancable { /** * Methode zur Verschlüsselung @@ -217,4 +217,21 @@ public static function getRandomString($length = 32) : string return random_bytes($length); } + /** + * Returns config class instance + * @return config + * @since 5.2.2 + */ + public static function getInstance() + { + $iClass = self::class; + + if (!isset($GLOBALS['fpcm']['objects'][$iClass])) { + $GLOBALS['fpcm']['objects'][$iClass] = new $iClass(); + } + + return $GLOBALS['fpcm']['objects'][$iClass]; + + } + } diff --git a/inc/classes/dirs.php b/inc/classes/dirs.php index 612f70e88..c484fd1b4 100644 --- a/inc/classes/dirs.php +++ b/inc/classes/dirs.php @@ -67,11 +67,12 @@ private static function initDirs() private static function initUrls() { $isCli = baseconfig::isCli(); + $hasConst = defined('FPCM_URLS_BASE') && trim(FPCM_URLS_BASE); if ($isCli) { $GLOBALS['fpcm']['urls']['base'] = 'localhost'; } - elseif (defined('FPCM_URLS_BASE') && trim(FPCM_URLS_BASE)) { + elseif ($hasConst) { $GLOBALS['fpcm']['urls']['base'] = FPCM_URLS_BASE; } else { @@ -88,10 +89,11 @@ private static function initUrls() if ($isCli) { $GLOBALS['fpcm']['urls']['base'] .= '/' . $fpcmBase . '/'; } - else { + elseif (!$hasConst) { $parsed = parse_url($GLOBALS['fpcm']['urls']['base']); - if ($parsed['path'] !== $fpcmBase) { + if (!str_ends_with($GLOBALS['fpcm']['dir']['base'], $parsed['path']) && + $parsed['path'] !== $fpcmBase) { $GLOBALS['fpcm']['urls']['base'] = sprintf("%s://%s/%s/", $parsed['scheme'], $parsed['host'], $fpcmBase); } } diff --git a/inc/controller/action/articles/article/base.php b/inc/controller/action/articles/article/base.php index 57a90e0d4..0c96d5ca2 100644 --- a/inc/controller/action/articles/article/base.php +++ b/inc/controller/action/articles/article/base.php @@ -10,7 +10,7 @@ /** * Article controller base * @article Stefan Seehafer - * @copyright (c) 2011-2022, Stefan Seehafer + * @copyright (c) 2011-2024, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ abstract class base extends \fpcm\controller\abstracts\controller diff --git a/inc/controller/action/articles/article/edit.php b/inc/controller/action/articles/article/edit.php index f2a9e6552..c88b77abc 100644 --- a/inc/controller/action/articles/article/edit.php +++ b/inc/controller/action/articles/article/edit.php @@ -10,7 +10,7 @@ /** * Article edit controller * @article Stefan Seehafer - * @copyright (c) 2011-2022, Stefan Seehafer + * @copyright (c) 2011-2024, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ class edit extends base { @@ -308,6 +308,17 @@ private function addButtons() ]) ]); + if ($this->article->getId() && $this->permissions->article->add) { + + $this->view->addButton( + (new \fpcm\view\helper\copyButton('articleCopy')) + ->setClass( $this->getToolbarButtonToggleClass(1, '', true) ) + ->setReadonly($this->article->isInEdit()) + ->setCopyParams($this->article, 'article') + ); + + } + if ($this->article->getImagepath()) { $this->view->addButton((new \fpcm\view\helper\linkButton('articleimg')) ->setUrl($this->article->getImagepath()) @@ -319,8 +330,9 @@ private function addButtons() if ($this->permissions->article->delete && !$this->request->fromGET('rev')) { $this->view->addButton((new \fpcm\view\helper\deleteButton('articleDelete')) - ->setClass( $this->getToolbarButtonToggleClass(1, 'fpcm ui-button-confirm', true)) - ->setReadonly($this->article->isInEdit())); + ->setClass( $this->getToolbarButtonToggleClass(1, true)) + ->setReadonly($this->article->isInEdit()) + ->setClickConfirm()); } if ($this->permissions->article->revisions) { @@ -330,8 +342,9 @@ private function addButtons() ->setReadonly($this->article->isInEdit()) ->setClass( $this->getToolbarButtonToggleClass(3) )); $this->view->addButton((new \fpcm\view\helper\deleteButton('revisionDelete')) - ->setClass($this->getToolbarButtonToggleClass(3, 'fpcm ui-button-confirm') ) - ->setText('EDITOR_REVISION_DELETE')); + ->setClass($this->getToolbarButtonToggleClass(3) ) + ->setText('EDITOR_REVISION_DELETE') + ->setClickConfirm()); } $this->addRelationButton(); diff --git a/inc/controller/action/categories/all.php b/inc/controller/action/categories/all.php index dbfa0037c..4529f18c6 100644 --- a/inc/controller/action/categories/all.php +++ b/inc/controller/action/categories/all.php @@ -67,6 +67,10 @@ public function request() $this->view->addNoticeMessage('SAVE_SUCCESS_EDITCATEGORY'); } + if ($this->request->hasMessage('deleted')) { + $this->view->addNoticeMessage('DELETE_SUCCESS_CATEGORIES'); + } + $this->list = new \fpcm\model\categories\categoryList(); $this->rollList = new \fpcm\model\users\userRollList(); @@ -101,7 +105,7 @@ public function process() $this->view->addButtons([ (new \fpcm\view\helper\linkButton('addnew'))->setUrl(\fpcm\classes\tools::getFullControllerLink('categories/add'))->setText('GLOBAL_NEW')->setIcon('tag')->setPrimary(), (new \fpcm\view\helper\button('massEdit', 'massEdit'))->setText('GLOBAL_EDIT')->setIcon('edit')->setIconOnly(), - (new \fpcm\view\helper\deleteButton('delete'))->setClass('fpcm ui-button-confirm') + (new \fpcm\view\helper\deleteButton('delete'))->setClickConfirm() ]); $rolls = (new \fpcm\model\users\userRollList())->getUserRollsTranslated(); @@ -203,5 +207,3 @@ protected function onDelete() : bool } } - -?> diff --git a/inc/controller/action/categories/category/base.php b/inc/controller/action/categories/category/base.php index 3c066d213..70fb62d36 100644 --- a/inc/controller/action/categories/category/base.php +++ b/inc/controller/action/categories/category/base.php @@ -19,7 +19,7 @@ class base extends \fpcm\controller\abstracts\controller use \fpcm\controller\traits\common\simpleEditForm, \fpcm\controller\traits\theme\nav\categories; - + /** * * @var \fpcm\model\categories\category @@ -28,22 +28,38 @@ class base extends \fpcm\controller\abstracts\controller protected $saveMessage = 'added'; + protected $deleteMessage = 'deleted'; + protected $tabHeadline = 'CATEGORIES_ADD'; public function process() { define('FPCM_VIEW_FLOATING_LABEL_ALL', true); - - $this->view->addButton( (new \fpcm\view\helper\saveButton('categorySave'))->setPrimary( $this->category->getId() > 0 ) ); + + $buttons = []; + $buttons[] = (new \fpcm\view\helper\saveButton('categorySave'))->setPrimary( $this->category->getId() > 0 ); + + if ($this->category->getId()) { + + $buttons[] = (new \fpcm\view\helper\copyButton('categoryCopy')) + ->setCopyParams($this->category, 'category'); + + + $buttons[] = (new \fpcm\view\helper\deleteButton('categoryDelete'))->setClickConfirm(); + } + + + $this->view->addButtons($buttons); + $this->view->addJsFiles(['system/categories.js']); $this->view->addTabs('fpcm-category-tabs', [ (new \fpcm\view\helper\tabItem('tabs-category')) ->setText($this->tabHeadline) ->setFile($this->getViewPath() . '.php') ]); - + $selectedGroups = explode(';', $this->category->getGroups() ?? ''); - + $checkFields = []; foreach ((new \fpcm\model\users\userRollList())->getUserRollsTranslated() as $rollname => $rollid) { $checkFields[] = (new \fpcm\view\helper\checkbox('category[groups][]', 'cat'.$rollid)) @@ -70,7 +86,7 @@ public function process() $this->view->render(); } - + public function oncategorySave() { if (!$this->checkPageToken()) { @@ -102,7 +118,7 @@ public function oncategorySave() return false; } - if ($res === false) { + if ($res === false) { $this->view->addErrorMessage('SAVE_FAILED_CATEGORY'); return false; } @@ -110,5 +126,22 @@ public function oncategorySave() $this->redirect('categories/list', [$this->saveMessage => 1]); return true; } - + + public function oncategoryDelete() + { + if (!$this->checkPageToken()) { + $this->view->addErrorMessage('CSRF_INVALID'); + return false; + } + + if ($this->category->delete()) { + $this->redirect('categories/list', [$this->deleteMessage => 1]); + return true; + } + + $this->view->addErrorMessage('DELETE_FAILED_CATEGORIES'); + return false; + + } + } diff --git a/inc/controller/action/ips/all.php b/inc/controller/action/ips/all.php index 64b9c280b..252338e72 100644 --- a/inc/controller/action/ips/all.php +++ b/inc/controller/action/ips/all.php @@ -88,7 +88,7 @@ public function process() $this->view->addJsFiles(['system/ipadresses.js']); $this->view->addButtons([ (new \fpcm\view\helper\linkButton('addnew'))->setUrl(\fpcm\classes\tools::getFullControllerLink('ips/add'))->setText('GLOBAL_NEW')->setIcon('globe')->setPrimary(), - (new \fpcm\view\helper\deleteButton('delete'))->setClass('fpcm ui-button-confirm'), + (new \fpcm\view\helper\deleteButton('delete'))->setClickConfirm(), ]); $this->view->addToolbarRight((string) (new \fpcm\view\helper\select('sortlist'))->setOptions($this->sorts)->setSelected($sort)->setFirstOption(\fpcm\view\helper\select::FIRST_OPTION_DISABLED)); diff --git a/inc/controller/action/smileys/all.php b/inc/controller/action/smileys/all.php index 0e3a6114b..fe23216dd 100644 --- a/inc/controller/action/smileys/all.php +++ b/inc/controller/action/smileys/all.php @@ -54,7 +54,7 @@ public function process() $this->view->addButtons([ (new \fpcm\view\helper\linkButton('addSmiley'))->setText('GLOBAL_NEW')->setUrl(\fpcm\classes\tools::getFullControllerLink('smileys/add'))->setIcon('plus')->setPrimary(), - (new \fpcm\view\helper\deleteButton('deleteSmiley'))->setClass('fpcm ui-button-confirm') + (new \fpcm\view\helper\deleteButton('deleteSmiley'))->setClickConfirm() ]); $this->view->setFormAction('smileys/list'); diff --git a/inc/controller/action/smileys/smiley/edit.php b/inc/controller/action/smileys/smiley/edit.php index dc2a97535..8ac44d930 100644 --- a/inc/controller/action/smileys/smiley/edit.php +++ b/inc/controller/action/smileys/smiley/edit.php @@ -27,7 +27,7 @@ protected function getActionText() : string public function getButtons(): array { $buttons = parent::getButtons(); - $buttons[] = (new \fpcm\view\helper\deleteButton('delete'))->setClass('fpcm ui-button-confirm'); + $buttons[] = (new \fpcm\view\helper\deleteButton('delete'))->setClickConfirm(); return $buttons; } diff --git a/inc/controller/action/system/data/backups.php b/inc/controller/action/system/data/backups.php index 1f87f9298..db8f27328 100644 --- a/inc/controller/action/system/data/backups.php +++ b/inc/controller/action/system/data/backups.php @@ -120,7 +120,7 @@ public function process() $isPg = \fpcm\classes\loader::getObject('\fpcm\classes\database')->getDbtype() === \fpcm\classes\database::DBTYPE_POSTGRES; - $this->view->addButton((new \fpcm\view\helper\deleteButton('delete'))->setClass('fpcm ui-button-confirm')->setReadonly($isPg)->setIconOnly(false)); + $this->view->addButton((new \fpcm\view\helper\deleteButton('delete'))->setClickConfirm()->setReadonly($isPg)->setIconOnly(false)); $this->view->addJsFiles(['system/backups.js']); if ($isPg) { diff --git a/inc/controller/action/templates/templates.php b/inc/controller/action/templates/templates.php index 3fb7d0a88..cb1bc2613 100644 --- a/inc/controller/action/templates/templates.php +++ b/inc/controller/action/templates/templates.php @@ -98,7 +98,7 @@ public function process() $buttons[] = (new \fpcm\view\helper\deleteButton('fileDelete')) - ->setClass('fpcm-ui-maintoolbarbuttons-tab3 fpcm ui-button-confirm ' . $hiddenClass2 ); + ->setClass('fpcm-ui-maintoolbarbuttons-tab3 ' . $hiddenClass2 )->setClickConfirm(); $buttons[] = (new \fpcm\view\helper\button('fileUpload')) ->setText('FILE_LIST_UPLOADFORM') diff --git a/inc/controller/action/users/roll/add.php b/inc/controller/action/users/roll/add.php index af1655e25..885b2694d 100644 --- a/inc/controller/action/users/roll/add.php +++ b/inc/controller/action/users/roll/add.php @@ -3,7 +3,7 @@ /** * User roll add controller * @author Stefan Seehafer - * @copyright (c) 2011-2022, Stefan Seehafer + * @copyright (c) 2011-2024, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ @@ -20,13 +20,10 @@ class add extends rollbase { public function request() { $this->getRollObject(); - $this->save(); $this->view->setFormAction('users/addroll'); - + return parent::request(); } } - -?> diff --git a/inc/controller/action/users/roll/base.php b/inc/controller/action/users/roll/base.php index b02b15467..7c603ecf9 100644 --- a/inc/controller/action/users/roll/base.php +++ b/inc/controller/action/users/roll/base.php @@ -3,13 +3,17 @@ /** * User roll add controller * @author Stefan Seehafer - * @copyright (c) 2011-2022, Stefan Seehafer + * @copyright (c) 2011-2024, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ namespace fpcm\controller\action\users\roll; -abstract class base extends \fpcm\controller\abstracts\controller +abstract class base +extends + \fpcm\controller\abstracts\controller +implements + \fpcm\controller\interfaces\requestFunctions { use \fpcm\controller\traits\theme\nav\users; @@ -20,6 +24,8 @@ abstract class base extends \fpcm\controller\abstracts\controller */ protected $userRoll; + protected $update = false; + public function isAccessible(): bool { return $this->permissions->system->users && $this->permissions->system->rolls; @@ -34,42 +40,79 @@ public function request() { return true; } - + protected function getRollObject($id = null) { $this->userRoll = new \fpcm\model\users\userRoll($id); $this->view->assign('userRoll', $this->userRoll); } - protected function save($update = false) + public function process() { - $this->view->addButton( (new \fpcm\view\helper\saveButton('saveRoll'))->setPrimary() ); - - if (!$this->buttonClicked('saveRoll')) { - return false; + $this->initButtons(); + + $tabs = [ + (new \fpcm\view\helper\tabItem('roll'))->setText($this->headlineVar)->setFile('users/rolledit.php') + ]; + + if ( $this->permissions->system->permissions && $this->userRoll->getId() ) { + $tabs[] = (new \fpcm\view\helper\tabItem('permission'))->setText('HL_OPTIONS_PERMISSIONS')->setFile('users/permissions_editor.php'); + } + + $this->view->addTabs('roll', $tabs, '', $this->getActiveTab()); + + return true; + } + + private function initButtons() + { + + $buttons = [ + (new \fpcm\view\helper\saveButton('saveRoll'))->setPrimary() + ]; + + if ($this->userRoll->getId()) { + $buttons[] = (new \fpcm\view\helper\copyButton('copyRoll'))->setCopyParams($this->userRoll, 'roll'); + } + + if ($this->userRoll->getId() && !$this->userRoll->isSystemRoll()) { + $buttons[] = (new \fpcm\view\helper\deleteButton('deleteRoll'))->setClickConfirm(); } - if ($this->buttonClicked('permissionsSave') && !$this->checkPageToken()) { + + $this->view->addButtons($buttons); + + } + + /** + * + * @return bool + */ + protected function onsaveRoll() + { + $this->initButtons(); + + if (!$this->checkPageToken()) { $this->view->addErrorMessage('CSRF_INVALID'); return false; } $rollName = $this->request->fromPOST('rollname'); - + if (!trim($rollName)) { $this->view->addErrorMessage('SAVE_FAILED_ROLL'); return true; } - + $this->userRoll->setRollName($rollName); $this->userRoll->setCodex($this->request->fromPOST('rollcodex')); - $func = $update ? 'update' : 'save'; - $msg = $update ? 'edited' : 'added'; + $func = $this->update ? 'update' : 'save'; + $msg = $this->update ? 'edited' : 'added'; $result = call_user_func([$this->userRoll, $func]); $errMsg = 'SAVE_FAILED_ROLL'; - if ($update && $result && $this->permissions->system->permissions && !$this->savePermissions()) { + if ($this->update && $result && $this->permissions->system->permissions && !$this->savePermissions()) { $errMsg = 'SAVE_FAILED_PERMISSIONS'; $result = false; } @@ -82,28 +125,49 @@ protected function save($update = false) $this->redirect('users/list', [$msg => 2, 'rg' => 1]); return true; } - + $this->view->addErrorMessage($errMsg); - - + + return true; } - - public function process() + + protected function ondeleteRoll() { - $tabs = [ - (new \fpcm\view\helper\tabItem('roll'))->setText($this->headlineVar)->setFile('users/rolledit.php') - ]; - - if ( $this->permissions->system->permissions && $this->userRoll->getId() ) { - $tabs[] = (new \fpcm\view\helper\tabItem('permission'))->setText('HL_OPTIONS_PERMISSIONS')->setFile('users/permissions_editor.php'); + + if (!$this->checkPageToken()) { + $this->view->addErrorMessage('CSRF_INVALID'); + return false; } - - $this->view->addTabs('roll', $tabs, '', $this->getActiveTab()); + if (!$this->permissions->system->rolls) { + $this->view->addErrorMessage('DELETE_FAILED_ROLL'); + return false; + } + + if ($this->userRoll->isSystemRoll()) { + $this->view->addErrorMessage('DELETE_FAILED_ROLL'); + return false; + } + + if ($this->userRoll->getId() === $this->session->getCurrentUser()->getRoll()) { + $this->view->addErrorMessage('DELETE_FAILED_ROLL_OWN'); + return false; + } + + $count = (new \fpcm\model\users\userRollList)->getUserRolls(); + if (count($count) == 1) { + $this->view->addErrorMessage('DELETE_FAILED_ROLL_OWN'); + return false; + } + + if (!$this->userRoll->delete()) { + $this->view->addErrorMessage('DELETE_FAILED_ROLL'); + return false; + } + + $this->redirect('users/list', ['rg' => 1]); return true; } } - -?> diff --git a/inc/controller/action/users/roll/edit.php b/inc/controller/action/users/roll/edit.php index ea8506420..afdea4832 100644 --- a/inc/controller/action/users/roll/edit.php +++ b/inc/controller/action/users/roll/edit.php @@ -3,7 +3,7 @@ /** * User roll add controller * @author Stefan Seehafer - * @copyright (c) 2011-2022, Stefan Seehafer + * @copyright (c) 2011-2024, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ @@ -13,6 +13,8 @@ class edit extends base { use \fpcm\controller\traits\users\savePermissions; + protected $update = true; + /** * * @var string @@ -34,19 +36,18 @@ public function request() $this->view = new \fpcm\view\error('LOAD_FAILED_ROLL', 'users/list'); return true; } - + $this->view->setFormAction($this->userRoll->getEditLink(), [], true); if ($this->permissions->system->permissions) { $this->fetchRollPermssions(); } - $this->save(true); return parent::request(); } /** - * + * * @return bool */ public function process() { @@ -59,5 +60,3 @@ public function process() { } } - -?> diff --git a/inc/controller/action/users/userlist.php b/inc/controller/action/users/userlist.php index 7ed2dc21e..ab240a1d8 100644 --- a/inc/controller/action/users/userlist.php +++ b/inc/controller/action/users/userlist.php @@ -3,7 +3,7 @@ /** * Login controller * @author Stefan Seehafer - * @copyright (c) 2011-2022, Stefan Seehafer + * @copyright (c) 2011-2024, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ @@ -14,7 +14,7 @@ class userlist extends \fpcm\controller\abstracts\controller use \fpcm\controller\traits\theme\nav\users, \fpcm\model\traits\statusIcons; - + /** * * @var \fpcm\model\users\userList @@ -46,7 +46,7 @@ class userlist extends \fpcm\controller\abstracts\controller protected $chartItemColors; /** - * + * * @return string */ protected function getViewPath() : string @@ -55,7 +55,7 @@ protected function getViewPath() : string } /** - * + * * @return bool */ public function isAccessible(): bool @@ -64,7 +64,7 @@ public function isAccessible(): bool } /** - * + * * @return bool */ protected function initActionObjects() @@ -76,7 +76,7 @@ protected function initActionObjects() } /** - * + * * @return bool */ public function request() @@ -97,37 +97,37 @@ public function request() } /** - * + * * @return bool */ public function process() { $this->initTabs(); - + $this->view->assign('usersListSelect', $this->userList->getUsersNameList()); - + $chart = new \fpcm\components\charts\chart(\fpcm\components\charts\chart::TYPE_PIE, 'userArticles'); $chart->setLegend([ 'position' => 'right' ]); $this->view->addCssFiles($chart->getCssFiles()); - + $this->view->addJsFiles(array_merge(['users/module.js'], $chart->getJsFiles())); $this->view->addJsLangVars(['USERS_ARTICLES_SELECT', 'HL_OPTIONS_PERMISSIONS']); $this->view->setFormAction('users/list'); - + $ddOpt = [ (new \fpcm\view\helper\dropdownItem('addUser'))->setUrl(\fpcm\classes\tools::getFullControllerLink('users/add'))->setText('USERS_ADD')->setValue('user')->setIcon('user-plus'), ]; - + if ($this->permissions->system->rolls) { $ddOpt[] = (new \fpcm\view\helper\dropdownItem('addRoll'))->setUrl(\fpcm\classes\tools::getFullControllerLink('users/addroll'))->setText('USERS_ROLL_ADD')->setValue('roll')->setIcon('user-tag'); } - $this->view->addButton( (new \fpcm\view\helper\dropdown('new'))->setText('GLOBAL_NEW')->setIcon('plus')->setOptions($ddOpt) ); - + $this->view->addButton( (new \fpcm\view\helper\dropdown('new'))->setText('GLOBAL_NEW')->setIcon('plus')->setOptions($ddOpt)->overrideButtonType('primary') ); + $this->view->addToolbarRight((string) (new \fpcm\view\helper\button('userStats')) ->setText('USERS_STATS_ARTICLE') ->setIcon('chart-pie') @@ -140,27 +140,27 @@ public function process() ]) ); $this->createUsersView(); - + if ($this->permissions->system->rolls) { $this->createRollsView(); } $chart->setLabels(array_keys($this->chartItems)); - + $chartItem = new \fpcm\components\charts\chartItem( array_values($this->chartItems), array_values($this->chartItemColors) ); $chartItem->setBorderColor('none'); - + $chart->setValues($chartItem); $this->view->assign('userArticles', $chart); $this->view->addJsVars([ 'chartData' => $chart ]); - + $this->view->includeForms('users'); $this->view->addAjaxPageToken('users/actions'); $this->view->render(); @@ -175,11 +175,11 @@ private function createUsersView() { $usersInGroups = $this->userList->getUsersAll(true); $userGroups = $this->rollList->getUserRollsByIds(array_keys($usersInGroups)); - + $notFoundRoll = new \fpcm\model\users\userRoll(); $notFoundRoll->setRollName($this->language->translate('GLOBAL_NOTFOUND')); $notFoundRoll->setId(-1); - + $userGroups[-1] = $notFoundRoll; if (!isset($usersInGroups[-1])) { $usersInGroups[-1] = []; @@ -190,10 +190,10 @@ private function createUsersView() $usersInGroups[-1] += $diff; }, array_diff_key($usersInGroups, $userGroups)); - + $dataView = new \fpcm\components\dataView\dataView('userlist'); - + $dataView->addColumns([ (new \fpcm\components\dataView\column('button', '', 'flex-grow-1'))->setSize('auto')->setAlign('center'), (new \fpcm\components\dataView\column('username', 'GLOBAL_USERNAME'))->setSize(3), @@ -206,16 +206,16 @@ private function createUsersView() $currentUser = $this->session->getUserId(); $descr = $this->language->translate('USERS_ROLL'); - + $usersInGroups = array_filter($usersInGroups, function ($users, $rollId) use ($userGroups) { return !isset($userGroups[$rollId]) || ($rollId === -1) && !count($users) ? false : true; }, ARRAY_FILTER_USE_BOTH); - - + + foreach($usersInGroups AS $rollId => $users) { - + $title = '' . $descr.': '.$this->language->translate($userGroups[$rollId]->getRollName()) . ''; - + $dataView->addRow( new \fpcm\components\dataView\row([ new \fpcm\components\dataView\rowCol('button', '', 'd-none d-lg-block'), @@ -225,7 +225,7 @@ private function createUsersView() new \fpcm\components\dataView\rowCol('metadata', '', 'd-none d-lg-block'), ], '', true )); - + /* @var $user \fpcm\model\users\author */ foreach ($users as $userId => $user) { @@ -240,12 +240,12 @@ private function createUsersView() (new \fpcm\view\helper\badge('art'.$userId))->setValue($count)->setText('USERS_ARTICLE_COUNT')->setIcon('book'), $this->getStatusColor( (new \fpcm\view\helper\icon('user-slash fa-inverse'))->setText('USERS_DISABLED')->setClass('fpcm-ui-editor-metainfo')->setStack('square') , $user->getDisabled() ) ]; - + $buttons = [ (new \fpcm\view\helper\editButton('useredit'.$userId))->setUrlbyObject($user), (new \fpcm\view\helper\linkButton('usermail'.$userId))->setUrl('mailto:'.$user->getEmail())->setIcon('envelope')->setIconOnly()->setText('GLOBAL_WRITEMAIL'), ]; - + if ($user->getDisabled()) { $buttons[] = (new \fpcm\view\helper\submitButton(uniqid('enableUser'))) ->setText('GLOBAL_ENABLE') @@ -264,7 +264,7 @@ private function createUsersView() ->setReadonly($noRb) ->setData(['oid' => $userId, 'fn' => 'disableUser', 'dest' => 'confirmExec']); } - + $buttons[] = (new \fpcm\view\helper\deleteButton(uniqid('deleteUser'))) ->setClass('fpcm ui-userlist-actione') ->setIconOnly() @@ -282,7 +282,7 @@ private function createUsersView() )); } - + } $this->view->addDataView($dataView); @@ -296,9 +296,9 @@ private function createUsersView() private function createRollsView() { $rolls = $this->rollList->getUserRollsTranslated(); - + $dataView = new \fpcm\components\dataView\dataView('rollslist'); - + $dataView->addColumns([ (new \fpcm\components\dataView\column('button', ''))->setSize(2)->setAlign('center'), (new \fpcm\components\dataView\column('title', 'USERS_ROLLS_NAME'))->setSize('auto'), @@ -311,7 +311,7 @@ private function createRollsView() 'id' => $rollId ])) ]; - + if ($this->permissions->system->permissions) { $buttons[] = (new \fpcm\view\helper\linkButton('rollPermBtn'.$rollId))->setUrl(\fpcm\classes\tools::getFullControllerLink('users/permissions', [ 'id' => $rollId @@ -321,13 +321,13 @@ private function createRollsView() ->setClass('fpcm ui-rolls-edit') ->setData(['type' => 'iframe']); } - + $buttons[] = (new \fpcm\view\helper\deleteButton(uniqid('deleteROll'))) ->setClass('fpcm ui-rollslist-action-delete') ->setIconOnly() ->setReadonly($rollId <= 3) ->setData(['oid' => $rollId, 'fn' => 'deleteRoll']); - + $dataView->addRow( new \fpcm\components\dataView\row([ new \fpcm\components\dataView\rowCol('button', implode('', $buttons), '', \fpcm\components\dataView\rowCol::COLTYPE_ELEMENT), @@ -340,7 +340,7 @@ private function createRollsView() $this->view->addDataView($dataView); return true; } - + protected function initTabs() { $tabs = []; @@ -348,17 +348,15 @@ protected function initTabs() ->setText('USERS_LIST') ->setFile($this->getViewPath()) ->setTabToolbar(1); - + if ($this->permissions->system->rolls) { $tabs[] = (new \fpcm\view\helper\tabItem('rolls')) ->setText('USERS_LIST_ROLLS') ->setFile('components/dataview__inline.php') ->setTabToolbar(2); } - + $this->view->addTabs('users', $tabs, '', $this->getActiveTab()); } } - -?> diff --git a/inc/controller/action/wordban/all.php b/inc/controller/action/wordban/all.php index 8096ce3d4..265b25402 100644 --- a/inc/controller/action/wordban/all.php +++ b/inc/controller/action/wordban/all.php @@ -69,7 +69,7 @@ public function process() $this->view->addJsFiles(['system/texts.js']); $this->view->addButtons([ (new \fpcm\view\helper\linkButton('addnew'))->setUrl(\fpcm\classes\tools::getFullControllerLink('wordban/add'))->setText('GLOBAL_NEW')->setIcon('ban')->setPrimary(), - (new \fpcm\view\helper\deleteButton('delete'))->setClass('fpcm ui-button-confirm') + (new \fpcm\view\helper\deleteButton('delete'))->setClickConfirm() ]); return true; diff --git a/inc/controller/action/wordban/item/edit.php b/inc/controller/action/wordban/item/edit.php index 80ebc9ea8..056397d74 100644 --- a/inc/controller/action/wordban/item/edit.php +++ b/inc/controller/action/wordban/item/edit.php @@ -36,7 +36,7 @@ public function process() } /** - * + * * @return string */ protected function getActionText() : string @@ -53,22 +53,30 @@ protected function onDelete() if (!$this->checkPageToken()) { $this->view->addErrorMessage('CSRF_INVALID'); return true; - } - + } + if (!$this->item->delete()) { - $this->view->addErrorMessage('DELETE_FAILED_WORDBAN'); + $this->view->addErrorMessage('DELETE_FAILED_WORDBAN'); return true; } - + $this->redirect('wordban/list', array('deleted' => 1)); - return true; + return true; } - + + /** + * + * @return array + */ public function getButtons(): array { $buttons = parent::getButtons(); - $buttons[] = (new \fpcm\view\helper\deleteButton('delete'))->setClass('fpcm ui-button-confirm'); - + + $buttons[] = (new \fpcm\view\helper\copyButton('categoryText')) + ->setCopyParams($this->item, 'text'); + + $buttons[] = (new \fpcm\view\helper\deleteButton('delete'))->setClickConfirm(); + return $buttons; } diff --git a/inc/controller/ajax/common/copy.php b/inc/controller/ajax/common/copy.php new file mode 100644 index 000000000..14d0eef6a --- /dev/null +++ b/inc/controller/ajax/common/copy.php @@ -0,0 +1,256 @@ + + * @copyright (c) 2024, Stefan Seehafer + * @license http://www.gnu.org/licenses/gpl.txt GPLv3 + * @since 5.2.2 + */ +class copy extends \fpcm\controller\abstracts\ajaxController +{ + + use \fpcm\controller\traits\common\isAccessibleTrue; + + /** + * + * @var bool + */ + private bool $result = false; + + /** + * + * @var string + */ + private string $destination = ''; + + /** + * + * @var \fpcm\view\message|null + */ + private ?\fpcm\view\message $message = null; + + /** + * + * @var string + */ + private string $callback = ''; + + /** + * Controller-Processing + */ + public function process() + { + if ($this->processByParam() !== true) { + $this->response + ->setReturnData([ + 'message' => $this->message + ]) + ->fetch(); + } + + $this->response->setReturnData([ + 'result' => $this->result, + 'message' => $this->message, + 'destination' => $this->destination, + 'callback' => $this->callback + ])->fetch(); + + } + + /** + * + * @return bool + */ + protected function processArticle() : bool + { + if (!$this->permissions->article->add) { + return false; + } + + $id = $this->request->fromPOST('id'); + if (!$id) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_WORDBAN'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $obj = new \fpcm\model\articles\article($id); + if (!$obj instanceof \fpcm\model\interfaces\isCopyable || !$obj->exists()) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_WORDBAN'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $newId = $obj->copy(); + if (!$newId) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_WORDBAN'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $this->result = $newId; + + $this->destination = \fpcm\classes\tools::getControllerLink('articles/edit', [ + 'id' => $newId, + 'added' => $this->permissions->article->approve ? 2 : 1 + ]); + + return true; + } + + /** + * + * @return bool + */ + protected function processCategory() : bool + { + if (!$this->permissions->system->categories) { + return false; + } + + $id = $this->request->fromPOST('id'); + if (!$id) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_WORDBAN'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $obj = new \fpcm\model\categories\category($id); + if (!$obj instanceof \fpcm\model\interfaces\isCopyable || !$obj->exists()) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_WORDBAN'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $newId = $obj->copy(); + if (!$newId) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_WORDBAN'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $this->result = $newId; + + $this->destination = \fpcm\classes\tools::getControllerLink('categories/edit', [ + 'id' => $newId + ]); + + return true; + } + + /** + * + * @return bool + */ + protected function processFile() : bool + { + if (!$this->permissions->uploads->add) { + return false; + } + + $fn = $this->request->fromPOST('id', [ + \fpcm\model\http\request::FILTER_BASE64DECODE, + \fpcm\model\http\request::FILTER_DECRYPT + ]); + + if (!$fn) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_UPLOAD_COPY1'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $obj = new \fpcm\model\files\image($fn); + if (!$obj instanceof \fpcm\model\interfaces\isCopyable || !$obj->existsFolder()) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_UPLOAD_COPY2'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $newId = $obj->copy(); + if (!$newId) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_UPLOAD_COPY3'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $this->result = $newId; + $this->callback = 'filemanager.reloadFiles'; + return true; + } + + /** + * + * @return bool + */ + protected function processText() : bool + { + if (!$this->permissions->system->wordban) { + return false; + } + + $id = $this->request->fromPOST('id'); + if (!$id) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_WORDBAN'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $obj = new \fpcm\model\wordban\item($id); + if (!$obj instanceof \fpcm\model\interfaces\isCopyable || !$obj->exists()) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_WORDBAN'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $newId = $obj->copy(); + if (!$newId) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_WORDBAN'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $this->result = $newId; + + $this->destination = \fpcm\classes\tools::getControllerLink('wordban/edit', [ + 'id' => $newId + ]); + + return true; + } + + /** + * + * @return bool + */ + protected function processRoll() : bool + { + if (!$this->permissions->system->rolls) { + return false; + } + + $id = $this->request->fromPOST('id'); + if (!$id) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_ROLL'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $obj = new \fpcm\model\users\userRoll($id); + if (!$obj instanceof \fpcm\model\interfaces\isCopyable || !$obj->exists()) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_ROLL'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $newId = $obj->copy(); + if (!$newId) { + $this->message = new \fpcm\view\message($this->language->translate('SAVE_FAILED_ROLL'), \fpcm\view\message::TYPE_ERROR); + return false; + } + + $this->result = $newId; + + $this->destination = \fpcm\classes\tools::getControllerLink('users/editroll', [ + 'id' => $newId + ]); + + return true; + } + +} diff --git a/inc/controller/ajax/users/actions.php b/inc/controller/ajax/users/actions.php index 5b818029d..130bd70a2 100644 --- a/inc/controller/ajax/users/actions.php +++ b/inc/controller/ajax/users/actions.php @@ -188,5 +188,3 @@ protected function processDeleteRoll() } } - -?> diff --git a/inc/controller/traits/users/savePermissions.php b/inc/controller/traits/users/savePermissions.php index 5383b59c2..8d0b72879 100644 --- a/inc/controller/traits/users/savePermissions.php +++ b/inc/controller/traits/users/savePermissions.php @@ -71,5 +71,3 @@ protected function assignToView() : bool } } - -?> \ No newline at end of file diff --git a/inc/lang/de/vars.php b/inc/lang/de/vars.php index b709e5eaf..a6e0c438f 100644 --- a/inc/lang/de/vars.php +++ b/inc/lang/de/vars.php @@ -291,6 +291,9 @@ 'GLOBAL_CACHE_CLEAR' => 'Cache leeren', 'GLOBAL_CLOSE' => 'Schließen', 'GLOBAL_CONFIRM' => 'Bitte bestätigen', + 'GLOBAL_COPY' => 'Kopieren', + 'GLOBAL_COPY_OF' => 'Kopie von "%s"', + 'GLOBAL_COPY_OF_FILE' => 'Kopie_von_%s', 'GLOBAL_DELETE' => 'Löschen', 'GLOBAL_DEPRECATED' => 'Veraltet, wird in Kürze entfernt', 'GLOBAL_DISABLE' => 'Deaktivieren', @@ -530,7 +533,7 @@ 'PACKAGEMANAGER_SUCCESS' => 'Die Installation des Pakets wurde erfolgreich durchgeführt.', 'PACKAGEMANAGER_SUCCESS_UPDATE' => 'Die Aktualisierung des Pakets wurde erfolgreich durchgeführt.', 'PACKAGEMANAGER_TIMER' => 'Benötigte Zeit:', - 'PACKAGEMANAGER_UPDATEDB' => 'Aktualisiere Datenbank...', + 'PACKAGEMANAGER_UPDATEDB' => 'Aktualisierung wird abgeschlossen...', 'PACKAGEMANAGER_UPDATEFS' => 'Aktualisiere Dateisystem...', 'PACKAGEMANAGER_UPDATELOG' => 'Aktualisiere Paketmanager-Protokoll...', 'PACKAGES_BACKTODASHBOARD' => 'Zurück zum Dashboard', @@ -665,6 +668,7 @@ 'SAVE_FAILED_UPLOADMODULE' => 'Beim Hochladen der Modul-Paketdatei ist ein Fehler aufgetreten!', 'SAVE_FAILED_UPLOADPHP' => 'Beim Hochladen der Dateien ist ein Fehler aufgetreten!
    {{filenames}}', 'SAVE_FAILED_UPLOADTPLFILE' => 'Beim Hochladen der Vorlage ist ein Fehler aufgetreten!', + 'SAVE_FAILED_UPLOAD_COPY' => 'Beim Kopieren der Dateien ist ein Fehler aufgetreten!', 'SAVE_FAILED_UPLOAD_GEN' => 'Beim Hochladen der Dateien ist folgender Fehler aufgetreten: {{uploadMsg}}', 'SAVE_FAILED_USER' => 'Der Benutzer konnte nicht gespeichert werden!', 'SAVE_FAILED_USER_DISABLE' => 'Der Benutzer konnte nicht deaktiviert werden!', diff --git a/inc/lang/en/vars.php b/inc/lang/en/vars.php index 6bf6b52e0..b31e49435 100644 --- a/inc/lang/en/vars.php +++ b/inc/lang/en/vars.php @@ -291,6 +291,9 @@ 'GLOBAL_CACHE_CLEAR' => 'Clear cache', 'GLOBAL_CLOSE' => 'Close', 'GLOBAL_CONFIRM' => 'Please confirm', + 'GLOBAL_COPY' => 'Copy', + 'GLOBAL_COPY_OF' => 'Copy of "%s"', + 'GLOBAL_COPY_OF_FILE' => 'Copy_of_%s', 'GLOBAL_DELETE' => 'Delete', 'GLOBAL_DEPRECATED' => 'Deprecated, will be removed shortly', 'GLOBAL_DISABLE' => 'Disable', @@ -530,7 +533,7 @@ 'PACKAGEMANAGER_SUCCESS' => 'The installation of the package was successfully completed.', 'PACKAGEMANAGER_SUCCESS_UPDATE' => 'The package update was performed successfully.', 'PACKAGEMANAGER_TIMER' => 'Time required:', - 'PACKAGEMANAGER_UPDATEDB' => 'Updating database...', + 'PACKAGEMANAGER_UPDATEDB' => 'Update is being completed...', 'PACKAGEMANAGER_UPDATEFS' => 'Update file system...', 'PACKAGEMANAGER_UPDATELOG' => 'Update package manager log...', 'PACKAGES_BACKTODASHBOARD' => 'Back to dashboard', @@ -665,6 +668,7 @@ 'SAVE_FAILED_UPLOADMODULE' => 'An error occurred while uploading the module package file!', 'SAVE_FAILED_UPLOADPHP' => 'An error occurred while uploading the files!
    {{filenames}}', 'SAVE_FAILED_UPLOADTPLFILE' => 'An error occurred while uploading the template!', + 'SAVE_FAILED_UPLOAD_COPY' => 'An error occurred trying to copy the file!!', 'SAVE_FAILED_UPLOAD_GEN' => 'The following error occurred while uploading the files: {{uploadMsg}}.', 'SAVE_FAILED_USER' => 'The user could not be saved!', 'SAVE_FAILED_USER_DISABLE' => 'The user could not be deactivated!', diff --git a/inc/migrations/v521rc1.php b/inc/migrations/v521rc1.php index e880c1bf7..8fc8dcffd 100644 --- a/inc/migrations/v521rc1.php +++ b/inc/migrations/v521rc1.php @@ -21,7 +21,7 @@ class v521rc1 extends migration { protected function updateFileSystem(): bool { - fpcmLogSystem('Cleanuop file system for outdated files...'); + fpcmLogSystem('Cleanup file system for outdated files...'); $dirs_list = file( \fpcm\classes\dirs::getDataDirPath(\fpcm\classes\dirs::DATA_CONFIG . DIRECTORY_SEPARATOR . 'dirslist_v521.txt') ); if (!is_array($dirs_list)) { diff --git a/inc/model/abstracts/cli.php b/inc/model/abstracts/cli.php index 98b48dda2..fc36ec6b4 100644 --- a/inc/model/abstracts/cli.php +++ b/inc/model/abstracts/cli.php @@ -105,6 +105,12 @@ abstract class cli extends \fpcm\model\abstracts\staticModel { */ const PARAM_LISTROLLS = '--listrolls'; + /** + * CLI param: --check + * @ignore + */ + const PARAM_EXECCHECK = '--check'; + /** * CLI param: --exsystem * @ignore diff --git a/inc/model/abstracts/dataset.php b/inc/model/abstracts/dataset.php index b9ddfbe2e..02abecb27 100644 --- a/inc/model/abstracts/dataset.php +++ b/inc/model/abstracts/dataset.php @@ -15,7 +15,7 @@ /** * Model base object - * + * * @package fpcm\model\abstracts * @abstract * @author Stefan Seehafer aka imagine @@ -74,7 +74,7 @@ abstract class dataset implements \fpcm\model\interfaces\dataset, \Stringable { /** * Event-Liste - * @var \fpcm\events\events + * @var \fpcm\events\events */ protected $events; @@ -125,7 +125,7 @@ public function __construct($id = null) if (method_exists($this, 'getTableName')) { $this->getTableName(); } - + $this->dbcon = loader::getObject('\fpcm\classes\database'); $this->events = loader::getObject('\fpcm\events\events'); $this->cache = loader::getObject('\fpcm\classes\cache'); @@ -286,9 +286,9 @@ public function save() } $this->id = $this->dbcon->getLastInsertId(); - + $this->afterUpdateInternal(); - + $afterEvent = $this->getEventName('saveAfter'); if (class_exists(event::getEventNamespace($afterEvent))) { $this->events->trigger($afterEvent, $this->id)->getData(); diff --git a/inc/model/abstracts/file.php b/inc/model/abstracts/file.php index 4211c7b56..da261486a 100644 --- a/inc/model/abstracts/file.php +++ b/inc/model/abstracts/file.php @@ -453,6 +453,18 @@ public function isValidDataFolder(string $path = '', string $type = '/') : bool return ops::isValidDataFolder($path, $type); } + /** + * Returns encrypted filename + * @return string + * @since 5.2.2-dev + */ + public function getCryptFileName() : string + { + $crypt = \fpcm\classes\crypt::getInstance(); + + return base64_encode($crypt->encrypt($this->filename)); + } + /** * "realpath" wrapper for non-existing files * @param string $path diff --git a/inc/model/articles/article.php b/inc/model/articles/article.php index 854305e9c..5e115cef6 100644 --- a/inc/model/articles/article.php +++ b/inc/model/articles/article.php @@ -9,14 +9,18 @@ /** * Artikel object - * + * * @package fpcm\model\articles * @author Stefan Seehafer aka imagine * @copyright (c) 2011-2022, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ -class article extends \fpcm\model\abstracts\dataset -implements \fpcm\model\interfaces\isCsvImportable { +class article +extends + \fpcm\model\abstracts\dataset +implements + \fpcm\model\interfaces\isCsvImportable, + \fpcm\model\interfaces\isCopyable { use \fpcm\model\traits\autoTable, \fpcm\model\traits\statusIcons, @@ -25,7 +29,7 @@ class article extends \fpcm\model\abstracts\dataset \fpcm\model\traits\articles\revisionUtils, \fpcm\model\traits\articles\twitterUtils, \fpcm\model\traits\articles\sourcesUtils; - + /** * Cache-Name für einzelnen Artikel * @since 3.4 @@ -612,7 +616,7 @@ public function getPinnedUntil(): int { public function setPinnedUntil(int $pinnedUntil) { $this->pinned_until = $pinnedUntil; } - + /** * Setzt Status, ob Artikel bearbeitet werden kann * @param bool $editPermission @@ -650,7 +654,7 @@ public function getNicePathString() : string { return $this->cleanupUrlString( trim($this->url) ? $this->url : $this->title ); } - + /** * Return frontend article link * @param string $params @@ -687,9 +691,9 @@ public function getArticleShortLink() { $elLink = $this->getElementLink(); $elLinkEncode = urlencode($elLink); - + $external = !\fpcm\classes\baseconfig::canConnect() || (defined('FPCM_ARTICLE_DISABLE_SHORTLINKS') && FPCM_ARTICLE_DISABLE_SHORTLINKS) ? false : true; - + $return = $this->events->trigger('article\getShortLink', [ 'url' => $elLink, 'encoded' => $elLinkEncode, @@ -755,14 +759,14 @@ public function delete() } /** - * + * * @return bool * @since 5.1.0-a1 */ public function pushCategories() : bool { $categories = $this->getCategories(); - + if (!(new articleCategory($this->id, 0))->deleteByArticle()) { trigger_error(sprintf('Error while clean up article category assignement table for article %s', $this->id)); return true; @@ -841,6 +845,36 @@ public function isOldArticle() : bool return $this->createtime <= time() - FPCM_ARTICLES_OLDMESSAGE_INTERVALL; } + /** + * Creates copy of current article + * @return int + * @since 5.2.2-dev + */ + public function copy(): int + { + $cn = self::class; + + /* @var $copy article */ + $copy = new $cn(); + $copy->setTitle($this->language->translate('GLOBAL_COPY_OF', [$this->title], true)); + $copy->setContent($this->content); + $copy->setApproval($this->approval); + $copy->setArchived($this->archived); + $copy->setPinned($this->pinned); + $copy->setPinnedUntil($this->pinned_until); + $copy->setPostponed($this->postponed); + $copy->setComments($this->comments); + $copy->setCategories($this->getCategories()); + $copy->setImagepath($this->imagepath); + $copy->setSources($this->sources); + + $copy->setRelatesTo($this->id); + $copy->setCreateuser(\fpcm\model\system\session::getInstance()->getUserId()); + $copy->setCreatetime(time()); + + return $copy->save() ?: 0; + } + /** * Führt Ersetzung von gesperrten Texten in Artikel-Daten durch * @return bool diff --git a/inc/model/categories/category.php b/inc/model/categories/category.php index 5bad4445b..1d2c04f7b 100644 --- a/inc/model/categories/category.php +++ b/inc/model/categories/category.php @@ -9,14 +9,18 @@ /** * Kategorie-Objekt - * + * * @package fpcm\model\categories * @author Stefan Seehafer aka imagine * @copyright (c) 2011-2022, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ -class category extends \fpcm\model\abstracts\dataset -implements \fpcm\model\interfaces\isCsvImportable { +class category +extends + \fpcm\model\abstracts\dataset +implements + \fpcm\model\interfaces\isCsvImportable, + \fpcm\model\interfaces\isCopyable { /** * Kategorie-Name @@ -125,7 +129,7 @@ public function getCategoryImage() if (!$this->getIconPath()) { return (new \fpcm\view\helper\icon('image'))->setStack('ban text-danger')->setStackTop(true)->setText('GLOBAL_NOTFOUND'); } - + return '' . $this->getName() . ''; } @@ -163,7 +167,7 @@ public function update() * @return bool * @since 4.5-b8 */ - public function assignCsvRow(array $csvRow): bool + public function assignCsvRow(array $csvRow): bool { $data = array_intersect_key($csvRow, array_flip($this->getFields())); @@ -184,7 +188,7 @@ public function assignCsvRow(array $csvRow): bool $obj = clone $this; - $obj->setName($data['name']); + $obj->setName($data['name']); $obj->setGroups( implode(';', array_map( 'intval', explode(';', $data['groups']) ) ) ); $obj->setIconPath($data['iconpath'] ?? ''); @@ -260,4 +264,22 @@ protected function afterUpdateInternal(): bool return true; } + /** + * Creates copy of category + * @return int + * @since 5.2.2-dev + */ + public function copy(): int + { + $cn = self::class; + + /* @var $copy category */ + $copy = new $cn(); + $copy->setName($this->language->translate('GLOBAL_COPY_OF', [$this->name], true)); + $copy->setIconPath($this->iconpath); + $copy->setGroups($this->getGroups()); + + return $copy->save() ?: 0; + } + } diff --git a/inc/model/cli/files.php b/inc/model/cli/files.php new file mode 100644 index 000000000..d68d501e9 --- /dev/null +++ b/inc/model/cli/files.php @@ -0,0 +1,251 @@ + + * @copyright (c) 2024, Stefan Seehafer + * @license http://www.gnu.org/licenses/gpl.txt GPLv3 + * @since 5.2.2-a1 + */ +final class files extends \fpcm\model\abstracts\cli { + + /** + * /data folder whitelist for checks + */ + const DIRS_DATA_WHITELIST = [ + 'dbstruct', + 'share' + ]; + + /** + * File index array + * @var array + */ + private array $filesIndex = []; + + /** + * Folder excludes parameter + * @var array + */ + private array $excludes = []; + + /** + * Base path + * @var string + */ + private string $base = ''; + + /** + * Modul ausführen + * @return void + */ + public function process() + { + $fn = match ($this->funcParams[0]) { + self::PARAM_EXECCHECK => 'check', + self::PARAM_REMOVE => 'remove', + default => null + }; + + if (!$fn) { + $this->output('Invalid params', true); + } + + $this->base = \fpcm\classes\dirs::getFullDirPath(DIRECTORY_SEPARATOR); + + if (count($this->funcParams) > 1) { + $this->excludes = array_map([$this, 'escapeExcludes'], array_slice($this->funcParams, 1)); + $this->output(sprintf("Exclude from check:\n\n%s\n", implode(PHP_EOL, $this->excludes))); + } + + + $this->{$fn}(); + return true; + } + + /** + * Run check + * @param bool $out + * @return bool + */ + private function check(bool $out = true) + { + fpcmLogSystem('Scan for file system for outdated files...'); + + $filesPath = \fpcm\model\packages\update::getFilesListPath(); + if (!file_exists($filesPath) || !is_readable($filesPath)) { + $this->output(sprintf('%s does not exists or is no readable', $filesPath), true); + } + + $fileListContent = file($filesPath); + if (!is_array($fileListContent)) { + $this->output(sprintf('Failed to read %s', $filesPath), true); + return false; + } + + $fileListContent = array_slice($fileListContent, 0, -2); + if (!is_array($fileListContent)) { + $this->output('Invalid files data', true); + } + + $fileListContent = array_map(function ($fp) { + $fp = trim($fp); + return $fp === 'fanpress' ? $this->base : str_replace('fanpress/', $this->base, $fp); + }, $fileListContent); + + if (!count($fileListContent)) { + $this->output('Invalid files data', true); + return false; + } + + $dirs = array_filter($fileListContent, function ($fp) { + + $fp = trim($fp); + $bn = basename($fp); + + if (str_contains($fp, '/data') && !in_array($bn, self::DIRS_DATA_WHITELIST) ) { + return false; + } + + $p = str_replace('fanpress/', $this->base, $fp); + return is_dir($p); + }); + + if (!count($dirs)) { + $this->output('Invalid directory list count data', true); + return false; + } + + $progress = new progress(count($dirs)); + + $i = 1; + + foreach ($dirs as $dir) { + + $progress + ->setOutputText(\fpcm\model\files\ops::removeBaseDir($dir)) + ->setCurrentValue($i) + ->output(); + + $lup = realpath($dir . DIRECTORY_SEPARATOR); + if (!$lup) { + $this->output(sprintf('Invalid files lookup path %s', $lup), true); + } + + $lup .= DIRECTORY_SEPARATOR . '*'; + + $glob = glob($lup); + if (!is_array($glob) ) { + $this->output(sprintf('Failed to check files %s', $lup), true); + } + + $this->filesIndex = array_merge_recursive($this->filesIndex, $glob); + + $i++; + usleep(2500); + } + + + $this->filesIndex = array_diff($this->filesIndex, $fileListContent); + $this->filesIndex = array_diff($this->filesIndex, $this->excludes); + + if (!$out) { + return true; + } + + if (!count($this->filesIndex)) { + $this->output("No outdated files found.", true); + } + + $this->output(sprintf("Outdated found:\n\n- %s\n", implode(PHP_EOL . '- ', $this->filesIndex))); + return true; + } + + /** + * Remove old files + */ + private function remove() + { + + if (!$this->check(true)) { + $this->output(sprintf('Failed to check files %s', $this->base), true); + } + + if (io::input('Press any key to continue') === false) { + exit; + } + + $progress = new progress(count($this->filesIndex)); + + $i = 1; + + foreach ($this->filesIndex as $path) { + + + $progress + ->setOutputText(\fpcm\model\files\ops::removeBaseDir($path)) + ->setCurrentValue($i) + ->output(); + + + if (!file_exists($path) || !is_writable($path)) { + continue; + } + + if (!unlink($path)) { + $this->output(sprintf('Failed to remove files %s, cancel process...', $path), true); + } + + $i++; + } + + usleep(5000); + $this->filesIndex = []; + + $this->check(); + } + + /** + * Hilfe-Text zurückgeben ausführen + * @return array + */ + public function help() + { + $lines = []; + $lines[] = '> File system actions:'; + $lines[] = ''; + $lines[] = 'Usage: php (path to FanPress CM/)fpcmcli.php files '; + $lines[] = ''; + $lines[] = ' Action params:'; + $lines[] = ''; + $lines[] = ' --check check for oudated files'; + $lines[] = ' --remove removed outdates files'; + return $lines; + } + + /** + * Escape exclude paths + * @param string $str + * @return string + */ + private function escapeExcludes(string $str) : string + { + $return = trim(escapeshellarg(trim($str)), "'"); + + if (str_starts_with($return, $this->base)) { + return $return; + } + + return $this->base . $return; + } + +} diff --git a/inc/model/cli/pkg.php b/inc/model/cli/pkg.php index 7c248884b..9521aa037 100644 --- a/inc/model/cli/pkg.php +++ b/inc/model/cli/pkg.php @@ -262,7 +262,7 @@ private function processUpgradeSystem() */ private function processUpgradedbSystem() { - $this->output('Update local database...'); + $this->output('Update is being finalized...'); $finalizer = new \fpcm\model\updater\finalizer(); $success = $finalizer->runUpdate(); @@ -272,7 +272,7 @@ private function processUpgradedbSystem() } if ($success !== true) { - $this->output('An error occurred during Database update. ERROR CODE: ' . $success, true); + $this->output('An error occurred during update completion. ERROR CODE: ' . $success, true); } $this->config->init(); diff --git a/inc/model/cli/progress.php b/inc/model/cli/progress.php index 1c1b9caab..4a1747719 100644 --- a/inc/model/cli/progress.php +++ b/inc/model/cli/progress.php @@ -110,7 +110,7 @@ public function setCurrentValue(int $currentValue) */ public function setOutputText(string $outputText) { - $this->outputText = sprintf("%-". self::LABEL_CHARS ."s: ", mb_substr($outputText, 0, self::LABEL_CHARS)); + $this->outputText = sprintf("%-". self::LABEL_CHARS ."s ", mb_substr($outputText, 0, self::LABEL_CHARS)); if ($this->recal) { return $this; diff --git a/inc/model/files/image.php b/inc/model/files/image.php index 4f178fb2d..e316885dc 100644 --- a/inc/model/files/image.php +++ b/inc/model/files/image.php @@ -17,7 +17,8 @@ */ class image extends \fpcm\model\abstracts\file -implements \fpcm\model\interfaces\validateFileType { +implements \fpcm\model\interfaces\validateFileType, + \fpcm\model\interfaces\isCopyable { /** * Erlaubte Dateitypen @@ -678,6 +679,50 @@ public function getPropertiesArray(string $userName) : array ]; } + /** + * + * @return int + */ + public function copy(): int + { + $cn = self::class; + + if (!$this->existsFolder()) { + return 0; + } + + /* @var $copy image */ + $copy = new $cn( $this->language->translate('GLOBAL_COPY_OF_FILE', [basename($this->filename)], true), false ); + $copy->addUploadFolder(); + + $subFolderbase = basename(dirname($copy->getFullpath())); + if ($this->config->file_subfolders && !str_starts_with($copy->getFilename(), $subFolderbase)) { + $copy->setFilename(basename(dirname($copy->getFullpath())) . DIRECTORY_SEPARATOR . $copy->getFilename()); + } + + $copy->setAltText($this->alttext); + $copy->setUserid(\fpcm\model\system\session::getInstance()->getUserId()); + $copy->setFiletime(time()); + + if ($copy->existsFolder()) { + return 0; + } + + if (!copy($this->fullpath, $copy->getFullpath())) { + return 0; + } + + if (!$copy->createThumbnail()) { + return 0; + } + + if (!(new imagelist())->createFilemanagerThumbs([$copy->getFullpath()])) { + return 0; + } + + return $copy->save() ?: 0; + } + /** * Get cropper filename string * @return string diff --git a/inc/model/interfaces/JsModuleFiles.php b/inc/model/interfaces/JsModuleFiles.php index 5389ba227..03b6bd565 100644 --- a/inc/model/interfaces/JsModuleFiles.php +++ b/inc/model/interfaces/JsModuleFiles.php @@ -9,14 +9,14 @@ /** * JavaScript ECMA module files interface - * + * * @package fpcm\model\interfaces * @author Stefan Seehafer aka imagine * @copyright (c) 2023, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ interface JsModuleFiles { - + /** * Returns list of JavaScript files * @return array diff --git a/inc/model/interfaces/cacheBackend.php b/inc/model/interfaces/cacheBackend.php index f8dd0bb00..17879483e 100644 --- a/inc/model/interfaces/cacheBackend.php +++ b/inc/model/interfaces/cacheBackend.php @@ -9,7 +9,7 @@ /** * Cache backend interface - * + * * @package fpcm\model\interfaces * @author Stefan Seehafer aka imagine * @copyright (c) 2022, Stefan Seehafer diff --git a/inc/model/interfaces/cron.php b/inc/model/interfaces/cron.php index 7941731b2..ed0416c1a 100644 --- a/inc/model/interfaces/cron.php +++ b/inc/model/interfaces/cron.php @@ -9,7 +9,7 @@ /** * Cronjob-Interface - * + * * @package fpcm\model\interfaces * @author Stefan Seehafer aka imagine * @copyright (c) 2011-2022, Stefan Seehafer diff --git a/inc/model/interfaces/dashcontainer.php b/inc/model/interfaces/dashcontainer.php index 5351e8ed8..a56d494d5 100644 --- a/inc/model/interfaces/dashcontainer.php +++ b/inc/model/interfaces/dashcontainer.php @@ -33,4 +33,5 @@ public function getContent(); * @return int */ public function getPosition(); + } diff --git a/inc/model/interfaces/dataset.php b/inc/model/interfaces/dataset.php index 6192676a7..77a326bab 100644 --- a/inc/model/interfaces/dataset.php +++ b/inc/model/interfaces/dataset.php @@ -9,7 +9,7 @@ /** * Model-Interface - * + * * @package fpcm\model\interfaces * @author Stefan Seehafer aka imagine * @copyright (c) 2011-2022, Stefan Seehafer @@ -52,4 +52,5 @@ public function update(); * @return bool */ public function delete(); + } diff --git a/inc/model/interfaces/gsearchIndex.php b/inc/model/interfaces/gsearchIndex.php index 9915bf452..fc1d1c71b 100644 --- a/inc/model/interfaces/gsearchIndex.php +++ b/inc/model/interfaces/gsearchIndex.php @@ -39,11 +39,12 @@ public function getElementLink(mixed $param): string; * @return \fpcm\view\helper\icon */ public function getElementIcon(): \fpcm\view\helper\icon; - + /** * Prepare result text * @param string $text * @return string */ public function prepareText(string $text): string; + } diff --git a/inc/model/interfaces/hasPersistence.php b/inc/model/interfaces/hasPersistence.php index b3e565c03..2234dc836 100644 --- a/inc/model/interfaces/hasPersistence.php +++ b/inc/model/interfaces/hasPersistence.php @@ -9,7 +9,7 @@ /** * Interface to store objects as persitent data - * + * * @package fpcm\model\interfaces * @author Stefan Seehafer * @copyright (c) 2022, Stefan Seehafer @@ -17,9 +17,9 @@ * @since 5.1-dev */ interface hasPersistence { - + /** - * Return data + * Return data * @return int|string */ public function getPersistentData(): int|string; diff --git a/inc/model/interfaces/isAccessible.php b/inc/model/interfaces/isAccessible.php index a3b74a33d..f374e8cdd 100644 --- a/inc/model/interfaces/isAccessible.php +++ b/inc/model/interfaces/isAccessible.php @@ -23,6 +23,5 @@ interface isAccessible { * @return bool */ public function isAccessible() : bool; -} -?> \ No newline at end of file +} diff --git a/inc/model/interfaces/isCopyable.php b/inc/model/interfaces/isCopyable.php new file mode 100644 index 000000000..342da1c76 --- /dev/null +++ b/inc/model/interfaces/isCopyable.php @@ -0,0 +1,27 @@ + + * @copyright (c) 2024, Stefan Seehafer + * @license http://www.gnu.org/licenses/gpl.txt GPLv3 + * @since 5.2.2-dev + */ +interface isCopyable { + + /** + * Creates copy of current object + * @return int + */ + public function copy() : int; + +} diff --git a/inc/model/interfaces/isCsvImportable.php b/inc/model/interfaces/isCsvImportable.php index 945da5cff..b5e115026 100644 --- a/inc/model/interfaces/isCsvImportable.php +++ b/inc/model/interfaces/isCsvImportable.php @@ -9,7 +9,7 @@ /** * CSV importtable interface - * + * * @package fpcm\model\interfaces * @author Stefan Seehafer * @copyright (c) 2020, Stefan Seehafer @@ -18,8 +18,17 @@ */ interface isCsvImportable { + /** + * Return list fo fields to be used to CSV import + * @return array + */ public function getFields() : array; + /** + * Assign field from csv row to internal fields + * @param array $csvRow + * @return bool + */ public function assignCsvRow(array $csvRow) : bool; - + } diff --git a/inc/model/interfaces/isObjectInstancable.php b/inc/model/interfaces/isObjectInstancable.php index 095543141..1abd57ec2 100644 --- a/inc/model/interfaces/isObjectInstancable.php +++ b/inc/model/interfaces/isObjectInstancable.php @@ -18,8 +18,10 @@ */ interface isObjectInstancable { + /** + * Returns object of current class, caches instance for future use + * @return object + */ public static function getInstance(); } - -?> \ No newline at end of file diff --git a/inc/model/interfaces/model.php b/inc/model/interfaces/model.php index 6e34f2a88..40f31b37d 100644 --- a/inc/model/interfaces/model.php +++ b/inc/model/interfaces/model.php @@ -9,13 +9,13 @@ /** * Model-Interface - * + * * @package fpcm\model\interfaces * @author Stefan Seehafer aka imagine * @copyright (c) 2011-2022, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ -interface model { +interface model { /** * Initialisiert Objekt diff --git a/inc/model/interfaces/validateFileType.php b/inc/model/interfaces/validateFileType.php index 98466c5b3..5484b9af4 100644 --- a/inc/model/interfaces/validateFileType.php +++ b/inc/model/interfaces/validateFileType.php @@ -25,5 +25,3 @@ interface validateFileType { public static function isValidType(string $ext, string $type, array $map = []) : bool; } - -?> \ No newline at end of file diff --git a/inc/model/interfaces/viewComponent.php b/inc/model/interfaces/viewComponent.php index dad6f51f8..550fdfa7b 100644 --- a/inc/model/interfaces/viewComponent.php +++ b/inc/model/interfaces/viewComponent.php @@ -9,7 +9,7 @@ /** * View component interface - * + * * @package fpcm\model\interfaces * @author Stefan Seehafer aka imagine * @copyright (c) 2024, Stefan Seehafer @@ -46,7 +46,7 @@ public function getJsLangVars() : array; * @return array */ public function getCssFiles() : array; - + /** * Returns list of JavaScript files * @return array @@ -58,4 +58,5 @@ public function getJsModuleFiles(): array; * @return array */ public function getViewVars() : array; + } diff --git a/inc/model/users/userRoll.php b/inc/model/users/userRoll.php index 46e461913..bfe240c31 100644 --- a/inc/model/users/userRoll.php +++ b/inc/model/users/userRoll.php @@ -12,10 +12,14 @@ * * @package fpcm\model\user * @author Stefan Seehafer aka imagine - * @copyright (c) 2011-2022, Stefan Seehafer + * @copyright (c) 2011-2024, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 */ -class userRoll extends \fpcm\model\abstracts\dataset { +class userRoll +extends + \fpcm\model\abstracts\dataset +implements + \fpcm\model\interfaces\isCopyable { use \fpcm\model\traits\eventModuleEmpty; @@ -233,4 +237,36 @@ protected function removeBannedTexts() return true; } + /** + * Creates copy of user roll + * @return int + * @since 5.2.2-dev + */ + public function copy(): int + { + $cn = self::class; + + $lt = $this->language->translate($this->leveltitle); + + /* @var $copy userRoll */ + $copy = new $cn(); + $copy->setRollName($this->language->translate('GLOBAL_COPY_OF', [$lt], true)); + $copy->setCodex($this->codex); + + if (!$copy->save()) { + return 0; + } + + $id = $copy->getId(); + + $permNew = new \fpcm\model\permissions\permissions($id); + $permNew->setPermissionData( (new \fpcm\model\permissions\permissions($this->id))->getPermissionData() ); + + if (!$permNew->update()) { + trigger_error(sprintf('Unable to copy permissions for %s, use default set instead.', $copy->getRollNameTranslated())); + } + + return $id; + } + } diff --git a/inc/model/wordban/item.php b/inc/model/wordban/item.php index 75f674f6f..36facf165 100644 --- a/inc/model/wordban/item.php +++ b/inc/model/wordban/item.php @@ -9,15 +9,19 @@ /** * Word Ban Item Object - * + * * @package fpcm\model\wordban * @author Stefan Seehafer aka imagine - * @copyright (c) 2011-2022, Stefan Seehafer + * @copyright (c) 2011-2024, Stefan Seehafer * @license http://www.gnu.org/licenses/gpl.txt GPLv3 * @since 3.2.0 */ -class item extends \fpcm\model\abstracts\dataset -implements \fpcm\model\interfaces\isCsvImportable { +class item +extends + \fpcm\model\abstracts\dataset +implements + \fpcm\model\interfaces\isCsvImportable, + \fpcm\model\interfaces\isCopyable { /** * gesuchter Text @@ -234,7 +238,7 @@ public function getFields(): array ]; } - + /** * Returns event base string * @see \fpcm\model\abstracts\dataset::getEventModule @@ -271,4 +275,24 @@ protected function afterUpdateInternal(): bool return true; } + /** + * Creates copy of text item + * @return int + * @since 5.2.2-dev + */ + public function copy(): int + { + $cn = self::class; + + /* @var $copy item */ + $copy = new $cn(); + $copy->setSearchtext($this->language->translate('GLOBAL_COPY_OF', [$this->searchtext], true)); + $copy->setReplacementtext($this->language->translate('GLOBAL_COPY_OF', [$this->replacementtext], true)); + $copy->setCommentApproval($this->commentapproval); + $copy->setLockArticle($this->lockarticle); + $copy->setReplaceTxt($this->replacetxt); + + return $copy->save() ? $copy->getId() : 0; + } + } diff --git a/inc/view/helper/button.php b/inc/view/helper/button.php index 00e02178f..4be183076 100644 --- a/inc/view/helper/button.php +++ b/inc/view/helper/button.php @@ -18,7 +18,8 @@ class button extends helper { use traits\iconHelper, - traits\typeHelper; + traits\typeHelper, + traits\setClickHelper; /* @since 4.4.0 */ const NAME_PREFIX = 'btn'; @@ -86,32 +87,25 @@ public function setPrimary(bool $primary = true) } /** - * Bind function to button click - * @param string $func - * @param type $args + * Override bs button type + * @param string $type * @return $this - * @since 5.0-dev + * @since 5.0.0-b3 */ - final public function setOnClick(string $func, $args = null) + public function overrideButtonType(string $type) { - if (!$func) { - return $this; - } - - $this->data['fn'] = $func; - $this->data['fn-arg'] = $args; + $this->class = preg_replace('/(btn-)(\w+\s{1})(.*)/i', '$1'.$type.' $3', $this->class); return $this; } - + /** - * Override bs button type - * @param string $rel + * Set data attribute for ui confirm dialog * @return $this - * @since 5.0.0-b3 + * @since 5.2.2-dev */ - public function overrideButtonType(string $type) + final public function setClickConfirm() { - $this->class = preg_replace('/(btn-)(\w+\s{1})(.*)/i', '$1'.$type.' $3', $this->class); + $this->data['ui-confirm'] = true; return $this; } diff --git a/inc/view/helper/copyButton.php b/inc/view/helper/copyButton.php new file mode 100644 index 000000000..c73e6c6c6 --- /dev/null +++ b/inc/view/helper/copyButton.php @@ -0,0 +1,56 @@ + + * @copyright (c) 2024, Stefan Seehafer + * @license http://www.gnu.org/licenses/gpl.txt GPLv3 + * @since 5.2.2-a1 + */ +final class copyButton extends button { + + /** + * Optional init function + * @return void + */ + protected function init() + { + parent::init(); + $this->class .= ' fpcm-ui-button-copy'; + $this->iconOnly = true; + $this->setText('GLOBAL_COPY'); + $this->setIcon('copy'); + } + + /** + * Set copy params + * @param \fpcm\model\abstracts\dataset $object + * @param string $type + * @return $this + */ + final public function setCopyParams($object, string $type) + { + if (!is_object($object)) { + trigger_error('Invalid parameter, $object must be an object'); + return $this; + } + + if (!method_exists($object, 'getId')) { + trigger_error('Invalid parameter, $object must provide a functiond "getId()"'); + return $this; + } + + $this->setOnClick('system.createCopy', sprintf("%s:%s", $type, $object->getId())); + return $this; + } + +} diff --git a/inc/view/helper/dropdown.php b/inc/view/helper/dropdown.php index 47c639c96..41e18b66e 100644 --- a/inc/view/helper/dropdown.php +++ b/inc/view/helper/dropdown.php @@ -49,6 +49,13 @@ traits\uiSizeHelper; */ protected $value = ''; + /** + * Dropdown button type + * @var string + * @since 5.2.2-dev + */ + protected $btnType = ''; + /** * Return element string * @return string @@ -64,6 +71,10 @@ protected function getString() $btn->setIcon($this->icon); } + if (trim($this->btnType)) { + $btn->overrideButtonType($this->btnType); + } + if (!$this->useWrapper) { return implode(' ', [ $btn, @@ -131,7 +142,18 @@ public function setDdType(string $ddType) return $this; } - + /** + * Override bs button type + * @param string $btnType + * @return $this + * @since 5.2.2-dev + */ + public function overrideButtonType(string $type) + { + $this->btnType = $type; + return $this; + } + /** * Create options string * @param array $options diff --git a/inc/view/helper/dropdownItem.php b/inc/view/helper/dropdownItem.php index 3cc278b52..74443c4cd 100644 --- a/inc/view/helper/dropdownItem.php +++ b/inc/view/helper/dropdownItem.php @@ -20,7 +20,8 @@ class dropdownItem extends helper { use traits\valueHelper, traits\iconHelper, - traits\urlHelper; + traits\urlHelper, + traits\setClickHelper; /** * Optional init function diff --git a/inc/view/helper/traits/setClickHelper.php b/inc/view/helper/traits/setClickHelper.php new file mode 100644 index 000000000..9b1d6d5cf --- /dev/null +++ b/inc/view/helper/traits/setClickHelper.php @@ -0,0 +1,39 @@ + + * @copyright (c) 2024, Stefan Seehafer + * @license http://www.gnu.org/licenses/gpl.txt GPLv3 + * @since 5.2.2 + */ +trait setClickHelper { + + /** + * Bind function to button click + * @param string $func + * @param type $args + * @return $this + * @since 5.0-dev + */ + final public function setOnClick(string $func, $args = null) + { + if (!$func) { + return $this; + } + + $this->data['fn'] = $func; + $this->data['fn-arg'] = $args; + return $this; + } + +} diff --git a/tests/testcases/articles/articleTest.php b/tests/testcases/articles/articleTest.php index 4e8072d68..a61f3921b 100644 --- a/tests/testcases/articles/articleTest.php +++ b/tests/testcases/articles/articleTest.php @@ -51,6 +51,25 @@ public function testUpdate() $this->assertTrue($result); } + public function testCopy() + { + + sleep(2); + + /* @var $object \fpcm\model\articles\article */ + $object = $this->object; + $res = $object->copy(); + + $this->assertGreaterThan(0, $res); + + $copy = new \fpcm\model\articles\article($res); + $this->assertTrue($copy->exists()); + $this->assertStringContainsString('Kopie von', $copy->getTitle()); + $this->assertTrue($copy->getContent() === $object->getContent()); + $this->assertGreaterThan($object->getCreatetime(), $copy->getCreatetime()); + $this->assertTrue($copy->delete()); + } + public function testGetArticle() { diff --git a/tests/testcases/categories/categoryTest.php b/tests/testcases/categories/categoryTest.php index 21bd15203..f4c1369e3 100644 --- a/tests/testcases/categories/categoryTest.php +++ b/tests/testcases/categories/categoryTest.php @@ -43,6 +43,25 @@ public function testUpdate() $this->assertTrue($result); } + public function testCopy() + { + + sleep(2); + + /* @var $object \fpcm\model\categories\category */ + $object = $this->object; + $res = $object->copy(); + + $this->assertGreaterThan(0, $res); + + $copy = new \fpcm\model\categories\category($res); + $this->assertTrue($copy->exists()); + $this->assertStringContainsString('Kopie von', $copy->getName()); + $this->assertTrue($copy->getIconPath() === $object->getIconPath()); + $this->assertTrue($copy->getGroups() === $object->getGroups()); + $this->assertTrue($copy->delete()); + } + public function testGetcategory() { diff --git a/tests/testcases/ips/ipListTest.php b/tests/testcases/ips/ipListTest.php index 85a136707..212269c64 100644 --- a/tests/testcases/ips/ipListTest.php +++ b/tests/testcases/ips/ipListTest.php @@ -17,7 +17,7 @@ public function testGetIpAll() $data = $this->object->getIpAll(); $count = count($data); if ($count == 0) { - $this->markTestSkipped('No wordband items available in db'); + $this->markTestSkipped('No ip items available in db'); } $this->assertTrue(is_array($data)); diff --git a/tests/testcases/users/userRollTest.php b/tests/testcases/users/userRollTest.php index f47cad315..5792efbcc 100644 --- a/tests/testcases/users/userRollTest.php +++ b/tests/testcases/users/userRollTest.php @@ -27,6 +27,32 @@ public function testSave() $GLOBALS['objectId'] = $object->getId(); } + public function testCopy() + { + + sleep(2); + + /* @var $object fpcm\model\users\userRoll */ + $object = $this->object; + $res = $object->copy(); + + $this->assertGreaterThan(0, $res); + + $copy = new fpcm\model\users\userRoll($res); + $this->assertTrue($copy->exists()); + $this->assertStringContainsString('Kopie von', $copy->getRollNameTranslated()); + $this->assertTrue($copy->getCodex() === $object->getCodex()); + + $permOld = new \fpcm\model\permissions\permissions($this->object->getId()); + $permCopy = new \fpcm\model\permissions\permissions($copy->getId()); + + $expected = hash('sha256', json_encode($permOld->getPermissionData())); + $actual = hash('sha256', json_encode($permCopy->getPermissionData())); + + $this->assertEquals($expected, $actual); + $this->assertTrue($copy->delete()); + } + public function testUpdate() { diff --git a/tests/testcases/wordban/textTest.php b/tests/testcases/wordban/textTest.php index 2fdfcba37..8cc01b09c 100644 --- a/tests/testcases/wordban/textTest.php +++ b/tests/testcases/wordban/textTest.php @@ -31,6 +31,27 @@ public function testSave() $GLOBALS['objectId'] = $object->getId(); } + public function testCopy() + { + + sleep(2); + + /* @var $object \fpcm\model\wordban\item */ + $object = $this->object; + $res = $object->copy(); + + $this->assertGreaterThan(0, $res); + + $copy = new \fpcm\model\wordban\item($res); + $this->assertTrue($copy->exists()); + $this->assertStringContainsString('Kopie von', $copy->getSearchtext()); + $this->assertStringContainsString('Kopie von', $copy->getReplacementtext()); + $this->assertTrue($copy->getReplaceTxt() === $object->getReplaceTxt()); + $this->assertTrue($copy->getLockArticle() === $object->getLockArticle()); + $this->assertTrue($copy->getCommentApproval() === $object->getCommentApproval()); + $this->assertTrue($copy->delete()); + } + public function testUpdate() { diff --git a/version.txt b/version.txt index 804440660..ca622104a 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -5.2.1 \ No newline at end of file +5.2.2-b1 \ No newline at end of file