Jump to content


  • Content Count

  • Joined

  • Last visited

Community Reputation

32 Excellent

About johnstephens

  • Rank
    Full Member

Recent Profile Visitors

5,438 profile views
  1. Hello, ProcessWire friends! I have a template that includes a Cache field to combine other fields for full-text search. I wonder if it's possible to add the titles from a Page Reference field on the same template. If so, how would one do that? To clarify, suppose my template includes fields like this: Title: "#DungeonWorldDay 2020: Saturday, 22 August" Body: "The Dungeon World Discord server is hosting Dungeon World Day on Saturday, 22 August. GMs will be running pick-up games, intelligentsia will be offering how-to sessions and Q&A, and fledgling players are welcome to storm dungeons galore alongside hardened veterans. ¶ Please spread the word, invite your friends, invite your enemies if they are fun to play with, and invite your ambivalent associates." Tags (Page Reference) "Role-playing" "Events for Hipsters" Cache Is there a way to configure the Cache field to include "Role-playing" and "Events for Hipsters" (referenced page titles) in addition to the Title and Body? Thanks in advance!
  2. I tried that, and I tried DefaultRepeaterPage too, placing them both in the /site/classes directory like my custom DefaultPage class which works. Alas, calling my Repeater field content still instantiates ProcessWire\RepeaterPage. I may try your addHookMethod() suggestion shortly. Thanks for the tip!
  3. Thanks, @elabx! Perhaps module development is the next step on my ProcessWire education path. I hope I can figure out extending RepeaterPage first. 🙂
  4. I have a third question that is unrelated to this problem: How do I associate a new custom Page class with a template in ProcessWire, if the template doesn't have a template file (because it is used in other templates)?
  5. Hi! I'm using ProcessWire's ability to recognize custom Page classes, and I have added some very useful methods to my DefaultPage class. I just tried using one of the methods on a Repeater field item and got the error saying, "Method RepeaterPage::meta_description does not exist or is not callable in this context". Makes sense! My method is attached to DefaultPage, not RepeaterPage. That surfaces two questions: How do I extend RepeaterPage? By creating a DefaultRepeaterPage.php file in the /site/classes directory? What is the proper way to abstract methods that I want to use for both DefaultPage and RepeaterPage, so I don't have to repeat the same code in both classes? I think I understand how to do it if they both extended the same parent class, but I don't see how that can be the case here. Thank you in advance!
  6. Thanks, @rick! In my current implementation, I have the file field attached to a repeater, and limited to one upload per repeater item. As a consequence, I'm realizing that the page ID in the path is unique for each file in this case. Thank you for the insight! Cheers!
  7. Hi! I'm trying to retrieve a unique identifier of a file uploaded by a file fieldtype. For articles, I can simply use $page->id, but that doesn't work for files. $page->url and $page->file->url both give the the URL, by contrast. Suppose I have a file uploaded to "/site/assets/files/11067/test.pdf". The number "11067" would have to be unique. Is there any way to get that number using the API? The immediate problem is I need an identifier I can append to an HTML id attribute for interaction with JavaScript. <a id="D11067">, for example, would work fine. Thanks in advance!
  8. Of course, the simplest solution to my specific issue was to use page()->siblings() instead of rootParent()->children(). Fixed.
  9. Hi! I'm trying to create a list of related pages to display on the current page. First, I'm setting a variable with the first element of the "topics" page reference field, like so: $topic1 = page()->if('topics', function() { return page()->topics->first(); }, false); Then, I want to check all the sibling pages and gather those who have the same topic. This code works, but the result will include the current page in the results. $related_articles = $topic1->id ? page()->rootParent()->children("topics=$topic1, sort=-datePublished, limit=3") : new PageArray(); I can remove the current page by appending the remove method… page()->rootParent()->children("topics=$topic1, sort=-datePublished, limit=3")->remove(page()) …but that sometimes results in the list producing fewer results than the limit, even when there are more pages tagged with the same topic beyond the "limit" offset. What I'd like to do is exclude the current page first, and then set the limit. Thanks in advance for any guidance you can offer!
  10. I figured it out! Sub-selectors was the wrong approach, but a subfield selector seems to do the trick: $articles = page()->children('sort=authors.lastname'); Woo. Love it!
  11. This feels like a case for sub-selectors, but I'm not sure how to put it together.
  12. I have a list of articles that I want to sort alphabetically by the author's lastname. The author(s) are linked to each article's page using a page reference field, and the author pages have a field for their lastname. Most articles have only one linked author page, but some may have more than one. For sorting, I only want to consider the first author in the list. I'm stumped how to make this work. $articles = page()->children('sort=FIRSTAUTHOR'); // ?? If I get one page from the list, I would be able to select the field I want to sort by like so: foreach ($articles as $article) { $sortfield = $article->authors->first()->lastname; } Thanks in advance for any guidance you can offer!
  13. Don't use that code. I found a better way. What I discovered was, using the Iterator interface from PHP's standard library did not cause the same problems as DOMNodeList. I still can't account for why calling a function inside my foreach block caused the DOMNodeList to skip alternate nodes, but using an Iterator seems to just work. Unfortunately, there's no Iterator that deals directly with DOM nodes, and you can't feed a DOMNodeList to any Iterator's constructor. That proved to be simple enough to solve by converting the DOMNodeList to an array first: function array_from($listable) { $new_array = []; foreach($listable as $item) { $new_array[] = $item; } return $new_array; } Once I had an array, I could feed it to a new ArrayIterator, and then use foreach to go reliably do stuff to each DOMNode item. Here's a sample of what it looks like in action. Since I'm importing content from Textpattern, the source includes images as HTML img elements as well as a variety of Textpattern tags (including txp:image tags and some smd_macros) // Regular HTML img elements $img_elements = new \ArrayIterator(array_from($dom->getElementsByTagName('img'))); // txp:image tags $images = new \ArrayIterator(array_from($dom->getElementsByTagName('image'))); // smd_macro called txp:image_hd $hd_images = new \ArrayIterator(array_from($dom->getElementsByTagName('image_hd'))); // smd_macro called txp:picture $pictures = new \ArrayIterator(array_from($dom->getElementsByTagName('picture'))); // Combine them all using AppendIterator $source_images = new \AppendIterator(); $source_images->append($img_elements); $source_images->append($images); $source_images->append($hd_images); $source_images->append($pictures); // Do the stuff that needs to be done to each DOMNode item foreach ($source_images as $image) { // Handle a single image here… } I hope this helps someone in the future! Or, maybe me if I have to solve a similar problem again…
  14. I think I've solved the problem. I have no idea why this is necessary, but running the foreach block inside a recursive function seems to rapidly pick up all the images: function add_to_page_recursor($images, $image_prefix, $all_images, $newpage, $dom) { foreach($images as $image) { handle_picture($image, $image_prefix, $all_images, $newpage, $dom); $count = $images ? $images->count() : 0; if ($count > 0) add_to_page_recursor($images, $image_prefix, $all_images, $newpage, $dom); } } One obstacle to this solution is that DOMNodeList does not have the count() method before PHP 7.2, so this code requires PHP 7.2+. But for my publication server, it works. Now I just need to refactor the handle_picture function to handle all the variations of images I'm importing, but that should be simple. If anyone can shed any light on why the foreach block would be skipping images in the source that it can pick up in iterative passes, I'd love to learn what's going on here better. Thank you!
  15. Is this a known feature of PHP, that a function inside a foreach block can just blot out everything else happening inside the block for that iteration? The handle_picture() function works perfectly fine on the odd-numbered iterations (even array indices), no matter what image it is processing. And it fails on every even-numbered iteration (odd array indices). If I shuffle the source order, I get the same odd/even success/failure breakdown. So it's not choking on specific images, just whatever image happens to fall on even iterations. And then, it just ignores the whole iteration without an error or any indication.
  • Create New...