Jump to content

Robin S

  • Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by Robin S

  1. It's a bug - please open an issue on GitHub to bring it to Ryan's attention. The URL segments array has a 1-based index but this is lost to a 0-based index when the array is reversed here: https://github.com/processwire/processwire/blob/ee4c46a442f09cf81f9e1800c5fdfcd1176f538a/wire/core/WireInput.php#L576 As a workaround for now you could do this: $segments = $input->urlSegments; $last_segment = end($segments);
  2. I've added a note about the incompatibility with LanguageSupportPageNames to the module readme. I'm not sure how LanguageSupportPageNames works exactly or what part it plays in multi-language functionality but I can see that, like Custom Paths, it hooks Page::path and performs redirects before pages are viewed. This makes me think it may be impossible for these two modules to work together, but if anyone with multi-language experience can see a solution I'd be glad to apply any fix.
  3. v0.1.4 released: this version adds the functionality that if a visitor attempts to view a page at its real path (maybe they have it bookmarked or are following an old link) they are redirected to the custom path.
  4. I can't reproduce that. If I set the custom path to something that it is in PagePathHistory for the page then I can successfully view the page at the custom path without being redirected by PagePathHistory. If you can give me some steps to follow that will consistently reproduce the problem on a clean installation let me know and I'll try and investigate further.
  5. You can't use the PW core image plugin because in FormBuilder there is no page to store images in. But I have just released v0.1.2 which lets you configure a global toolbar for all Markup CKEditor fields. If you add "Image" to the toolbar setting you can use the standard CKEditor image plugin to insert an image by URL.
  6. Welcome to the PW forums @j-w! The thing you're asking about doesn't relate to PW as such - it's more an Apache question. As far as I know there isn't anything in the PW htaccess that would prevent you from using basic auth. I don't think you can use <Location> in htaccess - that has to go in your virtual hosts config file. If you want to use htaccess then this Stack Overflow answer might help (it worked for me when I tested it): https://stackoverflow.com/a/14605066/1036672 Or see the answer to the same question for a virtual hosts example: https://stackoverflow.com/a/26980313/1036672
  7. For anyone interested in how you might do this by modifying the SQL query that's created from a PW selector, here is one way: In your template file or wherever: // A field name is supplied in a custom "empty_first" item in the $options array // The field name supplied here must also be used as a sort value in the selector string $items = $pages->find('template=news_item, sort=-date_1, limit=20', ['empty_first' => 'date_1']); In /site/ready.php: $wire->addHookAfter('PageFinder::getQuery', function(HookEvent $event) { $options = $event->arguments(1); // Return early if there is no "empty_first" item in the $options array if(!isset($options['empty_first'])) return; // Get the DatabaseQuerySelect object /** @var DatabaseQuerySelect $dqs */ $dqs = $event->return; // Prepend a custom ORDER BY $dqs->orderBy("ISNULL(_sort_{$options['empty_first']}.data) DESC", true); // Return the modified query $event->return = $dqs; });
  8. When you do a $pages->find($selector) operation your selector is translated by PW into an SQL query. It's a bit of a simplification (because there some exceptions for things like "count"), but essentially this means that you can only sort by values that correspond to columns in the database. So for example you can sort by "title" because that corresponds to a column in the database. But you can't use some logic at runtime to conditionally build up a custom value based on various properties of each page and then sort by that because there is no corresponding column for that custom value. So your options are... 1. If the only way to work out the sort is to iterate over all the pages in a PageArray and you then want to paginate that sorted PageArray, you can do something like this: // $results is a PageArray that you have applied some custom sorting to at runtime $total = $results->count(); $limit = 10; // Convert page number to be zero-based $page_num = $input->pageNum - 1; $start = $page_num * $limit; // Get the slice of results for the current pagination $results = $results->slice($start, $limit); $results->setStart($start)->setLimit($limit)->setTotal($total); foreach($results as $result) { // Output result } echo $results->renderPager(); But because you have to load all the results into memory in order to sort them, this strategy is not going to scale well with large numbers of results. 2. If the sorting doesn't actually depend on dynamic input you can think about using an integer field in the template to store a weighting that you can sort by in a $pages->find() selector. You would use a saveReady hook to populate this sort_weighting field according to other field values in the page. You can use the API to loop over all your book pages to set a sort_weighting value initially. The sorting in your code didn't quite make sense to me so you'll probably need to adapt this to suit, but the general idea is this: // In /site/ready.php $pages->addHookAfter('saveReady', function(HookEvent $event) { $page = $event->arguments(0); if($page->template == 'book') { $sort_weighting = 0; if(!$page->subject) $sort_weighting = -1; if(!$page->subject && !$page->author) $sort_weighting = -2; $page->sort_weighting = $sort_weighting; } }); // In your template code $results = $pages->find("template=book, sort=sort_weighting, sort=subject, sort=cleanauthor, sort=cleantitle, limit=50");
  9. Set the form action to the be the same URL that renders the form. Or just don't set the action for the form because the current URL is the default action. Look for the name of the submit button ("submit" is the default) in $input->post and process the form input when it is present. Example: public function ___execute() { $input = $this->wire('input'); $modules = $this->wire('modules'); /** @var InputfieldForm $form */ $form = $modules->get('InputfieldForm'); /** @var InputfieldText $f */ $f = $modules->get('InputfieldText'); $f->name = 'greeting'; $f->label = 'Greeting'; $form->add($f); /** @var InputfieldText $f */ $f = $modules->get('InputfieldText'); $f->name = 'name'; $f->label = 'Name'; $f->required = true; $form->add($f); /** @var InputfieldSubmit $f */ $s = $modules->get('InputfieldSubmit'); $form->add($s); // Process input if form was submitted if($input->post('submit')) { // Core form processing (required, etc) $form->processInput($input->post); // Custom processing $greeting = $form->getChildByName('greeting'); if($greeting->value !== 'Hello') { $greeting->error('Please say "Hello"'); } // Redirect (Post/Redirect/Get pattern) $this->wire('session')->redirect('./'); } return $form->render(); }
  10. Ryan's comment in the fixed GitHub issue: Therefore, to include unpublished pages you must get the unformatted value of the field: $items = $page->getUnformatted('your_page_reference_field_name');
  11. Could you please show the code you are using to access your custom image fields in your template file, and explain what error/problem happens when you try to do this? I might be misunderstanding but it seems like you are getting hung up on the inputfield names in Page Edit when really you don't have to worry about these. You just access the custom fields from the Pageimage object using the names of the fields that you added to the template. So if you added the "title" field to the template that defines your custom image fields you access that field as $image->title, where $image is the Pageimage object from your image field. The relevant part of the introductory blog post is:
  12. It's partly covered by the icons but it's just the field name: "image"
  13. Are you referring to the input names when you inspect them in your browser dev tools? It's normal for them to have that suffix - I expect it's to distinguish between inputs when there are multiple images in the same Page Edit form. But you should still be able to access the custom image fields without any suffix. It's working for me - see screenshot:
  14. There is this module made by @bernhard:
  15. It doesn't hurt to do this, but most of the time it's not necessary. When you change the output formatting state for a page, this only lasts for the current request. On the next request PW is going to automatically set the output formatting state depending on the context the code that's executing - to quote the docs, "By default, output formatting is turned on on the front-end of the site, and off on the back-end (admin) of the site." So probably the only case you would need to explicitly turn output formatting on after you have turned it off is if you had some code in a template file that was setting values to $page, and then later in that template file you are also outputting $page values to the front-end. But I think that would be quite rare, because most of the time when you are using the API to set page values you'll be doing that from a script separate from any template file that's outputting to the public front-end.
  16. There's an open issue in the old PW repo about the inability to sort by the "sort" subfield of a Select Options field: https://github.com/ryancramerdesign/ProcessWire/issues/2049 Looks like the "title" and "value" subfields don't work either. I've opened a new issue here: https://github.com/processwire/processwire-issues/issues/1231 @rash, in the meantime, if sorting by title is critical to your site perhaps the best thing would be to replace your Select Options field with a Page Reference field. When you've created the Page Reference field and added it to the relevant templates you could execute some API code in the Tracy Debugger console to transfer the Select Options field values to the Page Reference field (match option title to page title).
  17. Everything is possible in ProcessWire. 😉 For example, you could create a "max_children" integer field and add it to the template of any pages you want to limit the children of. Then use this hook in /site/ready.php: $wire->addHookAfter('Page::addable', function (HookEvent $event) { /** @var Page $page */ $page = $event->object; // If page has the "max_children" field, and the field isn't empty, and the number of children is at the maximum... if($page->hasField('max_children') && $page->max_children !== '' && $page->numChildren >= $page->max_children) { // ...then don't allow child pages to be added $event->return = false; } }); But this is not to say it's the best solution, just that it's possible. 🙂
  18. I agree that the concept of output formatting could be better documented - questions about it come up regularly in the forums. @ryan, it would be helpful if there was a page explaining output formatting in the "Getting started" documentation. The key thing to take onboard is that if you are going to be setting and saving values to a page, you must set output formatting to false before you do anything relating to that setting and saving. In your case, you are getting the value of a field that you will later modify... /* get the images object array for the Page */ $myPageImg = $page->images; ...before you have turned off output formatting for $page. If you do... /* get the images object array for the Page */ $page->of(false); $myPageImg = $page->images; ...then when you save $page, the changes you make to $myPageImg will be saved to the field value. Another thing: assigning a field value to a variable like this ($myPageImg = $page->images) when you intend to later modify that field value is probably not a good idea. You can get away with it for an images field because the value of an images field is an object, and objects in PHP are assigned by reference. But the same would not be true for any field whose value is not an object, e.g. a text field. When you assign the value of a text field to a variable you are assigning by value, meaning that changes made to the variable are not simultaneously applied to the field value. To illustrate... $page->of(false); $images = $page->images; // $images is an object assigned by reference $images->add('https://www.site.com/image.jpg'); $page->save(); // Changes to $images will be saved to $page->images $page->of(false); $headline = $page->headline; // $headline is a string assigned by value $headline .= ' foo'; $page->save(); // Changes to $headline will NOT be saved to $page->headline So in your case, there's really no benefit to assigning $myPageImg = $page->images near the start of your code - it just increases the chance of confusion. You'd be better to make changes to $page->images directly: //... $page->of(false); while($file_headers[0] == 'HTTP/1.1 200 OK'){ $page->images->add($file); $affixnum++; $file = 'https://www.site.com/pictures/' . $page->title . "_" . $affixnum . $extension; $file_headers = @get_headers($file); $page->save(); echo $file . " added. <br />"; } //...
  19. You can use a limit in a children() selector: https://processwire.com/docs/selectors/#limit For example, this... $sections = $your_page->children("limit=3"); ...will limit $sections to the first 3 children of $your_page.
  20. For /site/ready.php, but you can adapt for use in a module if needed: $wire->addHookBefore('InputfieldPageTable::render', function (HookEvent $event) { /** @var InputfieldPageTable $table */ $table = $event->object; $field = $table->hasField; // Get array of table template data in the form 'label' => 'icon' $table_template_ids = $field->template_id; $table_templates = $event->wire('templates')->find(['id' => $table_template_ids]); $table_template_data = []; foreach($table_templates as $table_template) { $table_template_data[$table_template->get('label|name')] = $table_template->icon; } $event->wire()->addHookBefore('InputfieldButton::render', function (HookEvent $event) use ($table_template_data) { /** @var InputfieldButton $button */ $button = $event->object; // Return early if this is not a button we want to modify if(!isset($table_template_data[$button->value])) return; // Set button icon $button->icon = $table_template_data[$button->value]; }); });
  21. I guess you could disable counting in your selector and get the count of matching pages separately via $pages->count() on the first page only. Then pass the count in the query string (or store it in $session) and use PaginatedArray::setTotal() to set the total count to the PageArray on each pagination. And if necessary you can fake the pagination entirely as shown by Ryan here:
  22. Thanks for alerting me to this. In v0.1.0, if you didn't supply a limit as part of the selector string then all the matching pages are listed, which strictly speaking is the correct result. But it seems that Lister doesn't expect this scenario and renders pagination numbers as if a limit of 25 was supplied. Clicking these numbers doesn't do anything though because actually all the pages are already listed. But I think for most cases it's best to for Lister Selector to apply a default limit of 25 if none is supplied in the selector string - that way it's more in line with how Lister and Lister Pro behave. So I've changed to this in v0.1.1. And if you want a different limit you can supply it in the selector string (and you can use limit=0 if you want to see all results at once, but be aware that Lister will then render the incorrect pagination once again).
  23. Lister Selector A Process module that uses Lister/ListerPro, but with a selector string input instead of the normal InputfieldSelector filters. Features For power users, typing a selector string is often faster and more intuitive than fiddling with InputfieldSelector. It also lets you copy/paste selector strings that you might be using somewhere else in your code. Allows the Lister rows to be sorted by multiple fields (not possible in Lister/ListerPro) Allows the use of OR-groups (not possible in Lister/ListerPro) If ListerPro is installed you can run ListerPro actions on the listed pages - the available actions are defined in the module config. Bookmarks can be configured in the module config and accessed via the flyout menu for the module page. Usage Type your selector string on the Selector tab. The selector is applied when the "Selector string" field is blurred, so hit Tab when you have finished typing your selector. Unlike Lister/ListerPro, you can't sort results by clicking the column headings. Control the sort within the selector string instead. Superusers can jump to the module config (e.g. to create a bookmark) by clicking the cog icon at the top right of the module interface. The module is mostly intended for use by superusers, because in most cases site editors won't understand the ProcessWire selector string syntax. If you want another role to be able to access Lister Selector then give the role the "lister-selector" permission. Only superusers can define bookmarks because in ProcessWire module config screens are only accessible to superusers. Screenshots Process page Module config (when ListerPro is installed) Advanced If for any reason you want to create dynamic bookmark links to Lister Selector for a given selector you can do that like this: /** @var $pls ProcessListerSelector */ $pls = $modules->get('ProcessListerSelector'); $selector = "template=foo, title%=bar"; $pls_link = $pls->getProcessPage()->url . '?bm=' . $pls->urlSafeBase64Encode($selector); echo "<a href='$pls_link'>My link</a>"; https://github.com/Toutouwai/ProcessListerSelector https://modules.processwire.com/modules/process-lister-selector/
  24. I've released v0.2.3 to add some support for this. v0.2.3 1. Adds a setting to the module config: "Name stub classes for compatibility with custom Page classes". This option names the template stub classes using the same format as for custom Page class names. A side-effect of this is that your IDE may warn you that multiple definitions exist for your custom Page classes. This is because the stub class files generated by this module will always be distinct from any custom Page class files you create (overwriting the actual Page class files is not on the table for this module). If you enable this option then you'll indicate the class name for $page in your template files like this (using the home template as an example): /** @var HomePage $page */ 2. For new installs the default path for stub files is now "/site/templates/AutoTemplateStubs/". This path is still configurable though.
  25. @teppo, a bit off-topic but I'm curious about the use of runHooks() because I haven't seen this used before. If you are adding a custom hook method "myEvent", what's the reason to do... $this->runHooks('myEvent', ['what' => 'listening']); ...instead of... $this->myEvent('listening');
  • Create New...