diff --git a/.drone.yml b/.drone.yml index 9f197ff68a3..f37d0bfe6e4 100644 --- a/.drone.yml +++ b/.drone.yml @@ -269,6 +269,6 @@ steps: --- kind: signature -hmac: 71729a12ae3556e03f924ac95c8a5cd14bb6a3c2d20ef6768fdd1e853397fd22 +hmac: 53e23beca546bde246f812a726320210f33d231106cb154a65cc900ecbf7502e ... diff --git a/administrator/components/com_admin/sql/updates/mysql/4.1.0-2021-06-27.sql b/administrator/components/com_admin/sql/updates/mysql/4.1.0-2021-06-27.sql new file mode 100644 index 00000000000..20e5a05614c --- /dev/null +++ b/administrator/components/com_admin/sql/updates/mysql/4.1.0-2021-06-27.sql @@ -0,0 +1,2 @@ +INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `locked`, `manifest_cache`, `params`, `custom_data`, `checked_out`, `checked_out_time`, `ordering`, `state`) VALUES +(0, 'plg_system_addmodulebutton', 'plugin', 'addmodulebutton', 'system', 0, 1, 1, 0, 1, '', '', '', NULL, NULL, 0, 0); diff --git a/administrator/components/com_admin/sql/updates/postgresql/4.1.0-2021-06-27.sql b/administrator/components/com_admin/sql/updates/postgresql/4.1.0-2021-06-27.sql new file mode 100644 index 00000000000..20e5a05614c --- /dev/null +++ b/administrator/components/com_admin/sql/updates/postgresql/4.1.0-2021-06-27.sql @@ -0,0 +1,2 @@ +INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `locked`, `manifest_cache`, `params`, `custom_data`, `checked_out`, `checked_out_time`, `ordering`, `state`) VALUES +(0, 'plg_system_addmodulebutton', 'plugin', 'addmodulebutton', 'system', 0, 1, 1, 0, 1, '', '', '', NULL, NULL, 0, 0); diff --git a/administrator/components/com_modules/forms/preview_positions.xml b/administrator/components/com_modules/forms/preview_positions.xml new file mode 100644 index 00000000000..ac8df237613 --- /dev/null +++ b/administrator/components/com_modules/forms/preview_positions.xml @@ -0,0 +1,24 @@ + +
+
+ + + + + + + +
+
diff --git a/administrator/components/com_modules/layouts/joomla/form/field/modulespositionedit.php b/administrator/components/com_modules/layouts/joomla/form/field/modulespositionedit.php index 2af5c7d562a..11aba886386 100644 --- a/administrator/components/com_modules/layouts/joomla/form/field/modulespositionedit.php +++ b/administrator/components/com_modules/layouts/joomla/form/field/modulespositionedit.php @@ -76,13 +76,38 @@ ->usePreset('choicesjs') ->useScript('webcomponent.field-fancy-select'); -?> -> $id, - 'list.select' => $value, - 'list.attr' => implode(' ', $selectAttr), - ) - ); -?> +$app = Factory::getApplication(); +$clientId = $app->input->getBool('client_id', 0); + +$modalUrl = 'index.php?option=com_modules&view=module&layout=preview_positions&id=1&client_id=' . $clientId; + +// &tmpl=component doesn't redirect if the user isn't logged into backend hence we are adding it conditionally +$modalUrl = $app->isClient('site') ? 'administrator/' . $modalUrl : $modalUrl . '&tmpl=component'; +?> +> + $id, + 'list.select' => $value, + 'list.attr' => implode(' ', $selectAttr), + ) +); +?> + + Text::_('COM_MODULES_MODULE_SELECT_POSITION'), + 'url' => $modalUrl, + 'bodyHeight' => 70, + 'modalWidth' => 95, + 'footer' => '', + ) +); +?> + diff --git a/administrator/components/com_modules/src/Controller/ModuleController.php b/administrator/components/com_modules/src/Controller/ModuleController.php index 3a1d093ab77..eff78df5545 100644 --- a/administrator/components/com_modules/src/Controller/ModuleController.php +++ b/administrator/components/com_modules/src/Controller/ModuleController.php @@ -68,6 +68,38 @@ public function add() $app->setUserState('com_modules.add.module.params', $params); } + /** + * Set Position and Menu ID in State for the New Module in Frontend Module Placement. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function selectPosition() + { + $app = $this->app; + + // Get the Menu ID and Position. + $menuId = $this->input->getInt('menu', 0); + $position = $this->input->get('position'); + + if (empty($position)) + { + $redirectUrl = $this->input->server->getString('HTTP_REFERER'); + + $this->setRedirect(Route::_($redirectUrl, false)); + + $app->enqueueMessage(Text::_('COM_MODULES_ERROR_INVALID_POSITION'), 'warning'); + } + + $app->setUserState('com_modules.add.module.menu_id', $menuId); + $app->setUserState('com_modules.add.module.position', $position); + + // Select Position is only used in the Frontend Module Placement so we pass client_id as 0 + $redirectUrl = 'index.php?option=com_modules&view=select&client_id=0'; + $this->setRedirect(Route::_($redirectUrl, false)); + } + /** * Override parent cancel method to reset the add module state. * @@ -82,7 +114,9 @@ public function cancel($key = null) $result = parent::cancel(); $this->app->setUserState('com_modules.add.module.extension_id', null); + $this->app->setUserState('com_modules.add.module.menu_id', null); $this->app->setUserState('com_modules.add.module.params', null); + $this->app->setUserState('com_modules.add.module.position', null); if ($return = $this->input->get('return', '', 'BASE64')) { @@ -205,7 +239,9 @@ protected function postSaveHook(BaseDatabaseModel $model, $validData = array()) break; } + $this->app->setUserState('com_modules.add.module.menu_id', null); $this->app->setUserState('com_modules.add.module.params', null); + $this->app->setUserState('com_modules.add.module.position', null); } /** diff --git a/administrator/components/com_modules/src/Model/ModuleModel.php b/administrator/components/com_modules/src/Model/ModuleModel.php index f0634bbd374..603bf63a815 100644 --- a/administrator/components/com_modules/src/Model/ModuleModel.php +++ b/administrator/components/com_modules/src/Model/ModuleModel.php @@ -548,6 +548,8 @@ public function &getClient() */ public function getForm($data = array(), $loadData = true) { + $app = Factory::getApplication(); + // The folder and element vars are passed when saving the form. if (empty($data)) { @@ -572,19 +574,26 @@ public function getForm($data = array(), $loadData = true) $this->setState('item.module', $module); // Get the form. - if ($clientId == 1) + if ($app->input->getCMD('layout') == 'preview_positions') { - $form = $this->loadForm('com_modules.module.admin', 'moduleadmin', array('control' => 'jform', 'load_data' => $loadData), true); - - // Display language field to filter admin custom menus per language - if (!ModuleHelper::isAdminMultilang()) - { - $form->setFieldAttribute('language', 'type', 'hidden'); - } + $form = $this->loadForm('com_modules.preview_positions', 'preview_positions', array('control' => 'jform', 'load_data' => $loadData), true); } else { - $form = $this->loadForm('com_modules.module', 'module', array('control' => 'jform', 'load_data' => $loadData), true); + if ($clientId == 1) + { + $form = $this->loadForm('com_modules.module.admin', 'moduleadmin', array('control' => 'jform', 'load_data' => $loadData), true); + + // Display language field to filter admin custom menus per language + if (!ModuleHelper::isAdminMultilang()) + { + $form->setFieldAttribute('language', 'type', 'hidden'); + } + } + else + { + $form = $this->loadForm('com_modules.module', 'module', array('control' => 'jform', 'load_data' => $loadData), true); + } } if (empty($form)) @@ -642,7 +651,17 @@ protected function loadFormData() $clientId = $app->input->getInt('client_id', 0); $filters = (array) $app->getUserState('com_modules.modules.' . $clientId . '.filter'); $data->set('published', $app->input->getInt('published', ((isset($filters['state']) && $filters['state'] !== '') ? $filters['state'] : null))); - $data->set('position', $app->input->getInt('position', (!empty($filters['position']) ? $filters['position'] : null))); + + // Pre-select Module Position set by Frontend Placement if it exists. + if ($position = $app->getUserState('com_modules.add.module.position')) + { + $data->set('position', $position); + } + else + { + $data->set('position', $app->input->getInt('position', (!empty($filters['position']) ? $filters['position'] : null))); + } + $data->set('language', $app->input->getString('language', (!empty($filters['language']) ? $filters['language'] : null))); $data->set('access', $app->input->getInt('access', (!empty($filters['access']) ? $filters['access'] : $app->get('access')))); } @@ -676,6 +695,7 @@ protected function loadFormData() */ public function getItem($pk = null) { + $app = Factory::getApplication(); $pk = (!empty($pk)) ? (int) $pk : (int) $this->getState('module.id'); $db = $this->getDbo(); @@ -757,8 +777,17 @@ public function getItem($pk = null) if (empty($pk)) { - // If this is a new module, assign to all pages. - $assignment = 0; + if ($menuId = (int) $app->getUserState('com_modules.add.module.menu_id')) + { + // If a Menu ID is selected via Frontend Placements then use that. + $assignment = 1; + $assigned[] = $menuId; + } + else + { + // If this is a new module, assign to all pages. + $assignment = 0; + } } elseif (empty($assigned)) { diff --git a/administrator/components/com_modules/tmpl/module/preview_positions.php b/administrator/components/com_modules/tmpl/module/preview_positions.php new file mode 100644 index 00000000000..42e92c48625 --- /dev/null +++ b/administrator/components/com_modules/tmpl/module/preview_positions.php @@ -0,0 +1,42 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Uri\Uri; + +/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */ +$wa = $this->document->getWebAssetManager(); +$wa->useScript('com_modules.admin-modules-preview_positions'); + +$isAdmin = Factory::getApplication()->input->get('client_id'); + +// Get the URL of the iframe that displays template preview. +$iframeBaseURL = Uri::root(); +$iframeBaseURL .= $isAdmin ? 'administrator' : ''; + +// Conditionally render the admin or site template select field. +$templateField = 'template_style_'; +$templateField .= $isAdmin ? 'admin' : 'site'; + +// Render the template select field. +$this->fieldsets = $this->form->getFieldsets(); +echo $this->form->renderField($templateField); +?> +
+ +
diff --git a/administrator/components/com_modules/tmpl/modules/default.php b/administrator/components/com_modules/tmpl/modules/default.php index bd95ae2d29c..00cd011b19c 100644 --- a/administrator/components/com_modules/tmpl/modules/default.php +++ b/administrator/components/com_modules/tmpl/modules/default.php @@ -132,7 +132,7 @@ editor, $item->checked_out_time, 'modules.', $canCheckin); ?> - + escape($item->title); ?> escape($item->title); ?> diff --git a/administrator/language/en-GB/com_modules.ini b/administrator/language/en-GB/com_modules.ini index cfb0cc3c87b..7aa298dc6a0 100644 --- a/administrator/language/en-GB/com_modules.ini +++ b/administrator/language/en-GB/com_modules.ini @@ -32,6 +32,7 @@ COM_MODULES_EMPTYSTATE_TITLE_SITE="No Site Modules have been created yet." COM_MODULES_ERR_XML="Module XML data not available" COM_MODULES_ERROR_CANNOT_FIND_MODULE="Can't find module" COM_MODULES_ERROR_INVALID_EXTENSION="Invalid module" +COM_MODULES_ERROR_INVALID_POSITION="Invalid position" COM_MODULES_ERROR_NO_MODULES_SELECTED="No module selected." COM_MODULES_EXPAND="Expand" COM_MODULES_EXTENSION_PUBLISHED_DISABLED="Module disabled and published." @@ -90,7 +91,10 @@ COM_MODULES_MENU_ITEM_URL="URL" COM_MODULES_MODULE="Module" COM_MODULES_MODULE_ASSIGN="Module Assignment" COM_MODULES_MODULE_DESCRIPTION="Module Description" +COM_MODULES_MODULE_SELECT_POSITION="Select Module Position from Template Preview" +COM_MODULES_MODULE_SELECT_POSITION_BUTTON="Select from Template Preview" COM_MODULES_MODULE_TEMPLATE_POSITION="%1$s (%2$s)" +COM_MODULES_MODULE_TEMPLATE_SELECT="Select Template to Preview" COM_MODULES_MODULES="Modules" COM_MODULES_MODULES_FILTER_SEARCH_DESC="Search in module title and note. Prefix with ID: to search for a module ID." COM_MODULES_MODULES_FILTER_SEARCH_LABEL="Search Modules" diff --git a/administrator/language/en-GB/joomla.ini b/administrator/language/en-GB/joomla.ini index f52986c131e..2fe2bc88b0a 100644 --- a/administrator/language/en-GB/joomla.ini +++ b/administrator/language/en-GB/joomla.ini @@ -556,6 +556,8 @@ JGLOBAL_PASSWORD="Password" JGLOBAL_PASSWORD_RESET_REQUIRED="You are required to reset your password before proceeding." JGLOBAL_PERMISSIONS_ANCHOR="Set Permissions" JGLOBAL_PREVIEW="Preview" +JGLOBAL_PREVIEW_PLACE_MODULE="Place Module Here" +JGLOBAL_PREVIEW_PLACE_MODULE_POSITION="in Position: %s" JGLOBAL_PREVIEW_POSITION="Position: %s" JGLOBAL_PREVIEW_STYLE="Style: %s" JGLOBAL_PUBLISHED_DATE="Published Date" diff --git a/administrator/language/en-GB/plg_system_addmodulebutton.ini b/administrator/language/en-GB/plg_system_addmodulebutton.ini new file mode 100644 index 00000000000..adabe9720ca --- /dev/null +++ b/administrator/language/en-GB/plg_system_addmodulebutton.ini @@ -0,0 +1,10 @@ +; Joomla! Project +; (C) 2021 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +PLG_SYSTEM_ADDMODULEBUTTON="System - Add Module Button" +PLG_SYSTEM_ADD_MODULE_BUTTON_LABEL="Add Module to this Menu" +PLG_ADD_MODULE_BUTTON_XML_DESCRIPTION="Displays a button to insert a module in a menu directly from the Frontend." +PLG_SYSTEM_ADD_MODULE_BUTTON_CREATE_MODULE_PERMISSIONS_WARNING="Please login with an account having permission to Select Module Position" +PLG_SYSTEM_ADD_MODULE_BUTTON_EDIT_MODULE_PERMISSIONS_WARNING="Please login with an account having permission to Edit Module Position" diff --git a/administrator/language/en-GB/plg_system_addmodulebutton.sys.ini b/administrator/language/en-GB/plg_system_addmodulebutton.sys.ini new file mode 100644 index 00000000000..127eff4b24d --- /dev/null +++ b/administrator/language/en-GB/plg_system_addmodulebutton.sys.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; (C) 2021 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +PLG_SYSTEM_ADDMODULEBUTTON="System - Add Module Button" +PLG_ADD_MODULE_BUTTON_XML_DESCRIPTION="Displays a button to insert a module in a menu directly from the Frontend." diff --git a/api/language/en-GB/joomla.ini b/api/language/en-GB/joomla.ini index d77a4d87690..919884ad1c6 100644 --- a/api/language/en-GB/joomla.ini +++ b/api/language/en-GB/joomla.ini @@ -552,6 +552,8 @@ JGLOBAL_PASSWORD="Password" JGLOBAL_PASSWORD_RESET_REQUIRED="You are required to reset your password before proceeding." JGLOBAL_PERMISSIONS_ANCHOR="Set Permissions" JGLOBAL_PREVIEW="Preview" +JGLOBAL_PREVIEW_PLACE_MODULE="Place Module Here" +JGLOBAL_PREVIEW_PLACE_MODULE_POSITION="in Position: %s" JGLOBAL_PREVIEW_POSITION="Position: %s" JGLOBAL_PREVIEW_STYLE="Style: %s" JGLOBAL_PUBLISHED_DATE="Published Date" diff --git a/build/media_source/com_modules/joomla.asset.json b/build/media_source/com_modules/joomla.asset.json index 8797814a122..fceb5d6cc0c 100644 --- a/build/media_source/com_modules/joomla.asset.json +++ b/build/media_source/com_modules/joomla.asset.json @@ -97,6 +97,29 @@ "type": "module" } }, + { + "name": "com_modules.admin-modules-preview_positions.es5", + "type": "script", + "uri": "com_modules/admin-modules-preview_positions-es5.min.js", + "dependencies": [ + "core" + ], + "attributes": { + "nomodule": true, + "defer": true + } + }, + { + "name": "com_modules.admin-modules-preview_positions", + "type": "script", + "uri": "com_modules/admin-modules-preview_positions.min.js", + "dependencies": [ + "com_modules.admin-modules-preview_positions.es5" + ], + "attributes": { + "type": "module" + } + }, { "name": "com_modules.admin-select-modal.es5", "type": "script", diff --git a/build/media_source/com_modules/js/admin-modules-preview_positions.es6.js b/build/media_source/com_modules/js/admin-modules-preview_positions.es6.js new file mode 100644 index 00000000000..a5bb4debdbe --- /dev/null +++ b/build/media_source/com_modules/js/admin-modules-preview_positions.es6.js @@ -0,0 +1,21 @@ +/** + * @copyright (C) 2021 Open Source Matters, Inc. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +/** + * Add a keyboard event listener to the Select Template Style element. + * + * This script is meant to be loaded deferred. This means that it's non-blocking + * (the browser can load it whenever) and it doesn't need an on DOMContentLoaded event handler + * because the browser is guaranteed to execute it only after the DOM content has loaded, the + * whole point of it being deferred. + * + */ + +const elIframe = document.getElementById('module-position-select'); +const elTemplateSelect = document.getElementById('jform_template_style_select'); + +elTemplateSelect.addEventListener('change', (event) => { + elIframe.src = elIframe.src.substring(0, elIframe.src.indexOf('templateStyle=') + 14) + event.target.value; +}); diff --git a/build/media_source/layouts/js/chromes/outline.es6.js b/build/media_source/layouts/js/chromes/outline.es6.js new file mode 100644 index 00000000000..3e44523b444 --- /dev/null +++ b/build/media_source/layouts/js/chromes/outline.es6.js @@ -0,0 +1,40 @@ +/** + * @copyright (C) 2021 Open Source Matters, Inc. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +(() => { + 'use strict'; + + /** + * Javascript to insert the onclick event + * We get the position associated to the button that has been clicked + * and we use it to set the position of joomla field fancy select element + * of the parent window. + */ + + document.addEventListener('DOMContentLoaded', () => { + // Get the elements + const elements = document.querySelectorAll('.jmod-position-select'); + + for (let i = 0, l = elements.length; l > i; i += 1) { + // Listen for click event + elements[i].addEventListener('click', (event) => { + const position = event.target.getAttribute('data-position'); + + // Select value of the choices field by triggering mousedown event on the position option + const elPositionSelect = window.parent.parent.document.querySelector('#jform_position'); + const elPositionOptions = elPositionSelect.closest('.choices').querySelectorAll('.choices__item--selectable'); + for (let j = 0; j < elPositionOptions.length; j += 1) { + if (elPositionOptions[j].dataset.value === position) { + elPositionOptions[j].dispatchEvent(new Event('mousedown')); + } + } + + if (window.parent.parent.Joomla.Modal) { + window.parent.parent.Joomla.Modal.getCurrent().close(); + } + }); + } + }); +})(); diff --git a/build/media_source/plg_system_addmodulebutton/js/addmodulebutton.es6.js b/build/media_source/plg_system_addmodulebutton/js/addmodulebutton.es6.js new file mode 100644 index 00000000000..6ee7a748592 --- /dev/null +++ b/build/media_source/plg_system_addmodulebutton/js/addmodulebutton.es6.js @@ -0,0 +1,32 @@ +/** + * @copyright (C) 2021 Open Source Matters, Inc. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ +((document) => { + 'use strict'; + + document.addEventListener('DOMContentLoaded', () => { + const addModuleBtnLabel = Joomla.getOptions('js-addModuleBtn'); + const elMain = document.querySelector('main'); + + // Create the form node + const elForm = document.createElement('form'); + elForm.setAttribute('method', 'get'); + + // Hidden input field to pass the get param for place module view + const placeModuleFlag = document.createElement('input'); + placeModuleFlag.setAttribute('type', 'hidden'); + placeModuleFlag.setAttribute('name', 'pm'); + placeModuleFlag.setAttribute('value', '1'); + elForm.appendChild(placeModuleFlag); + + const addModuleBtn = document.createElement('button'); + addModuleBtn.setAttribute('type', 'submit'); + addModuleBtn.classList.add('btn', 'jmodadd'); + addModuleBtn.innerText = addModuleBtnLabel; + elForm.appendChild(addModuleBtn); + + // Append the form + elMain.appendChild(elForm); + }); +})(document); diff --git a/installation/sql/mysql/base.sql b/installation/sql/mysql/base.sql index 99199575edf..3f14624af1f 100644 --- a/installation/sql/mysql/base.sql +++ b/installation/sql/mysql/base.sql @@ -321,6 +321,7 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, (0, 'plg_sampledata_multilang', 'plugin', 'multilang', 'sampledata', 0, 1, 1, 0, 1, '', '', '', 2, 0), (0, 'plg_system_accessibility', 'plugin', 'accessibility', 'system', 0, 0, 1, 0, 1, '', '{}', '', 1, 0), (0, 'plg_system_actionlogs', 'plugin', 'actionlogs', 'system', 0, 1, 1, 0, 1, '', '{}', '', 2, 0), +(0, 'plg_system_addmodulebutton', 'plugin', 'addmodulebutton', 'system', 0, 1, 1, 0, 1, '', '{}', '', 1, 0), (0, 'plg_system_cache', 'plugin', 'cache', 'system', 0, 0, 1, 0, 1, '', '{"browsercache":"0","cachetime":"15"}', '', 3, 0), (0, 'plg_system_debug', 'plugin', 'debug', 'system', 0, 1, 1, 0, 1, '', '{"profile":"1","queries":"1","memory":"1","language_files":"1","language_strings":"1","strip-first":"1","strip-prefix":"","strip-suffix":""}', '', 4, 0), (0, 'plg_system_fields', 'plugin', 'fields', 'system', 0, 1, 1, 0, 1, '', '', '', 5, 0), diff --git a/installation/sql/postgresql/base.sql b/installation/sql/postgresql/base.sql index 8cda51c8fee..81848f38e74 100644 --- a/installation/sql/postgresql/base.sql +++ b/installation/sql/postgresql/base.sql @@ -327,6 +327,7 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", (0, 'plg_sampledata_multilang', 'plugin', 'multilang', 'sampledata', 0, 1, 1, 0, 1, '', '', '', 2, 0), (0, 'plg_system_accessibility', 'plugin', 'accessibility', 'system', 0, 0, 1, 0, 1, '', '{}', '', 1, 0), (0, 'plg_system_actionlogs', 'plugin', 'actionlogs', 'system', 0, 1, 1, 0, 1, '', '{}', '', 2, 0), +(0, 'plg_system_addmodulebutton', 'plugin', 'addmodulebutton', 'system', 0, 1, 1, 0, 1, '', '{}', '', 1, 0), (0, 'plg_system_cache', 'plugin', 'cache', 'system', 0, 0, 1, 0, 1, '', '{"browsercache":"0","cachetime":"15"}', '', 3, 0), (0, 'plg_system_debug', 'plugin', 'debug', 'system', 0, 1, 1, 0, 1, '', '{"profile":"1","queries":"1","memory":"1","language_files":"1","language_strings":"1","strip-first":"1","strip-prefix":"","strip-suffix":""}', '', 4, 0), (0, 'plg_system_fields', 'plugin', 'fields', 'system', 0, 1, 1, 0, 1, '', '', '', 5, 0), diff --git a/language/en-GB/joomla.ini b/language/en-GB/joomla.ini index 7a6ffb736e9..03dece34e4d 100644 --- a/language/en-GB/joomla.ini +++ b/language/en-GB/joomla.ini @@ -323,6 +323,8 @@ JGLOBAL_NUM="#" JGLOBAL_OTPMETHOD_NONE="Disable Two Factor Authentication" JGLOBAL_PASSWORD="Password" JGLOBAL_PASSWORD_RESET_REQUIRED="You are required to reset your password before proceeding." +JGLOBAL_PREVIEW_PLACE_MODULE="Place Module Here" +JGLOBAL_PREVIEW_PLACE_MODULE_POSITION="in Position: %s" JGLOBAL_PREVIEW_POSITION="Position: %s" JGLOBAL_PREVIEW_STYLE="Style: %s" JGLOBAL_PRINT="Print" diff --git a/layouts/chromes/outline.php b/layouts/chromes/outline.php index 1312c50ffc7..1367adfca49 100644 --- a/layouts/chromes/outline.php +++ b/layouts/chromes/outline.php @@ -12,21 +12,48 @@ use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; -Factory::getApplication()->getDocument() +$app = Factory::getApplication(); +$app->getDocument() ->getWebAssetManager() - ->registerAndUseStyle('layouts.chromes.outline', 'layouts/chromes/outline.css'); + ->registerAndUseStyle('layouts.chromes.outline', 'layouts/chromes/outline.css') + ->registerAndUseScript('layouts.chromes.outline', 'layouts/chromes/outline.js'); $module = $displayData['module']; +// Place Modules Button +$showPlaceModuleButton = $app->input->getBool('pm') && ($app->getName() == 'site'); + +// Attributes of Select Position Tag for Placing Modules +$menuId = $app->getMenu()->getActive(); +$action = isset($menuId->id) ? 'href="administrator/index.php?option=com_modules&task=module.selectPosition&position=' . $module->position . "&menu=" . $menuId->id . '"' : ''; +$tag = "a"; + +// True for Backend Edit Module Position's Modal Iframe +if ($showPlaceModuleButton && $app->input->getBool('edit')) +{ + $tag = "button"; + $action = 'data-position="' . $module->position . '"'; +} ?>
position); ?>
-
- style); ?> -
+ +
+ < class="btn btn-sm btn-info jmod-position-select" > + + + position); ?> + + > +
+ +
+ style); ?> +
+
content; ?> diff --git a/libraries/src/Document/Renderer/Html/ModulesRenderer.php b/libraries/src/Document/Renderer/Html/ModulesRenderer.php index 70629c347e1..90f277bc080 100644 --- a/libraries/src/Document/Renderer/Html/ModulesRenderer.php +++ b/libraries/src/Document/Renderer/Html/ModulesRenderer.php @@ -40,7 +40,7 @@ public function render($position, $params = array(), $content = null) $app = Factory::getApplication(); $user = Factory::getUser(); - $frontediting = ($app->isClient('site') && $app->get('frontediting', 1) && !$user->guest); + $frontediting = ($app->isClient('site') && $app->get('frontediting', 1) && !$user->guest && !$app->input->get('pm')); $menusEditing = ($app->get('frontediting', 1) == 2) && $user->authorise('core.edit', 'com_menus'); foreach (ModuleHelper::getModules($position) as $mod) diff --git a/libraries/src/Helper/ModuleHelper.php b/libraries/src/Helper/ModuleHelper.php index b955e276469..1efad8f7bae 100644 --- a/libraries/src/Helper/ModuleHelper.php +++ b/libraries/src/Helper/ModuleHelper.php @@ -15,6 +15,7 @@ use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; +use Joomla\CMS\Helper\ContentHelper; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\LayoutHelper; @@ -100,7 +101,7 @@ public static function &getModules($position) if (\count($result) === 0) { - if ($input->getBool('tp') && ComponentHelper::getParams('com_templates')->get('template_positions_display')) + if ($input->getBool('pm') || ($input->getBool('tp') && ComponentHelper::getParams('com_templates')->get('template_positions_display'))) { $result[0] = static::createDummyModule(); $result[0]->title = $position; @@ -208,7 +209,7 @@ public static function renderModule($module, $attribs = array()) } // Dynamically add outline style - if ($app->input->getBool('tp') && ComponentHelper::getParams('com_templates')->get('template_positions_display')) + if (($app->input->getBool('tp') && ComponentHelper::getParams('com_templates')->get('template_positions_display')) || ($app->input->getBool('pm') && ContentHelper::getActions('com_modules')->get('core.create'))) { $attribs['style'] .= ' outline'; } diff --git a/plugins/system/addmodulebutton/addmodulebutton.php b/plugins/system/addmodulebutton/addmodulebutton.php new file mode 100644 index 00000000000..eeb3e1c942e --- /dev/null +++ b/plugins/system/addmodulebutton/addmodulebutton.php @@ -0,0 +1,88 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + * @since __DEPLOY_VERSION__ + */ + +defined('_JEXEC') or die; +use Joomla\CMS\Helper\ContentHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\CMS\WebAsset\WebAssetManager; +/** + * Displays the Add Module button for Frontend Placement. + * + * @since __DEPLOY_VERSION__ + */ +class PlgSystemAddModuleButton extends CMSPlugin +{ + /** + * Load plugin language files automatically + * + * @var boolean + * @since 3.9.0 + */ + protected $autoloadLanguage = true; + + /** + * Application object + * + * @var \Joomla\CMS\Application\CMSApplication + * @since 3.2 + */ + protected $app; + + /** + * Listener for the `onBeforeRender` event + * + * @return void + * + * @since 1.0 + */ + public function onBeforeRender() + { + + if ($this->app->isClient('administrator')) + { + return; + } + + // Frontend Module Placement Variables + $canCreateModules = ContentHelper::getActions('com_modules')->get('core.create'); + $canEditModules = ContentHelper::getActions('com_modules')->get('core.edit'); + $placeModules = $this->app->input->getBool('pm'); + $editPosition = $this->app->input->getBool('edit'); + $showAddModuleBtn = $canCreateModules && !$this->app->input->getBool('tp') && !$placeModules; + + // Display Warning message when user is not logged in or does not have permissions + if ($placeModules) + { + if ($editPosition && !$canEditModules) + { + $this->app->enqueueMessage(Text::sprintf('PLG_SYSTEM_ADD_MODULE_BUTTON_EDIT_MODULE_PERMISSIONS_WARNING'), 'warning'); + + return; + } + elseif (!$canCreateModules) + { + $this->app->enqueueMessage(Text::sprintf('PLG_SYSTEM_ADD_MODULE_BUTTON_CREATE_MODULE_PERMISSIONS_WARNING'), 'warning'); + + return; + } + } + + // Display the Add Module Button + if ($showAddModuleBtn) + { + // Add Script Options to pass the Button label Language Constant + $this->app->getDocument()->addScriptOptions('js-addModuleBtn', Text::_('PLG_SYSTEM_ADD_MODULE_BUTTON_LABEL')); + + // Script for appending the Add Module Button + $this->app->getDocument()->getWebAssetManager() + ->registerAndUseScript('plg_system_addmodulebutton_js', 'media/plg_system_addmodulebutton/js/addmodulebutton.js', [], ['defer' => true]); + } + } +} diff --git a/plugins/system/addmodulebutton/addmodulebutton.xml b/plugins/system/addmodulebutton/addmodulebutton.xml new file mode 100644 index 00000000000..e0a44a2c51f --- /dev/null +++ b/plugins/system/addmodulebutton/addmodulebutton.xml @@ -0,0 +1,22 @@ + + + plg_system_addmodulebutton + Joomla! Project + June 2021 + (C) 2021 Open Source Matters, Inc. + GNU General Public License version 2 or later; see LICENSE.txt + admin@joomla.org + www.joomla.org + __DEPLOY_VERSION__ + PLG_ADD_MODULE_BUTTON_XML_DESCRIPTION + + addmodulebutton.php + + + language/en-GB/en-GB.plg_system_addmodulebutton.ini + language/en-GB/en-GB.plg_system_addmodulebutton.sys.ini + + + js + + diff --git a/templates/cassiopeia/index.php b/templates/cassiopeia/index.php index 517fc465b0e..43eae6fb284 100644 --- a/templates/cassiopeia/index.php +++ b/templates/cassiopeia/index.php @@ -10,6 +10,7 @@ defined('_JEXEC') or die; use Joomla\CMS\Factory; +use Joomla\CMS\Helper\ContentHelper; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; @@ -34,6 +35,10 @@ $menu = $app->getMenu()->getActive(); $pageclass = $menu !== null ? $menu->getParams()->get('pageclass_sfx', '') : ''; +// Display inactive positions for Place Module View +$modulesPermissions = ContentHelper::getActions('com_modules'); +$showAllPositions = $app->input->getBool('pm') && ($modulesPermissions->get('core.edit') || $modulesPermissions->get('core.create')); + // Template path $templatePath = 'templates/' . $this->template; @@ -79,12 +84,12 @@ $hasClass = ''; -if ($this->countModules('sidebar-left', true)) +if ($this->countModules('sidebar-left', true) || $showAllPositions) { $hasClass .= ' has-sidebar-left'; } -if ($this->countModules('sidebar-right', true)) +if ($this->countModules('sidebar-right', true) || $showAllPositions) { $hasClass .= ' has-sidebar-right'; } @@ -119,19 +124,19 @@ ?>">
- countModules('topbar')) : ?> + countModules('topbar') || $showAllPositions) : ?>
- countModules('below-top')) : ?> + countModules('below-top') || $showAllPositions) : ?>
- params->get('brand', 1)) : ?> + params->get('brand', 1) || $showAllPositions) : ?>