Juergen Posted October 23, 2017 Share Posted October 23, 2017 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. 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 Link to comment Share on other sites More sharing options...
abdus Posted October 23, 2017 Share Posted October 23, 2017 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. 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); }); 3 1 Link to comment Share on other sites More sharing options...
Juergen Posted October 23, 2017 Author Share Posted October 23, 2017 Thanks @abdus for the detailed response! I will try it out . Link to comment Share on other sites More sharing options...
Juergen Posted October 28, 2017 Author Share Posted October 28, 2017 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: 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. Link to comment Share on other sites More sharing options...
abdus Posted October 28, 2017 Share Posted October 28, 2017 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 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); }); 3 1 Link to comment Share on other sites More sharing options...
wbmnfktr Posted October 28, 2017 Share Posted October 28, 2017 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. 1 Link to comment Share on other sites More sharing options...
Juergen Posted October 28, 2017 Author Share Posted October 28, 2017 That is exactly what I am looking for. I will try to implement your solution!!!! Thanks! Link to comment Share on other sites More sharing options...
Juergen Posted October 28, 2017 Author Share Posted October 28, 2017 @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. 2 Link to comment Share on other sites More sharing options...
Juergen Posted October 28, 2017 Author Share Posted October 28, 2017 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: After pressing the save button an nothing was choosen: 2 Link to comment Share on other sites More sharing options...
Juergen Posted November 5, 2017 Author Share Posted November 5, 2017 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. 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 3 Link to comment Share on other sites More sharing options...
adrian Posted November 5, 2017 Share Posted November 5, 2017 @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? 2 Link to comment Share on other sites More sharing options...
Juergen Posted November 5, 2017 Author Share Posted November 5, 2017 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). 2 Link to comment Share on other sites More sharing options...
adrian Posted November 5, 2017 Share Posted November 5, 2017 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? 1 Link to comment Share on other sites More sharing options...
Juergen Posted November 5, 2017 Author Share Posted November 5, 2017 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 4 Link to comment Share on other sites More sharing options...
Robin S Posted November 5, 2017 Share Posted November 5, 2017 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. 3 Link to comment Share on other sites More sharing options...
Juergen Posted November 6, 2017 Author Share Posted November 6, 2017 Opened a new issue on Github (https://github.com/ryancramerdesign/ProcessWire/issues/2065) Link to comment Share on other sites More sharing options...
Juergen Posted November 6, 2017 Author Share Posted November 6, 2017 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 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 Maybe this could be useful for others too, so feel free to try it out if you want. Link to comment Share on other sites More sharing options...
kongondo Posted November 6, 2017 Share Posted November 6, 2017 (edited) 3 hours ago, Juergen said: Opened a new issue on Github (https://github.com/ryancramerdesign/ProcessWire/issues/2065) @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 November 6, 2017 by kongondo Link to comment Share on other sites More sharing options...
Juergen Posted November 6, 2017 Author Share Posted November 6, 2017 Sorry, I have reported it in the new repository (https://github.com/processwire/processwire-issues/issues/424) Link to comment Share on other sites More sharing options...
Juergen Posted December 8, 2017 Author Share Posted December 8, 2017 A big thanks to Ryan! He has included this feature request in PW 3.0.85 Read more 1 Link to comment Share on other sites More sharing options...
Noel Boss Posted February 7, 2018 Share Posted February 7, 2018 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'); }); 2 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now