All Activity
- Today
-
wbmnfktr started following Black Friday: Z.AI Coding Plan
-
I know that a lot of you enjoy some AI assistance while coding, therefore I wanted to point you to this deal: Compared to Claude Sonnet 4.5 I use Z.AI GLM 4.6 for quite some time now in OpenCode and Cline and I am super happy with it. For the price the results are awesome. No fear to ask for another iteration as usage limits are way higher than in Claude Code, Codex, and probably many others. Works in almost all tools availabe, see here: https://docs.z.ai/scenario-example/develop-tools/claude I am not affiliated with them so there is just this plain text link to their subscribe page. Take a look for yourself. ➡️ z.ai/subscribe
-
@markus-th I checked it out and it works like treat!😀 A huge thank you from Dillingen to Kulmbach!
-
Thanks! Looks like what I need. I check it out.
-
If you dynamically add parameters to your link, you can use them in a hook: $wire->addHookBefore('Pages2Pdf::download', function($event) { /** @var Pages2Pdf $module */ $module = $event->object; $input = $event->wire('input'); $sanitizer = $event->wire('sanitizer'); // 1. Check if we are in your specific scenario // (Optional: limit this to specific templates if needed) $page = $event->arguments(0); if($page->template != 'your-list-template') return; // 2. Retrieve your filter parameters from the URL // Example URL: ?pages2pdf=1&filter_category=music&date=2023 $category = $input->get('filter_category'); $date = $input->get('date'); // 3. Build a safe filename string // It is crucial to sanitize these inputs to avoid invalid filesystem characters $filenameParts = [$page->name]; if($category) { $filenameParts[] = $sanitizer->pageName($category); } if($date) { $filenameParts[] = $sanitizer->pageName($date); } // Add unique ID or timestamp if you want to avoid caching conflicts completely // $filenameParts[] = time(); $newFilename = implode('-', $filenameParts) . '.pdf'; // 4. Overwrite the module setting for this specific request $module->set('filename', $newFilename); }); Important: The hook must be implemented via init.php. It does not work in ready.php. Actually tested it here to add the amount of portions into the filename: https://www.dothiscookingthing.de/rezepte/lachs-spiesse-mit-garnelen-aus-dem-ofen/
-
Ivan Gretsky started following maximus
-
I guess the class needs to be called instead of the native one to implement its execute method. I added this in its construct: foreach ($this->defaultDataAddl as $k => $v) { if (!array_key_exists($k, $this->data)) $this->data[$k] = $v; } $this->addHook('ProcessPageEditImageSelect::execute', $this, '___execute'); and it has the data from the config, and the $this->page assignment changing, but it's not being called during instantiation in context, so it doesn't call any other of the object's methods needed to execute?
-
adrian started following Nested Checkboxes
-
Lovely!
- 1 reply
-
- 1
-
-
- inputfield
- checkboxes
-
(and 2 more)
Tagged with:
-
Robin S started following Nested Checkboxes
-
Nested Checkboxes An inputfield for Page Reference fields that groups options by their parent page, and optionally by grandparent page too. This can help editors understand the grouping of the selectable pages, and also makes it quicker for an editor to select or unselect an entire group of pages. The checkboxes at the parent and grandparent level are not for storing those pages in the field value - only for quickly selecting or unselecting groups of pages at the lowest level of the hierarchy. For example, in the screen recording above the "Cities" Page Reference field allows only pages with the "city" template, and the pages at the country and continent level are not included in the field value. The inputfield is only for use with Page Reference fields because the structure comes from the page tree. Requires PW >= v3.0.248. Configuration For each field that uses the inputfield you have these options: Checkboxes structure: choose "Parents" or "Parents and grandparents". Collapse sections that contain no checked checkboxes: this option makes the inputfield more compact. There are also the standard column width and column quantity options familiar from the InputfieldCheckboxes inputfield. These apply to the selectable pages at the lowest level of the hierarchy, and the structure is arguably more readable when these are left at their defaults. https://github.com/Toutouwai/InputfieldNestedCheckboxes https://processwire.com/modules/inputfield-nested-checkboxes/
- 1 reply
-
- 14
-
-
-
- inputfield
- checkboxes
-
(and 2 more)
Tagged with:
-
ProcessPagesExportImport on multilanguage website
ngrmm replied to spoetnik's topic in General Support
@spoetnik export/import works with multilanguage pages The new website has to have the same languages as the origin website. And you need to have the right paths for parents. - Yesterday
-
I enabled a site module extending a core module: class ProcessPageEditImageSelect_Pickpage extends ProcessPageEditImageSelect implements Module, ConfigurableModule { public static function getModuleinfo() { return [ 'title' => 'Page Edit Image pickfrompage', 'summary' => 'Extends core module: Enables specifying a default page to pick images from if the page being edited has no image fields.', 'autoload' => true, 'permission' => 'page-edit', ]; } public function __construct() { parent::__construct(); } public function init() { parent::init(); bd($this->data); } public function ___execute() {} public function getModuleConfigInputfields(array $data) { $inputfields = $this->wire(new InputfieldWrapper()); $modules = $this->wire()->modules; /** @var InputfieldPageListSelect $f */ $f = $modules->get('InputfieldPageListSelect'); $f->attr('name', 'defaultPage'); $f->attr('value', @$data['defaultPage']); $f->label = $this->_('Default page for images if no image fields'); $f->value = @$data['defaultPage']; $inputfields->add($f); /** @var InputfieldPageListSelect $f */ $f = $modules->get('InputfieldTextarea'); $f->attr('name', 'templateDefaultPage'); $f->attr('value', @$data['templateDefaultPage']); $f->label = $this->_('Default page by template for images if no image fields'); $f->notes = "1 pair of selectors `{template}:{page}` per line, e.g.,: `1:/home/`, or `home:1`, or `contact:template=home`"; $f->value = @$data['templateDefaultPage']; $inputfields->add($f); return $inputfields; } } where the execute method is the one here and the init method returns the core module's data but my execute method has no effect. What's wrong with how I'm doing it? Thank you On pages where it doesn't apply, there's an error notice from the init method of the core module: Modules: Failed to init module: ProcessPageEditImageSelect_Pickpage - No page specified but nothing shows (Tracy debugging) if not autoloaded, and ___execute has no effect either way.
-
Is there a way to generate a unique filename from within the PW page or the Pages2Pdf template file? Background: The user can use a filter on the page and show a list of items, depending on the filters he uses. With a click on a "Generate PDF" button the PDF is created. The problem here is, that I don't want the pagename as a filename(from module settings), but rather a filename that represents the used filters. All parameter are known in the PW template and Pages2Pdf template. Is there a hook? Or a parameter additonal to pages2pdf=1
-
This works to replace the ___execute method to select a default page for pages that don't contain image fields, but hooking from its own module would be a much better & drier approach but I don't know how: public function ___execute() { if($this->config->demo) throw new WireException("Sorry, image editing functions are disabled in demo mode"); if(!$this->page) { $error = "No page provided"; $this->error($error); return "<p>$error</p>"; } $sanitizer = $this->wire()->sanitizer; $modules = $this->wire()->modules; $input = $this->wire()->input; $images = $this->getImages($this->page, $this->page->fields); $pickpage = false; // locate any image fields $imageFields = $this->getImageFields($this->page); if (!wireCount($imageFields)) { $default = @$this->data['defaultPage'] ?: false; if ($default > 0) { $pg = $this->wire->pages->get($default); if ($pg->id > 0) { $this->page = $pg; $pickpage = true; } } // Selector page by template selection in module config: $pgByTpl = @$this->data['templateDefaultPage']; if (!empty($pgByTpl)) { $lines = explode("\n", $pgByTpl); foreach ($lines as $line) { $pcs = explode(':', $line); if (count($pcs) < 2) continue; $tpl = wire('templates')->get($pcs[0]); $pg = wire('pages')->get($pcs[1]); if ($tpl->id == $this->editorPage->template->id) { $pickpage = true; $this->page = $pg; break; } } } if ($pickpage) $this->message('Default images selection page set in site module '.get_class($this)); $images = $this->getImages($this->page, $this->page->fields); // locate image fields from default $imageFields = $this->getImageFields($this->page); } if(wireCount($imageFields)) { $imageFieldNames = implode(',', array_keys($imageFields)); /** @var InputfieldButton $btn */ $btn = $modules->get('InputfieldButton'); $uploadOnlyMode = "$this->page" === "$this->editorPage" ? 1 : 2; $btn->href = "../edit/?modal=1&id={$this->page->id}&fields=$imageFieldNames&uploadOnlyMode=$uploadOnlyMode"; $btn->value = $this->_('Upload Image'); $btn->addClass('upload pw-modal-button pw-modal-button-visible'); $btn->icon = 'upload'; $changes = $input->get('changes'); if($changes) { foreach(explode(',', $changes) as $name) { $name = $sanitizer->fieldName($name); $field = $this->wire()->fields->get($name); if(!$field) continue; $out .= "<script>refreshPageEditField('$name');</script>"; } } } else $btn = null; if($this->input->get('file')) return $this->executeEdit(); // initialization of variables was here $out = ''; if(wireCount($images)) { $winwidth = (int) $input->get('winwidth'); $in = $modules->get('InputfieldImage'); /** @var InputfieldImage $in */ $in->set('adminThumbs', true); $lastFieldLabel = ''; $numImageFields = 0; foreach($images as $image) { /** @var PageImage $image */ $fieldLabels = array(); $parentFields = $image->get('_parentFields'); if(!is_array($parentFields)) $parentFields = array(); foreach($parentFields as $parentField) { $fieldLabels[] = $parentField->getLabel(); } $fieldLabels[] = $image->field->getLabel(); $fieldLabel = implode(' > ', $fieldLabels); if($fieldLabel != $lastFieldLabel) { $numImageFields++; $out .= "\n\t<li class='select_images_field_label detail'>" . $sanitizer->entities($fieldLabel) . "</li>"; } $lastFieldLabel = $fieldLabel; if($this->noThumbs) { $width = $image->width(); $alt = $sanitizer->entities1($image->description); if($width > $this->maxImageWidth) $width = $this->maxImageWidth; $img = "<img src='$image->URL' width='$width' alt=\"$alt\" />"; } else { $image->set('_requireHeight', true); // recognized by InputfieldImage $info = $in->getAdminThumb($image); $img = $info['markup']; } $out .= "\n\t<li><a href='./edit?file={$image->page->id},{$image->basename}" . "&modal=1&id={$this->page->id}&winwidth=$winwidth'>$img</a></li>"; } $class = $this->noThumbs ? "" : "thumbs"; if($numImageFields > 1) $class = trim("$class multifield"); $out = "\n<ul id='select_images' class='$class'>$out\n</ul>"; } /** @var InputfieldForm $form */ $form = $modules->get("InputfieldForm"); $form->action = "./"; $form->method = "get"; /** @var InputfieldPageListSelect $field */ $field = $modules->get("InputfieldPageListSelect"); $field->label = $this->_("Images on Page:") . ' ' . $this->page->get("title") . " (" . $this->page->path . ")"; // Headline for page selection, precedes current page title/url $field->description = $this->_("If you would like to select images from another page, select the page below."); // Instruction on how to select another page $field->attr('id+name', 'page_id'); $field->value = $this->page->id; $field->parent_id = 0; $field->collapsed = wireCount($images) ? Inputfield::collapsedYes : Inputfield::collapsedNo; $field->required = true; $form->append($field); // getImageFields was here $out = $form->render() . $out; if($btn) $out .= $btn->render(); return "<div id='ProcessPageEditImageSelect'>" . $out . "\n</div>"; } Add these fields to getModuleConfigInputFields method: /** @var InputfieldPageListSelect $f */ $f = $modules->get('InputfieldPageListSelect'); $f->attr('name', 'defaultPage'); $f->attr('value', @$data['defaultPage']); $f->label = $this->_('Default Page if no image fields'); $f->value = @$data['defaultPage']; $inputfields->add($f); /** @var InputfieldPageListSelect $f */ $f = $modules->get('InputfieldTextarea'); $f->attr('name', 'templateDefaultPage'); $f->attr('value', @$data['templateDefaultPage']); $f->label = $this->_('Default page by template for images if no image fields'); $f->notes = "1 pair of selectors `{template}:{page}` per line, e.g.,: `1:/home/`, or `home:1`, or `contact:template=home`"; $f->value = @$data['templateDefaultPage']; $inputfields->add($f); Copy the entire module directory into your site modules directory.
-
ausblick started following Community or forum solutions
-
A customer asked for a simple solution for setting up something like a forum-style community, it's very vague in requirement. Has anyone done something like this with Processwire, or do you have any recommendations for self-hosted solutions which could be used together with a PW site?
-
This does not work: // Prevent textual input from changing, e.g., if '2020-00-01' is entered, erase $this->addHookAfter('InputfieldDatetime::processInput', function(HookEvent $event) { // Get the object the event occurred on, if needed $InputfieldDatetime = $event->object; // An 'after' hook can retrieve and/or modify the return value $return = $event->return; // Get values of arguments sent to hook (if needed) $input = $event->arguments(0); $page = $return->hasPage; $fname = $InputfieldDatetime->attributes['name']; $val = $input[$fname]; // $old = $return->hasPage->$fname; // bd(date('Y-m-d', $old)); /* Your code here, perhaps modifying the return value */ $fmt = $page->getField($fname)->dateInputFormat; // bd($fmt); $new = date('Y-m-d', strtotime($val)); if ($val == $new) return; // bd($val, $new); $this->wire->error( sprintf("Skipping field `%s` date string input: '%s' (format='%s')", $page->getField($fname)->get('label|name'), $val, $fmt) ); $return->hasPage->$fname = ''; // clear value // Populate back return value, if you have modified it $event->return = $return; }); I guess it might be better hook sanitizeValue in the Fieldtype? Sorry I'm getting very confused. The $event->return->hasPage->$fname has worked in prior tests, but I'm also confused because initially that property is set to the old value.
- Last week
-
Why does this hook require saving 2x before applying the updated return value? function hookAfter_processInput(HookEvent $event) { // Get values of arguments sent to hook (if needed) $input = $event->arguments(0); $page = $event->return->hasPage; /* Your code here, perhaps modifying the return value */ $fname = $event->object->attributes['name']; $field = wire('fields')->get($fname); if (is_null($field) || !in_array($field->id, $this->data['fields'])) return; $newvalue = $input->$fname; $existing = $event->return->hasPage->$fname; $ok = $this->validate_date($newvalue, $existing); if ($ok) return; else { // Invalid: $event->return->hasPage->$fname = ''; // need to save 2x to affect ?? // $page->of(false); $page->setAndSave($fname, '', ['noHooks' => true]); // nothing $this->error("Invalid date text input for field `".$page->getField($field)->get('label|name')."`: $newvalue"); } }
-
pmichaelis started following TextformatterAutoAnchor
-
Ok, there ist an error in Line 54. It should be like this: function ($m) use ($hs) { $slug = strtolower(str_replace(' ', '-', $m[2])); $anchor = $this->anchorEnabled ? '<a class="' . $this->anchorClass . '" href="#' . $slug . '">' . $this->anchorContent . '</a>' : ''; return '<'.$m[1].' id="'.$slug.'">' . $anchor . $m[2] . '</'.$m[1].'>'; }, Thanks
-
PW date fields allow date strings such as "2020-00-01", as allowed by strtotime PHP function, but they will change upon save, with no warning. This erases the value on 1st save if it would otherwise be changed, but if it's valid, erases on 2nd save: $this->addHookAfter('InputfieldDatetime::processInput', function(HookEvent $event) { // Get the object the event occurred on, if needed $InputfieldDatetime = $event->object; // An 'after' hook can retrieve and/or modify the return value $return = $event->return; // Get values of arguments sent to hook (if needed) $input = $event->arguments(0); $fname = $InputfieldDatetime->attributes['name']; // 'dated'; $new = $input[$fname]; $old = $return->hasPage->$fname; /* Your code here, perhaps modifying the return value */ $return->hasPage->$fname = ''; // Populate back return value, if you have modified it $event->return = $return; });
-
Whats possible, switching default language on existing sites
olafgleba replied to olafgleba's topic in Multi-Language Support
Hi @Sergio, i fully agree, this is tough to achieve. Because of the exceptions. As far as i am told joomla for example supposed to handle this a bit more easy. However they do it, i do not want to use Joomla anyway. Finally i wrote a little script which helps me to swap content. And - important for me - it includes most of the available multilingual field types. For my current project (which triggered the posting) this means i got this default language swapping thing ready in less than 5 hours. Instead of several days. Maybe this is also helpful for others... https://gist.github.com/olafgleba/1e1c33d8ad3fc22961dfba33a6ee2f36 -
Looking for easiest way to turn off autocomplete for admin login prompt
adrian replied to gRegor's topic in General Support
It might be ugly and not recommended, but I set the autocomplete value to a uniqid() value and it seems to work to actually prevent browsers autocompleting fields. -
Looking for easiest way to turn off autocomplete for admin login prompt
gRegor replied to gRegor's topic in General Support
I also learned that this mostly doesn't matter in modern browsers, since they'll ignore it. But hey, this security scan will pass now! 🤣 -
Looking for easiest way to turn off autocomplete for admin login prompt
gRegor replied to gRegor's topic in General Support
Hooking after that method wasn't as bad as I was thinking, so this is my current quick solution: wire()->addHookAfter('ProcessLogin::buildLoginForm', function(HookEvent $event) { $form = $event->return; $form->get('login_name')->attr('autocomplete', 'off'); $form->get('login_pass')->attr('autocomplete', 'off'); $event->return = $form; }); -
We have a site that is having some security scans run and they want us to disable autocomplete on the username/password field for the ProcessWire login form, using the autocomplete="off" attribute. I see that buildLoginForm is hookable, but would prefer to avoid rebuilding the entire form. Is there another hook (or simpler way that I'm forgetting) to target those two input fields and add the attribute? I may also just manually update the core file and make a note to re-apply it when we update PW, but fingers crossed there's a simpler way I'm missing. Depending on the results of this thread, I might file a feature request to make it a configurable option. Thanks for any help!