-
Posts
5,034 -
Joined
-
Days Won
340
Everything posted by Robin S
-
Weekly update and new blog post – 28 June 2024
Robin S replied to ryan's topic in News & Announcements
Awesome module @ryan! Could the module include an option to style custom children differently to genuine children? Or maybe add a special class to custom children items so we can target them in custom admin CSS. And for advanced cases, a hookable method so custom children items could be identified and potentially modified when rendered in Page List. -
If you are doing something like this... // Get poi page from Page Reference field $poi_page = $page->my_page_reference_field; // Render poi page echo $poi_page->render(); ...then you can pass variables to the template file of the poi page via the $options array argument. See docs: https://processwire.com/api/ref/page-render/render-page/ // Get poi page from Page Reference field $poi_page = $page->my_page_reference_field; // Render poi page echo $poi_page->render(['rendered_from' => $page]); And in the poi template file: if(!empty($options['rendered_from'])) { $rendered_from = $options['rendered_from']; //... } You can also use the $options['pageStack'] value in the template file. It's mentioned in the docs page linked to above, but probably easiest to understand if you just dump it and see what it contains.
-
See this recent reply to a similar question: I think render() is too late to apply collapsedHidden so hook InputfieldXXX::renderReadyHook() instead, but the same ideas apply. You can get the page being edited via the ID in $input.
-
I think you could use an inputfield dependency for this, with the "forpage" keyword: https://github.com/processwire/processwire/pull/255
-
I didn't study your post in detail, but maybe this gives you an idea: $wire->addHookBefore('InputfieldText::render', function(HookEvent $event) { /** @var InputfieldText $inputfield */ $inputfield = $event->object; // The Field object associated with the inputfield, so the name isn't affected by any repeater suffix $field = $inputfield->hasField; // The Page that the inputfield is in so you can check its template // to see if it belongs to the relevant repeater field $page = $inputfield->hasPage; if(!$field || !$page) return; if($field->name === 'text_1' && $page->template == 'repeater_test_repeater') { $inputfield->appendMarkup('hello'); } });
- 2 replies
-
- 3
-
-
- repeater matrix
- repeater
-
(and 3 more)
Tagged with:
-
For the title field you can enable access control in template context to limit editability by role. For deletion you can revoke the delete permission per template per role. For the settings tab there is a $template->noSettings property: https://github.com/processwire/processwire/blob/38a5320f612a4b38a7353265343219f224f20e6d/wire/core/Template.php#L100 You can conditionally set this with a hook for non-superusers: $wire->addHookBefore('ProcessPageEdit::execute', function(HookEvent $event) { /** @var ProcessPageEdit $ppe */ $ppe = $event->object; /** @var Page $page */ $page = $ppe->getPage(); if($page->template == 'my_template' && !$event->wire()->user->isSuperuser()) { $page->template->noSettings = 1; } });
-
System templates have the Template::flagSystem flag. So the "proper" way: $non_system_templates = new TemplatesArray(); foreach($templates as $template) { // Skip templates with the system flag if($template->flags & Template::flagSystem) continue; $non_system_templates->add($template); } The lazy way that is likely to work 99.9% of the time: $non_system_templates = $templates->find("flags=0"); For some reason templates that are used for the custom fields for files/images feature don't have the system flag, so you will have to exclude those separately if you have any. The name of such templates starts with "field-".
-
I don't know for sure but it looks like a problem with your Imagick configuration or file permissions. The module won't be able to solve that but if you update to the newly released v0.2.2 there is an option in the module config where you can choose to use GD for the conversion instead.
-
I do it essentially the same as what @TomPich said, but on the first move from local to remote I find it's a lot faster and more reliable to compress all the website files to a ZIP archive, upload that to the remote server, then extract it on the server. If you're using cPanel then the included File Manager is a convenient way to upload and extract. And when using a host that doesn't include a file manager I like to use TinyFileManager, although you need to take due care with security - as extra protection I rename the containing folder to include a dot prefix to prevent access when I'm not actively using it.
-
@mel47, currently the PW core does not support showIf or requiredIf in custom fields for files/images. See this issue: https://github.com/processwire/processwire-issues/issues/1889 And even if that issue is resolved, the way the core is handling showIf/requiredIf at the moment means that it will only evaluate the showIf/requiredIf based on values of other custom fields for the image/file item, not fields in the page that is open in Page Edit. But I've just updated the CustomInputfieldDependencies module so that it should handle your case. Please update the module to v0.3.0 and test it again.
-
Proposal: Every module should have an "active" checkbox
Robin S replied to nurkka's topic in Modules/Plugins
When $config->debug and $config->advanced are true there is an "Autoload disabled" checkbox available in the module info for any autoload module. Checking this box disables the module in the same way as Tracy's Module Disabler panel, but might be useful on a site that doesn't have Tracy installed. -
For a case like this you can use the hookable method to return any markup you like, including an image. It won't work for a UIkit tooltip though because that only supports text. Example: $wire->addHookAfter('PageFieldInfo::getPageInfo', function(HookEvent $event) { $page = $event->arguments(0); // The page $inputfield = $event->arguments(1); // InputfieldPage $field = $event->arguments(2); // The Page Reference field // Return custom markup if($field->name === 'colour') { $image = $page->get('image[0]'); if($image) $event->return = "<img src='$image->url' alt='$image->description' style='max-width:200px; max-height:200px; margin-top:10px;'>"; } });
-
Ryan said in the previous announcement that you can use a hook to set which pages become editable. So you could use this to make the grandchildren editable, or the children and the grandchildren if you wanted the child pages to serve as a kind of visual divider between the categories of options. Edit: example... // Define custom "children" for PageEditChildren $wire->addHookAfter('PageEditChildren::getChildren', function(HookEvent $event) { /** @var Page $page */ $page = $event->arguments(0); if($page->template == 'blank') { $items = new PageArray(); foreach($page->children as $child) { $items->add($child); foreach($child->children as $grandchild) { $items->add($grandchild); } } $event->return = $items; } }); // Hook rendering of PageEditChildren "child" fieldsets // to apply a data attribute for identifying the template // It would be cool if PageEditChildren did this by default $wire->addHookBefore('InputfieldFieldset::render', function(HookEvent $event) { /* @var $fieldset InputfieldFieldset */ $fieldset = $event->object; $wrap_class = $fieldset->wrapClass(); if($wrap_class !== 'PageEditChild') return; $attr = $fieldset->wrapAttr(); if(!isset($attr['data-page'])) return; $p = $event->wire()->pages->get($attr['data-page']); if(!$p->id) return; $fieldset->wrapAttr('data-template', $p->template->name); }); Custom admin SCSS: li.PageEditChild[data-template="references"] { > .InputfieldHeader { background-color:#f0f3f7 !important; } }
-
Awesome, thanks @ryan!
-
Related tutorial:
-
Since ProcessWire v3.0.152 we have been able to use custom Page classes: https://processwire.com/blog/posts/pw-3.0.152/#new-ability-to-specify-custom-page-classes Some PW users have said that they would like to have all their hooks relating to a particular Page class contained within their custom Page class file (in /site/classes/), to keep things tidy and organised. But custom Page classes do not have methods that fire on states like "init" or "ready", and it also wouldn't be ideal to attach hooks within a Page class because the class can be instantiated multiple times and therefore such hooks would be attached multiple times. I understand @bernhard has a feature that addresses this need built into his RockMigrations module, so check that out. And here's another approach, where hooks are attached in the places where you would normally attach them (e.g. in /site/ready.php or /site/init.php, or in the init() or ready() methods of some custom module) but the method that executes in the hook exists within your custom Page classes. Step 1 In /site/classes/DefaultPage.php, add the method shown below: class DefaultPage extends Page { // Call any Page methods with names that match the HookEvent object and method public function callHookMethods(HookEvent $event) { $objectName = $event->object->className; $methodName = $objectName . '_' . $event->method; if(!method_exists($this, $methodName)) return; $this->$methodName($event); } } This method looks for other Page method names that match the HookEvent object and method and if any exist it calls them. Hooks are attached using the format "Class::method" and this format wouldn't be valid for a method name, so an underscore is used instead of the two colons. E.g. "ProcessPageEdit::buildFormContent" would become "ProcessPageEdit_buildFormContent". Step 2 In any of your custom Page class files, add methods named to match the hookable methods you want to target. These custom Page classes should extend DefaultPage. In the example below I'm targeting hookable methods Pages::saveReady and ProcessPageEdit::buildFormContent in the BasicPagePage class. class BasicPagePage extends DefaultPage { public function Pages_saveReady(HookEvent $event) { // Show a message in the PW admin $this->wire()->message("About to save page named: $this->name"); } public function ProcessPageEdit_buildFormContent(HookEvent $event) { /** @var InputfieldWrapper $wrapper */ $wrapper = $event->return; // Add a custom markup field to Page Edit /** @var InputfieldMarkup $f */ $f = $this->wire()->modules->get('InputfieldMarkup'); $f->label = 'My custom markup'; $f->value = 'Hello!'; $wrapper->insertAfter($f, 'title'); } } Step 3 Attach hooks in the places where you normally would. You need to attach a hook for each hookable method you are targeting in your custom Page classes, but the hook code itself is minimal and where multiple Page classes target the same hookable method you only need it attach it once. In the example below I'm attaching hooks in /site/ready.php. $pages->addHookAfter('saveReady', function(HookEvent $event) { /** @var DefaultPage $page */ $page = $event->arguments(0); if($page instanceof DefaultPage) $page->callHookMethods($event); }); $wire->addHookAfter('ProcessPageEdit::buildFormContent', function(HookEvent $event) { /** @var ProcessPageEdit $ppe */ $ppe = $event->object; /** @var DefaultPage $page */ $page = $ppe->getPage(); if($page instanceof DefaultPage) $page->callHookMethods($event); }); Each hook only need to establish a $page object (often this is available as an argument to the hookable method) and if it's an instance of DefaultPage then it simply calls $page->callHookMethods($event), and any relevant hook-targeting methods for that particular Page class will fire. Here's the result of my two hook-targeting methods in BasicPagePage.php when I save a page of this class in Page Edit:
- 1 reply
-
- 6
-
-
-
@ryan, that sounds great, thanks!
-
A couple ProcessWire installations that get special treatment
Robin S replied to Jonathan Lahijani's topic in Dev Talk
Just wondering, have you (or anyone else) ever needed to put anything into those files? What scenarios are they useful in? I use status files for "init", "readySite" and "readyAdmin", and I think once or twice I've used "finished", but I don't have a grasp on what the files you mentioned would be useful for. -
The header action feature is cool, thanks @ryan! Is there a way that a header action can be added through the PHP API rather than through the Inputfield JS API? A couple of reasons this could be useful: 1. I can imagine a module that would add some header action feature to every inputfield (or every inputfield of a particular type) and it would be simpler to do this via a hook before Inputfield::render(). 2. Sometimes the inputfield you want to target doesn't exist in the DOM when the page loads (e.g. Repeater subfields or other Ajax-loaded scenarios) and so the JS code to add the header action would also need to watch for other events like "reloaded" or "ajaxComplete", and again it would be easier to add the action in a before render hook. I was thinking that if the PHP API supported header actions then JS actions could be handled via an "event" property. So something like this... $action->event = 'myAction'; ...which means that clicking the icon would trigger a "myAction" event, and the developer would use JS like... $(document).on('myAction', '.Inputfield', function(event) { const $inputfield = $(event.target); // Do something involving the inputfield... }); And in case the action doesn't need JS but should instead be PHP-based or just link to some URL there could also be an "href" property which would make the icon into a link, perhaps to be used with URL hooks... $action->href = "/my-action/$page->id/$field->name"; ...and... $wire->addHook('/my-action/{page_id}/{field_name}', function($event) { // Do something with $event->page_id and $event->field_name here... }); What do you think?
-
@Mats, I've released the module I mentioned above: If you want to use Media Lister to find an image and then copy it to a field in Page Edit you can follow these steps: Open Media Lister in one browser tab, and Page Edit for the page you want to copy an image to in another browser tab. Find the image you want to copy in Media Lister. Click the link in the Page column - this will open the page that contains the image in Page Edit and will focus the containing field. Use File Mover to select the image you want to copy. Switch to the browser tab that contains the page you want to copy the image to. In the destination field use File Mover to copy across the image you selected in the previous step.
-
File Mover Allows the selection of files or images for moving or copying to a different field. The destination field can be on the same page as the source field or on a different page. Screencast For convenience in the screencast this demonstration shows moving and copying images and files between fields on the same page, but you can also move/copy between pages by following the same process. Usage In any Images or Files field, hover on the field label to show the File Mover icon. Clicking on the icon will reveal the File Mover buttons. If no items are yet selected you'll see a button labelled "Select items to later move or copy". Click the button to enter selection mode. While in selection mode, click one or more images/files to select them. When you have finished selecting items click the "Done" button. Note: you can only select from one Images and one Files field at a time before completing the move/copy step. In the Images or Files field that you want to move/copy the items to, hover the label to show the File Mover icon and click it to reveal the File Mover buttons. When you click the Move or Copy button the page will automatically be saved and the selected items will be moved or copied accordingly. There is also a button to clear the current selection if needed. If you hover on any of the buttons a tooltip shows the filenames of the currently selected items, in case you need a reminder. Configuration There is a field in the module config that defines which roles are allowed to use the File Mover module. If the field is left empty then all roles are allowed. https://github.com/Toutouwai/FileMover https://processwire.com/modules/file-mover/
-
Module: Video embed for YouTube/Vimeo (TextformatterVideoEmbed)
Robin S replied to ryan's topic in Modules/Plugins
@netcarver, this information about unlisted videos may be relevant: https://help.vimeo.com/hc/en-us/articles/12426199699985-Overview-of-video-privacy-settings So for a video with a URL like https://vimeo.com/553329597/04c306a466 the src for the iframe embed will need to include h=04c306a466 in the query string. -
Module: Video embed for YouTube/Vimeo (TextformatterVideoEmbed)
Robin S replied to ryan's topic in Modules/Plugins
@netcarver, your version works great for the two examples I gave in the GitHub issue. Thanks!