Jump to content
Juergen

Possible to add an "select" text option to a template select at the top?

Recommended Posts

Hello @ all,

I have a template select in my calendar template, where the user can choose if the new created page should use the template of a company vacation, special opening hours, an event or a default date. It is the template select which will be taken before a new child page will be created.

screenshot-www.juergen-kern.at-2017-10-23-08-53-37.thumb.png.1219c2fc5789c1e34ace9ddbebb87b34.png

screenshot-www.juergen-kern.at-2017-10-23-07-25-11.png.3d4d7ba01ceb1bac8920fe31161747f5.png

Most of the time the user didnt take care of this select function and creates a page with the pre-selected template. After creating the page the user recognizes, that this is the wrong template and he has to delete the newly created page before creating a new child page with the correct template.

My question: Is it possible, fe via a hook, to add a "select template" option at the top of this select, so that the user has to select the correct template first? Otherwise he will get an error.

This would be a real enhancement to the workflow.

Best regards

 

 

Share this post


Link to post
Share on other sites

I had a tutorial about this here. Using the hook below you can move template select into content tab before the title. Keep in mind your editors should be able to change templates for your event templates.

image.png.311056674856a943a6e5546c8adfb8ed.png

wire()->addHookAfter('ProcessPageEdit::buildForm', function (HookEvent $e) {
    // make sure we're editing a page and not a user
    if ($e->process != 'ProcessPageEdit') return;

     $page = $e->object->getPage();
     // does page template start with event_?
     if (strpos($page->template, 'event_') !== 0) return;
    
    $form = $e->return;

    $contentTab = $form->children->get('id=ProcessPageEditContent');
    $settingsTab = $form->children->get('id=ProcessPageEditSettings');
    if (!$settingsTab) return;

    $template = $settingsTab->get('template');
    if (!$template) return;
    $settingsTab->remove('template');
    $contentTab->prepend($template);
});

 

  • Like 3
  • Thanks 1

Share this post


Link to post
Share on other sites

Hello @abdus

your code works, but it doesn´t solve my problem. I have the template selection not in my page template because I don´t want to allow the user to change the template.

The selection has to be taken before the new page is created. This is necessary because there are different UIs (forms) for the different type of templates.

This is the place where the user have to select the template type:

screenshot-www.juergen-kern.at-2017-10-28-12-59-58.png.9a5f87f75fda7ed6e4cfc3ffd7a491d3.png

At the top the user has to select the prefered template type depending on which kind of page he wants to create (Business vacation, Event, Default date or Special openinghours). Depending on this selection the corresponding input form will be loaded.

The problem: There is always one template type selected and no user action needs to be taken, but I want to force the user to select the type on his own - so he has to consider what kind of event he wants to create.

So instead of a pre-selected template type I want an option like "make a choice" so the user has to select one.

My idea was to add another template called "make a choice" which appear on the first place and has no function. Afterwards I make a custom validation via a hook. If this template selection was not changed to another template type  the user gets an error messages to select a template type. But maybe there will be another more simple solution.

Share this post


Link to post
Share on other sites

Ok, so if I understand it right, you want to force user to pick a template. I'm not sure if this fits your needs but with a bit of JS and hooks you can remove default value and set the template select required (with `required` html attribute), such that you cant get to the next step without deliberately picking a template

image.png.e59f19b7ae68f46e405da57317b6e3a4.png

wire()->addHookAfter('ProcessPageAdd::buildForm', function (HookEvent $e) {
    /** @var InputfieldForm $form */
    $form = $e->return;
    $form->add([
        'type' => 'markup',
        'value' => '<script>document.querySelector("[name=template]").selectedIndex = -1;</script>'
    ]);
    $template = $form->getChildByName('template');
    if ($template) $template->attr('required', 1);
});

 

  • Like 3
  • Thanks 1

Share this post


Link to post
Share on other sites

Another idea...

Create a new parent for each kind of event type. Set corresponding template restrictions in the template settings (family tab) and the users were forced to choose what kind of event they want to create.

  • Like 1

Share this post


Link to post
Share on other sites

@wbmnfktr

This was the structure that I had before and it works but I want to get rid of it, because the category pages were always displayed in my breadcrumbs.:( So I had to manipulate the breadcrumbs output before render.

  • Like 2

Share this post


Link to post
Share on other sites

So I ended up with the slightly modificated code of abdus which works like a charme:

wire()->addHookAfter('ProcessPageAdd::buildForm', function (HookEvent $e) {
    /** @var InputfieldForm $form */
    $form = $e->return;
    $form->add([
        'type' => 'markup',
        'value' => '<script>
                    $("#template").prepend("<option value=0 selected>Please select</option>");
                   </script>'
    ]);
    $template = $form->getChildByName('template');
    $template->attr('required', 1);
});

Before pressing the save button:

screenshot-www.juergen-kern.at-2017-10-28-13-56-05.png.f1b8401b6ea3a7731df9bd3644223151.png

After pressing the save button an nothing was choosen:

screenshot-www.juergen-kern.at-2017-10-28-13-56-30.png.f76cd07e55544dac76ff6f9e6345662c.png

 

  • Like 2

Share this post


Link to post
Share on other sites

This is my last an best working approach with no JS (only PHP).

In this case I use a Hook with a regex and a session variable to add an additional option tag with "Please select" at the top. So the user has to select one of the template types. Otherwise he will get an error message.

screenshot-www.juergen-kern.at-2017-11-05-19-24-27.png.5bae6ed83a050ede21b09f4111438127.png

Only to mention: There is also another Hook used to create the notice at the bottom (but this is not part of this topic ;))

