Feature RTC PR-5.1-dev Pending

User tests: Successful: Unsuccessful:

avatar dgrammatiko
dgrammatiko
18 Aug 2023

Pull Request for Issue #NULL .

Summary of Changes

Add a way to have the templateDetails.xml drive the entirety (basically the menu_assignment tab) of the style view.

Why?

So, right now the style edit layout has a hardcoded sub layout for the users to select the menus that the template style should be applied. If a developer/template author needs to roll their own menu selecting logic/UI/UX they have to create a plugin, observe the option/view, etc and override the layout. This is way too painful and limiting. Thus this PR, building on the fundamentals of Joomla (basically form fields) allows a way simpler way for devs to roll their own UI/UX (obviously as this is based on form fields it will require AJAX functionality but then again who's not building AJAX first UI in 2023?)

Testing Instructions

  • Apply this PR (should also work with patch testser, if patch tester is working on J5)
  • Visit administrator/index.php?option=com_templates&view=styles
  • Click on Cassiopeia - Default link (or any other style-template in the list)
  • Check the Last tab: Menu Assignment
  • Edit the Cassiopeia templateDetails.xml and after the last </fields> (line 221) add:
<fields name="assigned">
  <fieldset name="assigned" addfieldprefix="Joomla\Component\Templates\Administrator\Field">
    <field name="assigned" type="menus" />
  </fieldset>
</fields>

Also create a file administrator/components/com_templates/src/Field/MenusField.php with the following contents

content
<?php
namespace Joomla\Component\Templates\Administrator\Field;

use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\TextField;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\Component\Menus\Administrator\Helper\MenusHelper;


// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects


class MenusField extends TextField
{
    protected $type = 'Menus';

    public function setup(\SimpleXMLElement $element, $value, $group = null)
    {
        // Make sure there is a valid FormField XML element.
        if ((string) $element->getName() !== 'field'){
            return false;
        }

        // Reset the input and label values.
        $this->input = null;
        $this->label = null;

        // Set the XML element object.
        $this->element = $element;

        // Set the group of the field.
        $this->group = $group;

        $attributes = [
        'multiple', 'name', 'id', 'hint', 'class', 'description', 'labelclass', 'onchange', 'onclick', 'validate', 'pattern', 'validationtext',
        'default', 'required', 'disabled', 'readonly', 'autofocus', 'hidden', 'autocomplete', 'spellcheck', 'translateHint', 'translateLabel',
        'translate_label', 'translateDescription', 'translate_description', 'size', 'showon'];

        $this->default = isset($element['value']) ? (string) $element['value'] : $this->default;

        // Set the field default value.
        $this->value = $value;

        // Lets detect miscellaneous data attribute. For eg, data-*
        foreach ($this->element->attributes() as $key => $value) {
            if (strpos($key, 'data-') === 0) {
                // Data attribute key value pair
                $this->dataAttributes[$key] = $value;
            }
        }

        foreach ($attributes as $attributeName) {
            $this->__set($attributeName, $element[$attributeName]);
        }

        // Allow for repeatable elements
        // $repeat = (string) $element['repeat'];
        // $this->repeat = ($repeat === 'true' || $repeat === 'multiple' || (!empty($this->form->repeat) && $this->form->repeat == 1));

        // Set the visibility.
        $this->hidden = ($this->hidden || strtolower((string) $this->element['type']) === 'hidden');

        $this->parentclass = isset($this->element['parentclass']) ? (string) $this->element['parentclass'] : $this->parentclass;

        // Add required to class list if field is required.
        if ($this->required) {
            $this->class = trim($this->class . ' required');
        }

        // Hide the label
        $this->hiddenLabel = true;
        $this->hidden = true;

        return true;
    }

    public function getInput() {
        // Initialise related data.
        $menuTypes = MenusHelper::getMenuLinks();
        $user      = $this->getCurrentUser();
        $app       = Factory::getApplication();
        $currentId = $app->input->getInt('id');

        /** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
        $wa = Factory::getApplication()->getDocument()->getWebAssetManager();
        $wa->useScript('com_templates.admin-template-toggle-assignment');

        $html = '<label id="jform_menuselect-lbl" for="jform_menuselect">'. Text::_('JGLOBAL_MENU_SELECTION') . '</label>'
        .  '<div class="btn-toolbar">'
        .    '<button class="btn btn-sm btn-secondary jform-rightbtn" type="button" onclick="Joomla.toggleAll()">'
        .    '<span class="icon-square" aria-hidden="true"></span> ' . Text::_('JGLOBAL_SELECTION_INVERT_ALL') . '</button>'
        .  '</div>'
        .  '<div id="menu-assignment" class="menu-assignment">'
        .    '<ul class="menu-links">';

        foreach ($menuTypes as &$type) {
            $html .= '<li>'
            . '<div class="menu-links-block">'
            .   '<button class="btn btn-sm btn-secondary jform-rightbtn mb-2" type="button" onclick=\'Joomla.toggleMenutype("' . $type->menutype . '")\'>'
            .   '<span class="icon-square" aria-hidden="true"></span> ' . Text::_('JGLOBAL_SELECTION_INVERT') . '</button>'
            .   '<h5>' . $type->title ?: $type->menutype . '</h5>';

            foreach ($type->links as $link) {
                $html .= '<label class="checkbox small" for="link' . (int) $link->value . '" >'
                . '<input type="checkbox" name="jform[assigned][]" value="'
                . (int) $link->value . '" id="link' . (int) $link->value . '"'
                . (($link->template_style_id == $currentId) ? ' checked="checked"' : '')
                . (($link->checked_out && $link->checked_out != $user->id) ? ' disabled="disabled"' : ' class="form-check-input chk-menulink menutype-' . $type->menutype . '"')
                . '/>';

                $html .= LayoutHelper::render('joomla.html.treeprefix', ['level' => $link->level]) . $link->text;
                $html .='</label>';
            }

            $html .= '</div></li>';
        }

        $html .= '</ul></div>';

        return $html;
    }
}
  • Revisit administrator/index.php?option=com_templates&view=styles
  • Click on Cassiopeia - Default link
  • Check the Last tab: Menu Assignment. Now you should have the same menus as before
  • That's that, you override the menu assignment using a custom field

Actual result BEFORE applying this Pull Request

Too much code needed for such an easy task and too complicated for template authors

Expected result AFTER applying this Pull Request

Link to documentations

Please select:

  • Documentation link for docs.joomla.org:

  • No documentation changes for docs.joomla.org needed

  • Pull Request link for manual.joomla.org: joomla/Manual#222

  • No documentation changes for manual.joomla.org needed

@HLeithner could yo have a look at this one? Thanks

avatar joomla-cms-bot joomla-cms-bot - change - 18 Aug 2023
Category Administration com_templates
avatar dgrammatiko dgrammatiko - open - 18 Aug 2023
avatar dgrammatiko dgrammatiko - change - 18 Aug 2023
Status New Pending
avatar dgrammatiko dgrammatiko - change - 18 Aug 2023
The description was changed
avatar dgrammatiko dgrammatiko - edited - 18 Aug 2023
avatar brianteeman
brianteeman - comment - 18 Aug 2023

Yay - adding another option - always a popular decision.

What is the real world use case for adding this option

avatar dgrammatiko
dgrammatiko - comment - 18 Aug 2023

Yay - adding another option - always a popular decision.

Where did you see that another option? I'm just making devs life easier: instead having to roll a plugin with a template now they could do it through the XML with a simple field.

What is the real world use case for adding this option

Obviously I have a use case thus the PR...

avatar richard67
richard67 - comment - 18 Aug 2023

As far as I can see, adding the new option happens only in the testing instructions and is only for testing.

avatar dgrammatiko
dgrammatiko - comment - 18 Aug 2023

@SharkyKZ what's your problem and you keep downvoting my PRs? Is there something wrong code wise that you want point out or...?

avatar brianteeman
brianteeman - comment - 18 Aug 2023

Maybe I misread the option part. I still think this needs to be justified with a real world use case. There is only one I am aware of and that was handled by the ext dev with documentation without any issue

avatar dgrammatiko
dgrammatiko - comment - 18 Aug 2023

I still think this needs to be justified with a real world use case

Yootheme are definitely overriding the menu assignment (at least they did last time I used one of their templates). Probably also JoomShaper and many other template studio out there. This is NOT an edge case...

avatar brianteeman
brianteeman - comment - 18 Aug 2023

You still dont explain why they are doing it. And presumably if they are then they have no problem that this pr is solving. /me confused

avatar dgrammatiko
dgrammatiko - comment - 18 Aug 2023

@brianteeman there's a why section in the description explaining why I did this PR and what it solves (dropping a side plugin just to have this part of the UI overridden and allow the whole form/view be driven by the XML). Is there something more you want me to explain here?

avatar brianteeman
brianteeman - comment - 18 Aug 2023

I read the why - it doesnt really explain the usecase

avatar dgrammatiko
dgrammatiko - comment - 18 Aug 2023

Ok, let me try again.

So the route /administrator/index.php?option=com_templates&view=style&layout=edit&id=11 (where id is the style id) is just a render of the form driven from the <config> part of the templateDetails.xml of a template:

<config>
<fields name="params">
<fieldset name="advanced">
<field
name="brand"
type="radio"
label="TPL_CASSIOPEIA_BRAND_LABEL"
default="1"
layout="joomla.form.field.radio.switcher"
filter="boolean"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="logoFile"
type="media"
default=""
label="TPL_CASSIOPEIA_LOGO_LABEL"
showon="brand:1"
/>
<field
name="siteTitle"
type="text"
default=""
label="TPL_CASSIOPEIA_TITLE"
filter="string"
showon="brand:1"
/>
<field
name="siteDescription"
type="text"
default=""
label="TPL_CASSIOPEIA_TAGLINE_LABEL"
description="TPL_CASSIOPEIA_TAGLINE_DESC"
filter="string"
showon="brand:1"
/>
<field
name="useFontScheme"
type="groupedlist"
label="TPL_CASSIOPEIA_FONT_LABEL"
default="0"
>
<option value="0">JNONE</option>
<group label="TPL_CASSIOPEIA_FONT_GROUP_LOCAL">
<option value="media/templates/site/cassiopeia/css/global/fonts-local_roboto.css">Roboto (local)</option>
</group>
<group label="TPL_CASSIOPEIA_FONT_GROUP_WEB">
<option value="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@100;300;400;700&amp;display=swap">Fira Sans (web)</option>
<option value="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@100;300;400;700&amp;family=Roboto:wght@100;300;400;700&amp;display=swap">Roboto + Noto Sans (web)</option>
</group>
</field>
<field
name="noteFontScheme"
type="note"
description="TPL_CASSIOPEIA_FONT_NOTE_TEXT"
class="alert alert-warning"
/>
<field
name="colorName"
type="filelist"
label="TPL_CASSIOPEIA_COLOR_NAME_LABEL"
default="colors_standard"
fileFilter="^custom.+[^min]\.css$"
exclude="^colors.+"
stripext="true"
hide_none="true"
hide_default="true"
directory="media/templates/site/cassiopeia/css/global/"
validate="options"
>
<option value="colors_standard">TPL_CASSIOPEIA_COLOR_NAME_STANDARD</option>
<option value="colors_alternative">TPL_CASSIOPEIA_COLOR_NAME_ALTERNATIVE</option>
</field>
<field
name="fluidContainer"
type="radio"
layout="joomla.form.field.radio.switcher"
default="0"
label="TPL_CASSIOPEIA_FLUID_LABEL"
>
<option value="0">TPL_CASSIOPEIA_STATIC</option>
<option value="1">TPL_CASSIOPEIA_FLUID</option>
</field>
<field
name="stickyHeader"
type="radio"
label="TPL_CASSIOPEIA_STICKY_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
filter="integer"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="backTop"
type="radio"
label="TPL_CASSIOPEIA_BACKTOTOP_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
filter="integer"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
</fieldset>
</fields>
</config>
with a result like:
Screenshot 2023-08-18 at 11 40 19

The only part that IS NOT driven by the XML and it is hardcoded is the menu assignment tab:

<?php if ($user->authorise('core.edit', 'com_menus') && $this->item->client_id == 0 && $this->canDo->get('core.edit.state')) : ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'assignment', Text::_('COM_TEMPLATES_MENUS_ASSIGNMENT')); ?>
<fieldset id="fieldset-assignment" class="options-form">
<legend><?php echo Text::_('COM_TEMPLATES_MENUS_ASSIGNMENT'); ?></legend>
<div>
<?php echo $this->loadTemplate('assignment'); ?>
</div>
</fieldset>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php endif; ?>

For a developer to roll their own UI/UX/Logic for the menu selection of a template style there is a requirement to write a plugin that will override the entirety of the layout just to have a rendering of the Menu Assignment tab.

This PR just enables the Menu Assignment tab to be rendered using the XML, in short devs don't have to roll a plugin anymore. That's all, 4 lines added all and all but an easier path to override something that is very hard to override in the current state!

avatar dgrammatiko dgrammatiko - change - 18 Aug 2023
Labels Added: PR-5.0-dev
avatar dgrammatiko dgrammatiko - change - 18 Aug 2023
The description was changed
avatar dgrammatiko dgrammatiko - edited - 18 Aug 2023
avatar HLeithner
HLeithner - comment - 18 Aug 2023

Basically I'm open for this change, but the way is too limited, normally we would render the complete fieldset and not a single field in a tab, like in joomla.edit.params or joomla.edit.fieldset so making this more generic would make more sense too me.

avatar dgrammatiko dgrammatiko - change - 18 Aug 2023
The description was changed
avatar dgrammatiko dgrammatiko - edited - 18 Aug 2023
avatar dgrammatiko
dgrammatiko - comment - 18 Aug 2023

@HLeithner code (testing instructions) updated so now the XML could have a new fields section and a fieldset named assigned. All the fields in the mentioned fieldset would be rendered at the menu assignment tab.

avatar dgrammatiko dgrammatiko - change - 18 Aug 2023
The description was changed
avatar dgrammatiko dgrammatiko - edited - 18 Aug 2023
avatar richard67
richard67 - comment - 18 Aug 2023

Besides all I have a question regarding the title of this PR: What does “DX” stand for?

avatar dgrammatiko
dgrammatiko - comment - 18 Aug 2023

What does “DX” stand for

Developer eXperience

avatar ceford
ceford - comment - 12 Sep 2023

I tested this and see the Menu Assignment panel replaced with It works Yes/No panel. But I am left wondering how I would select a menu item for this particular template style. It seems I have to write code to render and process whatever form fields I use instead of the Menu Assignment stuff. And perhaps I might add another panel to keep the Menu Assignment and do something else as well. I don't really have enough experience to say any more.


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/41388.

avatar HLeithner
HLeithner - comment - 30 Sep 2023

This pull request has been automatically rebased to 5.1-dev.

avatar dgrammatiko dgrammatiko - change - 30 Nov 2023
Labels Added: Feature
avatar dgrammatiko dgrammatiko - change - 30 Nov 2023
The description was changed
avatar dgrammatiko dgrammatiko - edited - 30 Nov 2023
avatar dgrammatiko dgrammatiko - change - 30 Nov 2023
Labels Added: PR-5.1-dev
avatar laoneo laoneo - test_item - 15 Dec 2023 - Tested successfully
avatar laoneo
laoneo - comment - 15 Dec 2023

I have tested this item ✅ successfully on 9f231fd

Tested still successfully regarding the description of the pr. I see the benefit, so it would be good if one from the template clubs can test here as well.


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/41388.

avatar alikon alikon - test_item - 3 Feb 2024 - Tested successfully
avatar alikon
alikon - comment - 3 Feb 2024

I have tested this item ✅ successfully on 5fd67e9


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/41388.

avatar Quy Quy - change - 3 Feb 2024
Status Pending Ready to Commit
avatar Quy
Quy - comment - 3 Feb 2024

RTC


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/41388.

avatar Quy
Quy - comment - 3 Feb 2024

@dgrammatiko Please fix conflict. Thanks.

avatar dgrammatiko dgrammatiko - change - 3 Feb 2024
Labels Added: RTC
Removed: PR-5.0-dev
avatar dgrammatiko
dgrammatiko - comment - 3 Feb 2024

Done

avatar dgrammatiko
dgrammatiko - comment - 8 Feb 2024

@Razzo1987 can I have a decision here if this is gonna be merged for 5.1?

Thanks

avatar Razzo1987
Razzo1987 - comment - 8 Feb 2024

@dgrammatiko Not yet, but we will give you an update in the next few days

Thank you for your work

avatar Razzo1987 Razzo1987 - change - 14 Feb 2024
The description was changed
avatar Razzo1987 Razzo1987 - edited - 14 Feb 2024
avatar Razzo1987 Razzo1987 - change - 27 Feb 2024
Status Ready to Commit Fixed in Code Base
Closed_Date 0000-00-00 00:00:00 2024-02-27 12:46:16
Closed_By Razzo1987
avatar Razzo1987 Razzo1987 - close - 27 Feb 2024
avatar Razzo1987 Razzo1987 - merge - 27 Feb 2024
avatar Razzo1987
Razzo1987 - comment - 27 Feb 2024

Thank you very much @dgrammatiko !

Add a Comment

Login with GitHub to post a comment