-
Posts
5,007 -
Joined
-
Days Won
333
Everything posted by Robin S
-
I think person 2 sees page B in 2 seconds. I did a kind of test... I have two pages and two templates: page "Yellow" uses template "colour" and page "A Basic Page" uses template "basic_page". There's nothing special about the template file for basic_page but in the template file for colour I have: sleep(30); If I load page Yellow in one tab and then immediately load A Basic Page in another tab then I have to wait 30 seconds to view either page. But if I load page Yellow in one tab and then immediately load A Basic Page in an incognito window (which will have a different session) then the page loads immediately.
-
You can do a find() but with a limit of 1. This will be just as efficient as findOne(), the only thing is that it will return a PageArray so use first() to get the item as a Page. $results = $pages->find("template=template-news, sort=created, limit=1, start=1"); if($results->count) { $title = $results->first()->title; }
-
anybody could show an example of how to use $config->ajax ?
Robin S replied to adrianmak's topic in Getting Started
Should be: return $this->halt(); -
Disable selected/used options (Page references) in Admin
Robin S replied to flash's topic in General Support
Yes, it will break. I gave the link to the blog post introducing "owner" selectors in the code, but here it is again: https://processwire.com/blog/posts/processwire-3.0.95-core-updates/ So this selector... template=colour, select_colour.owner.template=animal, select_colour.owner.id!=$page->id ...is saying... Match pages where: the page template is "colour" and the page is selected in a field named "select_colour" and the page containing the select_colour field is using the template "animal" and the page containing the select_colour field is not the page represented by the $page variable (i.e. the page that is currently open in Page Edit)- 3 replies
-
- 4
-
-
-
- select options
- hooks
-
(and 1 more)
Tagged with:
-
Disable selected/used options (Page references) in Admin
Robin S replied to flash's topic in General Support
This would be easier to explain in your context if you had included all the relevant field and template names in your post. So here is an example that you can adapt to your scenario... I have a Page Reference field named "select_colour" and generally the selectable pages are those that have the template "colour". I add this field to the template "animal", so that on animal pages I have a field to choose the colour of the animal. But in the select_colour field I only want to show colours that have not been selected on any other animal page. So in the settings for field select_colour I only use the "Custom PHP code" option for "Selectable pages" and add the following code to /site/ready.php: $wire->addHookAfter('InputfieldPage::getSelectablePages', function($event) { $page = $event->arguments(0); // Define selectable pages for the select_colour field if($event->object->hasField == 'select_colour') { /** @var Pages $pages */ $pages = $event->wire()->pages; // Find colours that are already selected on another animal page using "owner" selector // https://processwire.com/blog/posts/processwire-3.0.95-core-updates/ $already_selected_on_another_page = $pages->find("template=colour, select_colour.owner.template=animal, select_colour.owner.id!=$page->id"); // Return colour pages that aren't already selected on another page $event->return = $event->pages->find("template=colour, id!=$already_selected_on_another_page"); } }); Another possible approach is to loop over all the colour pages and remove those that are referenced on another page, but this is less specific because it will remove colours that have been referenced in any field, not just the select_colour field. $wire->addHookAfter('InputfieldPage::getSelectablePages', function($event) { $page = $event->arguments(0); // Define selectable pages for the select_colour field if($event->object->hasField == 'select_colour') { /** @var Pages $pages */ $pages = $event->wire()->pages; // All colours $colours = $pages->find("template=colour"); foreach($colours as $colour) { // Remove colour if any other pages are referencing it if($colour->numReferences) $colours->remove($colour); } // Return the filtered colours $event->return = $colours; } });- 3 replies
-
- 2
-
-
-
- select options
- hooks
-
(and 1 more)
Tagged with:
-
Page autocomplete query is getting 403 error
Robin S replied to Frank Vèssia's topic in API & Templates
Might be mod_security false positive. -
How do I use filters to search a string with Double Quotes?
Robin S replied to GradDev's topic in General Support
The *= operator isn't the right one to use because it doesn't match partial strings at the start. If you use the %= operator I think it will work. -
ProcessPageLister and pagination in a module
Robin S replied to MSP01's topic in Module/Plugin Development
You can set the base URL for MarkupPagerNav that is used for the Lister pagination. public function ___executeBrowser() { // Before MarkupPagerNav is rendered $this->addHookBefore('MarkupPagerNav::render', function(HookEvent $event) { $pager = $event->object; // Set base URL to the current page + URL segment $pager->setBaseUrl($this->wire()->page->url . 'browser/'); }); $browser = $this->modules->get('ProcessPageLister'); return $browser->execute(); } -
Something that would be worth a try... Use a saveReady hook to put all the tags as space-separated values into a (hidden) textarea field. Then use the **= operator with the tags string of the current project. From the docs:
-
Thanks. It looks like the legacy Default and Reno themes don't call AdminThemeFramework::getPrimaryNavArray() so the hooks added by this module don't have any effect. Therefore AdminThemeUikit is a requirement - I've updated the module install requirements and readme to reflect this.
-
I think a couple of ways are possible. One is similar to what @horst suggested, but you can use the special created timestamp of 10 to more exactly identify temporary images: $page->getUnformatted('images')->find('created!=10') There are also the public Pagefile::isTemp() and Pagefiles::isTemp() methods that can be used to check if a particular Pagefile/Pageimage is temporary or not. Another way would be to set the outputFormat property of the images field to array before you get the field value so that the value is standardised for single and multiple image fields. $page->getField('image')->outputFormat = FieldtypeFile::outputFormatArray; // Now use $page->image
-
It's not different between frontend and admin - I just used that in my screenshot to show the as-yet-unsaved image. So you do have to watch out for it in template code too. Suppose you have a busy site and you're using getUnformatted() in your template code. If a website editor accidentally uploads the wrong image (you can imagine some worst-case scenarios here!) but then notices before they save the page, that temporary image might have already been displayed and viewed on the frontend via getUnformatted().
-
True, but just want to point out there can be occasional "gotchas" when doing this because PW sometimes returns things that may be unexpected in the unformatted value. For example, the unformatted value of a Page Reference field will include unpublished pages and the unformatted value of a Repeater field can include "ready" items that are not populated in Page Edit. And in the case being discussed here the unformatted value of an images field can include "temporary" images that have been uploaded in Page Edit but the page has not yet been saved. So in the example below the frog image is saved but the fish image has just been uploaded but not yet saved, and might yet be abandoned before saving because of being a mistake or unwanted. Yet it appears in the unformatted value:
-
This is great, thanks @MoritzLost for taking the time to write this up and share it with us. Although I don't agree with all the points (e.g. I think the PW emphasis on tree/hierarchy is a pro rather than a con) it's really interesting and useful to get an insight into how other CMSs solve problems and bring value to their users. I'm not sure if Ryan feels the same, but speaking as a person who enjoys developing modules and tools: eventually the limiting factor becomes ideas. That is, the will is there to keep creating useful things but the challenge is knowing what will consititute a useful thing to the audience. So getting user requests and descriptions of pain points is what's needed to keep the wheels turning. Regarding the Craft/PW comparison... You did state this clearly at the outset but I want to really emphasise this because it's such a major factor: the difference in cost between Craft CMS and ProcessWire is massive. I did a conservative estimate of the net income I would have lost as a solo developer over the last 5 years if I had have built all of my projects with Craft/plugins rather than PW/modules. It's approximately USD $43,000. For someone in my position that is just huge. Maybe I could have successfully added those Craft costs into the quotes but then maybe I would have lost many bids if I had done that. In general, if you think about the difference between something that's free and something that costs upwards of $299 it should be no surprise if there are some advantages to the thing that costs you $299. In fact it would be outrageous if you paid $299 for a thing and it was worse or no better than a free alternative. Suppose I would like a drink and I'm choosing between the free option (a bottle of tap water) and the $299 option (a bottle of Dom Pérignon champagne). The champagne should be a better experience than the tap water. That's why it costs $299. But when we bring this analogy back to PW/Craft, it's like ordering a bottle of tap water and then having the pleasant surprise that it's a very nice méthode traditionnelle. Maybe not quite as good as the Dom Pérignon, but at $0 that's extraodinary value and a lot of people would say it's the better choice for them. For the types of projects I use PW for, I have the occassional niggle but it's very minor stuff. Although I'd love to see PW match all the features of all its commercial competitors I also think it's reasonable to take the position that a free product is not going to be all things to all people. I actually wish there was more clarity about the kind of developer and kind of projects PW is targetted towards. In my opinion, narrowing the focus of PW and clearly communicating this at processwire.com would be a step in the right direction. Even if this was somehow expressed in negative terms, e.g. Don't know what a foreach loop is? Other tools will suit you better than PW. Looking for themes? Other tools will suit you better than PW. Looking for "a plugin for everything"? Other tools will suit you better than PW. Part of a large development team? Other tools will suit you better than PW. Building "enterprise" applications? Other tools will suit you better than PW. Working on projects with budgets in the mid-five-figures and higher? Don't be such a cheapskate, get out your chequebook because other tools will suit you better than PW. The way I see it, the person who will get most value and satisfaction from PW is a solo developer with at least intermediate experience (or a willingness to get there), working on projects that have demands that go beyond the basic but where the budget is somewhat limited. The main benefit that I get from PW is that I can build surprisingly powerful sites and build them fast so the hours/costs are kept down. This is the PW sweetspot and I think we should lean into it.
- 23 replies
-
- 23
-
-
@rick messaged me asking for advice about how to use dependent selects in a module config. That is, where the options of one select change depending on what is selected in a another select. I thought others might also find this info useful so I've put together a demonstration module: https://github.com/Toutouwai/DemoDependentSelects The general idea is that the dependent (target) select needs to include all the possible options, and then the options are hidden or shown by JavaScript depending on what is selected in the source select. See the source code for comments.
- 6 replies
-
- 15
-
-
As far as I can see it's working as per the documentation for the method - wireRenderFile() just calls $files->render(): https://processwire.com/api/ref/wire-file-tools/render/ If you do set an additional allowed path (which in your case you don't need to do because files within /site/modules/ are allowed by default) then the allowedPaths option needs to be an array of extra allowed paths.
-
@MarkE, the SelectizeAll module itself doesn't get involved with the change event. The problem is likely to be an incompatibility between the Selectize and htmx libraries. I can't do much about that, but in the newly released SelectizeAll v0.1.2 you can disable the module for an inputfield by giving it a "no-selectize" class. So when you create a select or AsmSelect inputfield in your module you would add a line like this... $inputfield->addClass('no-selectize'); ...and then if the two modules are used together your selects/AsmSelects won't be "selectized".
-
Need a bit of help with .htaccess redirects in PW
Robin S replied to John W.'s topic in General Support
As a side note, if the new site is an enhancement of the old site (rather than a totally new site built from scratch) and you have PagePathHistory installed (a must-have on every site) then you should be able to move the existing news articles to any new path and PW will automatically redirect visitors from the old location to the new location. But if it's a new site from scratch where the IDs of historical news articles are now different to the old site, then I suggest doing the redirects using the PW API rather than manually entering a long list of redirects in htaccess. There are different ways you could do it. You could hook ProcessPageView::pageNotFound or use URL segments on the template for the /news/ page. Both would be fine, but I think the latter is a bit easier. Enable URL segments on the template for the /news/ page, and at the top of the template file: // If there is a URL segment (i.e. no real page exists at the requested URL) if($input->urlSegment1) { // Check for a published news article with that name $article = $pages->findOne("template=your_news_article_template, name=$input->urlSegment1"); if($article->id) { // If match found then redirect $session->redirect($article->url); } else { // Otherwise throw 404 throw new Wire404Exception(); } } // Only one segment is allowed if($input->urlSegment2) throw new Wire404Exception(); -
[Solved] AsmSelect -> Default value (for "roles")
Robin S replied to Ralf's topic in General Support
-
[Solved] Sorting roles - alphabetically (in user template)
Robin S replied to Ralf's topic in Themes and Profiles
Open the page tree to Admin > Access > Roles and open the Roles page for editing. Temporarily uncheck the "Locked" status on the Settings tab and save. On the Children tab, in Sort Settings, choose "name" in the "Children are sorted by" dropdown. Save. Now re-check the "Locked" status and save.- 1 reply
-
- 1
-
-
I don't think that will work because the page will also be saved immediately after it is created and so the hook would just delete it before it can be edited. I suggest you just clean up abandoned pages on a regular shedule using LazyCron. First install the module at Modules > Install > LazyCron. Then add a hook like this to /site/ready.php: // Once per day $wire->addHook('LazyCron::everyDay', function(HookEvent $event) { // Find pages that appear to be abandoned because they still have the default title 24 hours after creation $time = strtotime('-24 hours'); $abandoned_pages = $event->wire()->pages->find("template=your_template, title='New Owner', created>$time, include=all"); // Trash the abandoned pages foreach($abandoned_pages as $abandoned_page) { $abandoned_page->trash(); } }); You could delete the pages if you like but maybe safer to trash them and then just empty the trash once in a while.
-
How do I make ProcessWire display the 404 "page not found" page?
Robin S replied to ryan's topic in API & Templates
I'm surprised you have so many template variables to declare. You might like to take a look at Markup Regions because that should greatly reduce the number of variables you need to hold markup. Does the below work? (P.S. parentheses are redundant for include, include_once, etc because they are expressions rather than functions) include_once './_func.php'; include './_header.php'; include './_sidebar.php'; include './_footer.php'; include './_buttons.php'; -
I use generic words and numbers for field names, e.g. text_1, textarea_1. I really wish I didn't have to do this and could use descriptive names for fields, but the problem is that I almost always want to reuse fields for different purposes on different templates. So while a name like "city" is more meaningful than "text_1", I think it's much more confusing to call $page->city in some template when that field is actually holding a person's job title or something totally unrelated to "city". What I'd like is for PW to support field aliases, so that in the context of the "store" template I can give text_1 the alias of "city" and in the "employee" template I can give text_1 the alias of "job_title". Field aliases would need to be unique and in practice you could refer to a field by any of its aliases in any place in your code. I have an open request for this: https://github.com/processwire/processwire-requests/issues/154 Ryan seemed supportive at the time but it hasn't progressed anywhere since 2018. If anyone else would like this feature please "vote" for it with a thumbs-up at GitHub. I've also created a module that would allow aliases to be used but it can't be released due to issues with FileCompiler: https://github.com/processwire/processwire-issues/issues/882
-
I think a better way is to skip the Add Page step and then make sure the title is always automatically derived from your two fields when they are populated. There are a few steps involved, illustrated here with case that is different to yours but you can see the similarity. In this example the template of the parent page that new pages are added under is named "creatures", and the child template (where the title is being set automatically) is named "creature". 1. For the "creatures" template, make sure that it allows a single template for children: the "creature" template. 2. For the "creatures" template, in the "Name format for children" setting enter "Y/m/d H:i:s". This will allow the "Add Page" step to be skipped and will automatically set the page name to the current date and time. We will later change the name in a hook. 3. Optional but recommended: for the "creature" template, choose the "creatures" template as "Allowed template(s) for parent". This lets you quickly add child pages via Pages > Add New and also ensures that new pages can't be added in the wrong place. 4. For the "creature" template, edit the Title field in the template context and set the visibility to "Open when populated + Closed when blank + Locked (not editable)". The title is going to be set automatically so editors should not be able to change it manually. 5. Add hooks like the ones below to /site/ready.php. // Pages::added $pages->addHookAfter('added', function(HookEvent $event) { /** @var Page $page */ $page = $event->arguments(0); // Creature if($page->template == 'creature') { // This is a newly added page so the fields that make up the title won't be populated yet $page->setAndSave('title', 'New creature'); } }); // Pages::saveReady $pages->addHookAfter('saveReady', function(HookEvent $event) { /** @var Page $page */ $page = $event->arguments(0); /** @var Pages $pages */ $pages = $event->object; // Creature if($page->template == 'creature') { // Return early if the page is in the trash (thanks @taotoo) if($page->isTrash) return; // If the fields that make up the title are populated if($page->colour->id && $page->animal->id) { // Set the title $page->title = "{$page->colour->title} {$page->animal->title}"; // Sanitize the title as a page name $name = $event->wire()->sanitizer->pageName($page->title, true); // Set the page name while making sure it auto-increments if there is a sibling page with the same title $page->name = $pages->names()->uniquePageName($name, $page); } } }); Result:
- 5 replies
-
- 10
-