Here is the code, which should be placed inside ready.php:

$wire->addHookAfter('InputfieldSelect::render', function(HookEvent $event) {
    if($this->process != 'ProcessPageAdd') return;
    $inputfield = $event->object;
        if($inputfield->name === 'template') { 
            $regex = '#<\s*?select\b[^>]*>(.*?)</select\b[^>]*>#s';//regex to get all content (options) between the select tags
            $str = $event->return; 
            preg_match($regex, $str, $match);
            $origoptions = $match[1];//get all original options
            $options = $origoptions; 
            if(isset($_POST['template']))    
            wire('session')->selectedoption = wire('input')->post('template');//set session after POST
            if(wire('session')->selectedoption){
              wire('session')->remove('selectedoption');//remove session
            } else {              
              $options = str_replace("selected='selected'", '', $origoptions); //remove selected attribute on first load                            
            }          
            $newoptions = '<option value="">'.__("Please select").'</option>'.$options;//create all options including the empty option tag at the beginning            
            $result = str_replace($origoptions, $newoptions, $str);//replace the original options with the new options
            $event->return = $result;                        
        }
});

Short explanation:

The regex is used to get all options between the select tags (needed a little bit later to add the additional option tag on the top)

The session is used to check if the page is loaded for the first time. If yes (no session created) than the pre-selected option will be removed, so that the "Please select" option will be selected on the first page load. The session will be removed afterwards. So this step is only necessary to get the "Please select" as pre-selected value on the first page load.

Afterwards the additional option tag with "Please select" will be added to the original option tag which was grabbed with the regex.

The final step is to output the modified select.

At the moment I didnt find any problems or errors with this code, but if someone has some improvements please let me know.

Best regards

  • Like 3

Share this post


Link to post
Share on other sites

@Juergen - nice work. I actually wonder if perhaps this should be the default behavior for this. I almost never allow editors to choose a template for child pages, but with this approach I might be more willing to do so in the future.

Should this be a request to Ryan?

  • Like 2

Share this post


Link to post
Share on other sites

In my case I have a parent template for events. Under this parent the editor can create events and he can choose between 4 different type of events (at the moment). Each of them has its own template and form with different fields. Therefore it is necessary in this case that editors can choose between these types (depending on which type of event should be added).

Fe if the editor wants to add a new event with price, max. participants, deadline for reservation and so on.. he needs other input fields than if he wants to add only a date for a company vacation.

It would be great if this could be part of the core (fe with the option to select in the settings if a "Please select" should be displayed or not).

  • Like 2

Share this post


Link to post
Share on other sites
Just now, Juergen said:

Therefore it is necessary in this case that editors can choose between these types

Oh yeah, I agree it can be very useful. I have just managed to avoid the need so far, but your enhancement would make it much more functional and less prone to being ignored thereby creating problems.

 

2 minutes ago, Juergen said:

It would be great if this could be part of the core (fe with the option to select in the settings if a "Please select"s should be used or not).

Maybe add a request in the appropriate Github requests repo?

  • Like 1

Share this post


Link to post
Share on other sites

Tomorrow! I need to go offline for today - my girlfriend is not so pleased if I am working to much on Sundays - Happy wife, happy life ;)

  • Like 4

Share this post


Link to post
Share on other sites
2 hours ago, Juergen said:

This is my last an best working approach with no JS (only PHP).

Here is an attempt to simplify things a bit:

$wire->addHookAfter('ProcessPageAdd::buildForm', function(HookEvent $event) {
	$form = $event->return;
	$template_select = $form->getChildByName('template');
	$options = $template_select->getOptions();
	$options = array('' => '') + $options;
	$template_select->set('options', $options);
	// Set the value to something that will never match an option in the select (any string will do)
	$template_select->value = 'a';
	$template_select->attr('required', 1);
});

It's important to set the HTML required attribute (thanks to @abdus for the idea) and to make sure none of the options get the "selected" attribute (hence setting the inputfield value to something that will never match an option). Otherwise if the Add New form is submitted with no actual template selected a nasty error occurs - the core does not allow for the possibility that no valid template ID is included in POST.

 

  • Like 3

