-
Posts
1,394 -
Joined
-
Last visited
-
Days Won
17
Everything posted by Juergen
-
Hello, this is an issue I was struggeling for a while to find a working solution: The goal: A page must contain at least 1 child page. If there are more child pages than prevent the editor (user) from deleting the last child page and output a message that the deletion of the last page is not allowed. Idea behind this: I have pages which holds all the information of events, but the date or dates of the event are all located in child pages: - event page - event date 1 - eventdate 2... and so on. Each event must have at least on date. An event with no date doesnt make sense. This was the reason to not allow the deletion of the last date. Here is the code that you should copy into your ready.php $wire->addHookBefore("Pages::trash", function($event) { $page = $event->arguments(0); if(!in_array($page->template->name, array('single-date', 'single-event', 'single-business-vacation', 'single-special-business-hours'))) return; $parent = $page->parent; $childrennumber = count($page->parent->children); if($childrennumber === 1) { $event->replace = true; // now original function won't be called $event->return = $this->warning("Trashing not allowed!"); } else { $this->message("1 Page was deleted"); } }); 1) You must hook before the page will put to trash so "addHookBefore" has to be used 2) If you want to restrict it to certain types of templates than put the names of the templates in an array (in this case the templates I want to run the hook are "single-date, single-event, single-business-vacation or single-special-business-hour". If you want it to run on all pages simply remove this line from the code: if(!in_array($page->template->name, array('single-date', 'single-event', 'single-business-vacation', 'single-special-business-hours'))) return; 3) You need to get the number of all child pages from the parent to check if it is the last child or not. If there are more than 1 page and you delete on of them you get the message "1 page deleted" - so everthing is ok and the deletion of the page take place 4) If it is the last child than the trash function will be replaced by a message. So the trash function will not be executed and the warning message will be displayed instead. 5) Inform the editor with this message that the deletion of the last child is not allowed. I use here the message type "warning" (color orange), but you can also use "error" or "message" Thats all - hope this is useful for others that need something similar Only to mention: This works also via Ajax deletion fe in the page tree, but in first instance it seems that the last child was deleted because it disappears, but after refreshing the page the last child will be visible again.
-
Hook to change page status depending on children
Juergen replied to Juergen's topic in General Support
Here is the final code the prevents the deletion of the last child: I restrict it that the child is type of one of the following templates: single-date, single-event, single-business-vacation or single-special-business-hours. //Prevent deletion of the the last child $wire->addHookBefore("Pages::trash", function($event) { $page = $event->arguments(0); if(!in_array($page->template->name, array('single-date', 'single-event', 'single-business-vacation', 'single-special-business-hours'))) return; $parent = $page->parent; $childrennumber = count($page->parent->children); if($childrennumber === 1) { $event->replace = true; // now original function won't be called $event->return = $this->message("Trashing not allowed! There must be at least 1 page."); } }); I put a complete description in the Tutorial section -
Hook to change page status depending on children
Juergen replied to Juergen's topic in General Support
Ok, I have found a solution to prevent the deletion of a page: $wire->addHookBefore("Pages::trash", function($event) { $page = $event->object; $event->replace = true; // now original function won't be called $event->return = $this->message("Trashing not allowed!"); }); So it must be adapted to only run on certain templates and if there is only 1 child. -
Hook to change page status depending on children
Juergen replied to Juergen's topic in General Support
An idea would be to check the number of children before the deletion. If there is only 1 child than prevent processing of the delete function. Maybe lets say a hook that prevents the execution of the deletion???? Something like $event->replace = true; -
Hello @ all, I have the following scenario: A parent page status should be changed depending on the number of children. If there is at least 1 child the status should be published, if there are no children the statuts should be unpublished. Seems easy to realize at first sight, but ... the button of the parent page should also change. If there are children (at least 1): Only the save button should be displayed. If there are no children: The 2 buttons should be displayed. The children will be created or deleted via a Pagetable field and can be also created by clicking a checkbox in the parent page. I have tried to hook into "added" and "deleted" and also "trashed" to set the page->parent->status and it seems to work, but the button appearence will not change. So the status of the parent page seems to be changed but not the buttons (that is the problem) . Here is what it looks like after deleting of the last child: As you can see the status of the page was changed to "2048" which is unpublished, but the button is the same as before (no change). If you take a look into the settings tab you will see that the checkbox for unpublish is also not checked: The buttons change only if I hook into "before save ready" but this kind of hook doesnt take care of added or delete pages. It is also not possible to check for hasChildren because the creation of the children take place after saving of the parent page. Does someone has struggled with the same problem and has a working solution? Another approach would be to prevent the deletion of the last child.
-
Hello @ all, It want to change the markup of the notices (error, warning, message) a little bit. By default error messages in the admin look like this: There is the hint that a field is required and next to this message is the name of the field in parenthesis. This is the point, where I like to hook into. I want to replace the name of the field by the field label because editors dont know what the name of the field is. So if the user is not superuser this hook should take place. It is not a thing that is very important to me, but if there is a render function of the messages that will be hookable, it would be great if someone can tell me about. I have taken a look at https://processwire.com/api/hooks/captain-hook/ but I cannot find anything so maybe it will not be possible. But who knows....
-
I am using it too and I have not detected any problems since then.
-
Problem with field validation and values added via saveReady hook
Juergen replied to Juergen's topic in General Support
No need to investigate further. I have disabled the validation if the value will be added via a hook. The validation only take place if the value is entered manually -
Hello @ all, I cannot find a solution for this behavior: I want to validate a field which value is sometimes filled out manually and sometimes via a hook. The custom validation was done with this type of hook: $pages->addHookAfter("InputfieldDatetime::processInput", function($event) { The hook for adding a value in this field is done via this type of hook: $pages->addHookBefore('saveReady', function($event){ The 2 hooks work but the problem is that "processInput" runs before "saveReady" and so I always get the error that the field is empty. But the value will be added correctly if you can see at the following screenshot. If I fill out the value manually everything works. So which type of hook runs before "processinput" and can be used to populate a field with a value. Thanks
-
I know, this is the solution if you have only 1 template assigned.
-
How to store translateable value in multilanguage textfield via API
Juergen replied to Juergen's topic in General Support
My final solution is to use a Hook as @LostKobrakai recommended: Here is an example for others to hook into the page tree label. Code has to be placed inside ready.php $wire->addHookAfter('ProcessPageListRender::getPageLabel', function($event) { $label = $event->object; $page = $event->arguments(0); if($page->template->name == "single-date"){ $event->return = $page->title.'-test'; } }); For testing purposes I only use it on one template type called "single-date" and I change the label from "title" to "title + test". Here is what it looks like: As you can see the text "test" will be added on that template type next to the page title. This is only for others to see how it can be done. You have the idea. Only to mention: You will only need this Hook if you want to add text or values which are not stored in the DB. Otherwise you can use the field in the template settings. -
If you want to add your own button value at the add button of a pagetable field, you only have to put this little piece of code inside your ready.php file. $wire->addHookBefore('InputfieldPageTable::render', function($event) { $table = $event->object; if(!in_array($table->name, array('datespagetable'))) return; $this->addHookBefore("InputfieldButton::render", null, function(HookEvent $event){ $button = $event->object; if($button->name == 'button'){ $button->attr('value', 'Test'); } }); }); In this case I limit it to a certain pagetable field called "datespagetable". You have to change it to the name of your desired pagetable field. If you want to change it for all pagetable fields (no restrictions) simply remove the following line from the code: if(!in_array($table->name, array('datespagetable'))) return; If you want to add it to more than 1 pagetable field, then write all the fieldnames into the array: if(!in_array($table->name, array('datespagetable','pagetable1','pagetable2'))) return; You can change the value of the button text to your needs in this line of code: $button->attr('value', 'Test'); I simply called it "Test" in this case to show how it is working. If you need it to be a translateable string simply use: $button->attr('value', __('Test')); And now this is the result: This let you customize your pagetable field a little bit. Fe you can use "Add News" or "Add new events" or something else. If you want to change another attribute than the value please take a look at the following page: https://github.com/processwire/processwire/blob/master/wire/modules/Inputfield/InputfieldButton.module Best regards
-
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.
-
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).
-
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
-
Thank you @Robin S, your solution works!!! Also thank you for your detailed explanation, but where do you get all your knowledge about Hooks ? A big thanks to all other contributors for your help!
-
Thank you @kixe I will try this tomorrow.
-
I have put the code inside the ready.php, but I need to run it only on certain templates and under certain conditions. Therefore I need the page to get the template associated with it. So I dont want to disable them permanently only if the page has no children. If childrens were created the field should be enabled. The idea is to make this field (a checkbox field) checked and disabled if there are no children created. If the user has created at least 1 child page, then this checkbox field will be unchecked and enabled. The automatic checking works with this Hook after a new page (with no children) is added //Set create events to true if there are no child pages $this->pages->addHookAfter('added', function($event) { $page = $event->arguments('page'); $field = $event->arguments('field'); if(in_array($page->template->name, array('event_businessvacations','event_specialbusinesshours','event_dates','event_events'))) { if(count($page->children) == 0){ $page->setAndSave('createevents', true); } } }); To prevent the user to uncheck the checkbox I want to disable it in this case.
-
Both possible solutions dont work $page = $event->object->hasPage; or $pages->addHookBefore('Inputfield::render', function($event) instead of $this->pages->addHookBefore('Inputfield::render', function($event) $page is always empty
-
Hello, I have written a hook to disable an inputfield on certain conditions: //Disable createevents field if there are no children $this->pages->addHookBefore('Inputfield::render', function($event) { $page = $event->arguments(0); $field = $event->object; if(in_array($page->template->name, array('event_businessvacations','event_specialbusinesshours','event_dates','event_events'))) { if(count($page->children) == 0){ if('createevents' === $field->name ){ $field->attr("disabled" , "disabled"); } } } }); I am getting always following notice from Tracy: PHP Notice: Trying to get property of non-object in /home/.sites/24/site1275/web/site/ready.php:310 This is the line where $page is defined. How can I grab $page in this case correctly or do I have to use another type of Hook?
-
Ideas and best practices to secure a member registration system
Juergen replied to modifiedcontent's topic in Security
Hello @modifiedcontent Composer safes you a lot of time on updating and installing PHP-libraries which you want to use in your project. I use more than 1 external PHP library on my projects, so it makes really sense in my case. Honeypot class is only one of them. If you have time you could read how to install it on your system. It seems to be difficult at the beginning, but there are also tutorial videos on Youtube that are really good and easy to follow. The only thing to mention: you have to work with the CLI (command line interface). If you want to install for example the Honeypot class you only have to write composer require dominiquevienne/honeypot into your CLI and if you want to update it later you only have to write composer update Thats all and the latest version will be installed. No need to look for updates manually. No need to install updates manually. So its a really useful solution to install libraries and keep them up to date with less work.- 10 replies
-
- 1
-
-
- no subscription detected
- not recognized
-
(and 4 more)
Tagged with:
-
How to store translateable value in multilanguage textfield via API
Juergen replied to Juergen's topic in General Support
Thanks for your tipps. I will try it via the Hook method.