Share this post


Link to post
Share on other sites

Here are all enhancements for the template select field that I have used:

1) On page add process: Make an empty option at the top (thanks @Robin S for the easier solution) and give the user a note to select a template (optional add an additional class to style the inputfield)

2) On page edit process: Show the template select field inside the content tab instead of the settings tab (thanks @abdus), give the user a note to save the page after template change and optional add an additonal class to style the inputfield.

Here are the complete code lines which should be copied into the ready.php.

// 1) Template select field enhancements

//a) add empty select on top of template and additional note select on page add
$wire->addHookAfter('ProcessPageAdd::buildForm', function(HookEvent $event) {
	$form = $event->return;
	$template_select = $form->getChildByName('template');
	$options = $template_select->getOptions();
	$options = array('' => '') + $options;
	$template_select->set('options', $options);
	// Set the value to something that will never match an option in the select (any string will do)
	$template_select->value = 'a';
	$template_select->attr('required', 1);
  $template_select->notes = __("Please select which type of page you want to create first.");
  //$template_select->addClass('importantfield', 'wrapClass'); //optional
});

//b) Add class and additional note to template select field on page edit
$wire->addHookBefore('InputfieldSelect::render', function(HookEvent $event) {
    if($this->process != 'ProcessPageEdit') return;
    $page = $this->process->getPage();
    $inputfield = $event->object;
        if($inputfield->name === 'template') {
            $inputfield->notes = __("If you want to change the template, then choose your desired template and press the save button before filling out the fields.");
            //$inputfield->addClass('importantfield', 'wrapClass'); //optional
        }
});

//c) move template select to content tab on page edit
wire()->addHookAfter('ProcessPageEdit::buildForm', function ($event) {
    // make sure we're editing a page and not a user
    if ($event->process != 'ProcessPageEdit') return;
     $page = $event->object->getPage();
     // does page template start with event_?
     if (strpos($page->template, 'event_') !== 0) return;    
    $form = $event->return;
    $contentTab = $form->children->get('id=ProcessPageEditContent');
    $settingsTab = $form->children->get('id=ProcessPageEditSettings');
    if (!$settingsTab) return;
    $template = $settingsTab->get('template');
    if (!$template) return;
    $settingsTab->remove('template');
    $contentTab->prepend($template);
});

Here are some screenshots:

a) Page add

screenshot-www.juergen-kern.at-2017-11-06-07-52-07.png.945ee408a295ab2a6f5417663417bb94.png

As you can see the wrapper of the inputfield is grey in this case - this is because I have added a custom class to mark this field as important (background: #ddd).

b) Page edit

screenshot-www.juergen-kern.at-2017-11-06-07-52-39.png.0686f60ed65c6304c48e41eebef1c14f.png

Maybe this could be useful for others too, so feel free to try it out if you want.

Share this post


Link to post
Share on other sites
3 hours ago, Juergen said:

@Juergen, that is the old and unused issues' tracker. This ProcessWire issues repository is where reports should be submitted. If it is a request, it should be submitted here: ProcessWire feature requests repository.

Edited by kongondo

Share this post


Link to post
Share on other sites

Thanks for all the inputs! Whit it's help, I was able to move the content of the Children Tab to the content Tab and remove the tab, as well as moving the name field below the title or subtitle field. Maybe someone else can use this as well:

	// Reorder Fields
	wire()->addHookAfter('ProcessPageEdit::buildForm', function ($event) {

			// make sure we're editing a page and not a user
			if ($event->process != 'ProcessPageEdit') {
				return;
			}

			$page = $event->object->getPage();
			$form = $event->return;
			$settingsTab = $form->children->getChildByName('id=ProcessPageEditSettings');
			$contentTab = $form->children->getChildByName('id=ProcessPageEditContent');

			// move name below title or subtitle
			foreach ([$settingsTab->getChildByName('_pw_page_name')] as $child) {
				if ($child) {
					$child->getParent()->remove($child->name);
					$child->collapsed = Inputfield::collapsedPopulated;
					$contentTab->insertAfter($child, $contentTab->get('subtitle|title'));
				}
			}

			// continue only if on certain template
			if ($page->template->name !== 'ResourceEntry') {
				return;
			}
			
			// children tab
			$childrenTab = $form->children->getChildByName('id=ProcessPageEditChildren');
			
			if (!$childrenTab) {
				return;
			}
			// move all content
			foreach ($childrenTab->children as $child) {
				if ($child) {
					// relable "add" buttton
					if ($child->get('AddPageBtn') instanceof InputfieldButton) {
						$child->get('AddPageBtn')->attr('value', _('Add New'));
					}
					$contentTab->append($child);
				}
			}

			// remove Tab and tab content:
			$form->remove($childrenTab);
			$event->object->removeTab('ProcessPageEditChildren');
	});
	

 

  • Like 2

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...