Jump to content


  • Posts

  • Joined

Everything posted by LMD

  1. There are probably as many ways of doing this as there are people who use ProcessWire, but this is how I would do it based on what I understand of your set-up. On the template: <?php // Just include one partial here (the HTML code in 'block-sidebar' and 'block-sidebar-settings' appears identical) include('views/blocks/block-sidebar.php'); ?> Now in "block-sidebar.php" <?php $aside = page()->aside; // first get the current page aside if ($aside === "") { // If the current page aside is empty, try and get the settings aside $aside = $pages->get('1032')->settings_aside; } ?> <?php // Only show the entire block of code if $aside is not empty (if neither the current page nor settings have content) if ($aside !== "") : ?> <div class="uk-card card-bg uk-padding-small uk-margin-small-bottom"> <h6 class="uk-margin-remove uk-text-uppercase uk-text-normal" style="letter-spacing: 1px; margin-bottom: .5rem !important;"><span uk-icon="icon: info"></span> Aktuelles</h6> <?=$aside?> </div> <?php endif; ?>
  2. I do not understand what you were trying in your examples -- I think maybe you are muddled about how $sanitizer works? All it does it return the sanitized value. All you need to do is this: $activepost = $sanitizer->selectorValue($page->title); $articles = $pages->find("template=post, cbpage2=$author, limit=2, title!=$activepost, sort=-cbdate" ); /** * OR: in case you need to use it unsanitized elsewhere */ $activepost = $page->title; // note the position of the sanitizer method $articles = $pages->find("template=post, cbpage2=$author, limit=2, title!=" . $sanitizer->selectorValue($activepost) . ", sort=-cbdate" ); /** * OR (again): if $activepost is only used in the selector, you can ditch it altogether and use $page->title directly */ $articles = $pages->find("template=post, cbpage2=$author, limit=2, title!=" . $sanitizer->selectorValue($page->title) . ", sort=-cbdate" ); */
  3. @biber No problem, we've all been there at some point.
  4. Did you remember to update your password to the new one in your ProcessWire config file (/site/config.php)?
  5. Another like for the CKEditor plugin custom folder change! Regarding the download page of the website: I suspect this may have been mentioned before, but the displayed version still appears to be stuck on 3.0.174, when we are actually on version 3.0.178.
  6. @ryan This is an excellent addition to the core. For a particular use-case I have, I am wondering if something is possible -- either something already possible I've overlooked, or as a feature request. Sometimes it is useful to have the page info when building a selector for a page reference field — so I was wondering whether it could be possible to use placeholder tokens to pass the current page being edited to the ajax request? For example, in the "Text Tags" section of the field setup, we could enter something like this for the "Ajax URL" : /find-fieldname/{page.id}/?q={q} Where "{page.id}" is replaced with the ID of the page being edited at runtime. Similar to other places in the PW admin where we can insert placeholder tokens. Then in the hook itself, we have: $wire->addHook("/find-fieldname/([0-9]{4})/", function($e) { $pageID = $e->arguments(1); // ... the rest of the code }); I'm using page ID as an example, but it could be any valid page or template variable.
  7. UPDATE 20 Apr. 2021: This issue has now been fixed. I have just encountered the exact same issue here when trying to paginate a PageArray which has autojoined fields -- without autojoin the pagination works as expected, with it getTotal() shows the wrong number. This is with the newest DEV branch version of ProcessWire: ver. 3.0.175
  8. I had a thought about findRaw from a use-case I have (a flag set on pages via $page->meta() on save). I don't know how feasable it is, but it would neat if it was possible to join the contents of 'pages_meta' onto findRaw/getRaw queries. Nothing fancy, all it would need to do is return the raw data (just like any fieldtype that gets joined) as an array indexed by meta key. $p = $pages->findRaw('template=foobar, parent=1234', ['title', 'pages_meta']); /* Would return, for example: [5678] => [ 'title' => 'The Page Title', 'pages_meta' => [ 'foo' => 'Plain String', 'bar' => '{"a":"JSON","b":"String"}' ] */
  9. I never did like October 😄 ! I've fixed the code.
  10. Oh my, this is excellent! A first use case I thought of was archives (eg. blogs) filtered by date: YYYY/MM/DD /** * This example merely displays back the selected date as a string, but it could be used to show date-based archives. * The following URLs are valid: * /YYYY * /YYYY/MM * /YYYY/MM/DD */ // Build basic date regex to eliminate very silly things (e.g. 2021/15/92) $dateRegexY = '(year:\d{4})'; // any four digits (could be narrowed, e.g. '(19|20)\d{2}' $dateRegexM = '(month:(0[1-9]|1[0-2]))'; // 01-09 or 10-12 $dateRegexD = '(day:(0[1-9]|[1-2][0-9]|3[0-1]))'; // 01-09, 10-29, 30-31 (will need further validation!) // Put it together $dateRegex = '/' . $dateRegexY . '(/' . $dateRegexM . '(/' . $dateRegexD . ')?)?'; $wire->addHook($dateRegex, function($event) { $date = $event->year . ($event->month ? '/' . $event->month : '') . ($event->day ? '/' . $event->day : ''); return $date; }); The possibilities are endless.
  11. Without needing to buy a Pro module (although they are excellent!), there is indeed a ProcessWire way for this. You can use $files->render()/wireRenderFile() to achieve what you want: https://processwire.com/api/ref/wire-file-tools/render/ https://processwire.com/api/ref/functions/wire-render-file/ The advantage of using this over your own functions is that it will automatically keep access to all of PW's API variables (no need to pass them in). Here is a simple example based on your use-case description: /** * In your generic template file. * Note: it assumes there is a directory under 'templates' called 'layouts' (it could be called anything). * Just create your layouts in the 'layouts' dir and name them the same as the '$item->template' names you are using. */ // Get the content of the specified layout into a variable. // This example looks for '/site/templates/layouts/name-of-template.php' (the .php extension is assumed) $pageContent = $file->render('layouts/' . $item->template); // Just echo it out wherever you need it. echo $pageContent; Read the docs on it, there is more you can do with this method/function, including passing custom variables, and some other options to make it more flexible. There is also a $files->include() method which is the same except it directly echos out the content instead of returning it to a variable.
  12. Thanks Ryan, the update fixed the problem.
  13. This is great, but I've encountered a possible bug, unless I'm doing something wrong. As a test I used it to create a simple site map using a recursive function -- besides the page title, I also wanted a 'headline' field, so it seemed a good candidate for an autojoin test. When I ran it, however, there were some unexpected results (missing child pages and missing page names in urls) with the autojoin feature. Here is the plaintext output from my tests (correct version first, then the incorrect version): Notes: C = hasChildren / numChildren (same result) $p = current Page object being traversed In both cases, the sort order is default (manual sort in the backend). All pages use the same template (so definitely contain the fields being autojoined). In all cases, both the 'title' and 'headline' fields were present in the output, so I omitted them below. This is the recursive function I'm using: // Called with: siteMap($pages->get(1)) function siteMap(Page $p) { $str = ''; $children = $p->children('join=title|headline'); // with autojoin // $children = $p->children(); // without autojoin foreach ($children as $c) { $str .= $c->id . " " . $c->hasChildren() . " " . $p->id . " " . $c->url . "\n"; if ($c->hasChildren()) { $str .= siteMap($c); } } return $str; } The following methods all produce the same incorrect results: $p->children('join=title|headline'); wire('pages')->find('parent='.$p.', join=title|headline'); wire('pages')->findJoin('parent='.$p, 'title,headline'); wire('pages')->findJoin('parent='.$p, ['title','headline']); Furthermore, it made no difference if I used "field" or "join" as the keyword in the selector. ProcessWire 3.0.172 (DEV) PHP 7.2.0 MySQL 5.7.19
  14. [Edit: I probably should have tagged @gebeer.] Hi, I came across a use-case today that I wondered if it would be possible to add? It would be super useful to have the option to include child pages in the "Pick from the page being edited" and also "Pick from any page" source options. Furthermore, it would really useful if, when "include child pages" is selected (for all source options), the parent page being edited itself did not need to contain an image field. My use case is the following set-up: Gallery Collection 1 <-- contains 'image reference' field, but no 'images' field of its own. Album 1 <-- contains the 'images' field for a group of related image (e.g. different view of the same object). Album 2 Album Collection 2 Album 1 Etc... The parent "collection" template has an image reference to choose an image from one of the child album templates to serve as its "cover image". It would be even cooler if, as well as specifying the images field to select from, you could also specify images having a particular tag (if tags are used). But that might be asking for the moon 😄 !
  15. Thanks. I will look more closely at CroppableImage.
  16. @Robin SThank you for a perfectly timed module! I have a wishlist suggestion - as well as the fixed ratios, would it be feasible/possible to also include a minimum size for the crop area? For example, a banner image (used in your own screencast demo) might have a minimum width requirement. It is easy to shrink a larger cropped area, but obviously, a smaller area can not be "embiggened" without pixelation. Maybe it could be specified along with the ratio (e.g., using a comma): Banner Image=2:1,800:400 Technically, you'd only need to specify the length of the longest side.
  17. Oh, sorry, I don't have a RepeaterMatrix field in operation anywhere at the current time.
  18. Works like a charm for me, both outside and inside repeaters. Thank you, @gebeer!
  19. No, it's in my dev environment and I'm the only user (superuser). While I think of it, I also ensured the pages were 'published' and not 'hidden', in case that was the problem (it wasn't).
  20. Hi @gebeer, I tested the update this morning and oops... I don't know what happened this time, but it no longer works at all either inside or outside of a repeater. it just loads a clone of the page currently being edited. It's hard to explain, so this GIF should illustrate what's happening (this is a test outside of a repeater): The above demonstrates the "choose an image from page" and "choose any page" options. The field settings are correct - they are same ones that worked (outside of a repeater) previously. There are images available! I tried the other options (from a folder etc) and exactly the same thing happens. I uninstalled all other modules in case there was a JS conflict and still the same thing happens. In case it helps, here is the console log: I'm using ProcessWire version 3.0.147 and PHP 7.2. Happy New Year! (?)
  21. Hi @gebeer - thanks for this module. I'm having an issue when using it with repeaters, though. Previously I used the "FieldtypeImageFromPage" module which worked perfectly in repeaters, but swapped to this more flexible module and now get the following errors which prevents the field working (no image picker section available -- see screenshot after): Notice: Trying to get property 'data' of non-object in \site\modules\FieldtypeImageReference\InputfieldImageReference.module on line 58 Warning: Invalid argument supplied for foreach() in \site\modules\FieldtypeImageReference\InputfieldImageReference.module on line 58 As you can see, there is no way to pick an image (clicking on the blank area does nothing) -- the field is set-up correctly, because it works outside of a repeater without problems! I think the problem is that the module is getting the field settings data by fieldname, but of course, in a repeater the fieldname is changed to "fieldname_repeater####", causing the issue. It appears to be this line (58) : foreach($this->wire('fields')->get($this->name)->data as $key => $value) { In a repeater, $this->name is looking for "fieldname_repeater####" which returns a non-object, making the foreach bork.
  22. Not quite, that would return pages where only ONE of the search terms is present in any of the fields ("foo" OR "bar"). FIND EITHER "foo" OR "bar" IN title OR FIND EITHER "foo" OR "bar" IN field_a OR FIND EITHER "foo" OR "bar" IN field_b So it would match a page where only "foo" is contained in any (or all) of the fields -- but "bar" must also be present somewhere. Likewise, this would not work either: $selector = "title~=foo|bar, field_a~=foo|bar, field_b~=foo|bar"; FIND EITHER "foo" OR "bar" IN title AND FIND EITHER "foo" OR "bar" IN field_a AND FIND EITHER "foo" OR "bar" IN field_b In this case, "foo" or "bar" must be present in all the fields, which is also not what we want
  23. I don't know if this is the right place, it's not really a tutorial, just a tip based on notes I wrote myself in a recent project to get it straight in my own head. I thought it might be useful for others in a similar situation. Scenario: Create a search function that will search for keywords "foo" and "bar" in multiple fields, but the keywords do not have to be adjacent, in order, or even all in the same field. For eample, the selector must match if "foo" is in "field_a" and "bar" is in "field_b" -- so long as both keywords are present somewhere, the page match is valid. It is possible to just split the terms and do multiple queries on each field separately and then combine the results into a single PageArray for pagination (I believe there is a module that helps with this). However, I wanted to see if it was possible to do a basic version with a single query. Not The Solution: The following selector does not work when keywords appear separately in different fields (operator '~=' - contains all the words): $selector = "title|field_a|field_b~=foo bar"; What the selector is saying: FIND BOTH "foo" AND "bar" IN title OR FIND BOTH "foo" AND "bar" IN field_a OR FIND BOTH "foo" AND "bar" IN field_b In this case, both "foo" and "bar" have to be in the same field (but not adjacent or in order) to match. The Actual Solution What we need to use is "named selectors" to let us match each individual keyword separately while still using one selector. Using the same example as before: $selector = "selector1=(title|field_a|field_b~=foo), selector2=(title|field_a|field_b~=bar)"; What the selector is saying at its most basic level: FIND BOTH selector1 AND selector2 Or, to expand on this, it is saying: (FIND "foo" IN title OR field_a OR field_b) AND (FIND "bar" IN title OR field_a OR field_b) Crucially, "foo" and "bar" do not have to be in the same field to match. Practical Method In this example code, I am actually allowing the search for phrases (using "quoted text") as well as individual terms, so a person could enter... "foo bar" baz ... and it will keep "foo bar" together aa one term and "baz" as a separate term and match them as an exact phrase. // Keywords obtained from $input->get and cleaned (multiple spaces removed)/sanitized etc. $keywords = '"foo bar" baz'; // Split into individual search terms by space (preserve spaces in quoted text) $terms = str_getcsv($keywords, " "); // array("foo bar", "baz") // Build up named selectors $ns = ""; // named selectors string $i=1; // named selector count foreach ($terms as $term) { // operator '*=' - contains the exact word or phrase $ns .= ", ns{$i}=(title|field_a|field_b*=" . trim($term) . ")"; $i++; } //$ns = ", ns1=(title|field_a|field_b*=foo bar), ns2=(title|field_a|field_b*=baz)" // Construct the whole selector (modify/add other general selectors as needed) $selector = "template=my-template, limit=20, sort=-date" . $ns; // Find pages based on selector $results = $pages->find($selector); DISCLAIMER I haven't done any tests to see if this method is more efficient than running queries on each field separately and combining the results, I just wanted to see if it was possible!
  24. $page->references() is a great addition - I can immediately see it being use for tagging systems. I would like to now if it is, or maybe will eventually be, possible to use 'references' within a selector. For example: // Find all the tags and sort them by the number of references to each tag. // When looping through the results, the tags are in order of the most references (ie, most used tags) first. $all_tags = $pages->find('template=tag, sort=-references.count');
  25. @Macrura - ordinarily I would do that, but this is a little freebie site for a group of very non-tech savvy people (a favour for my mother). I feel it would be much easier for them to drag-n-drop their photo gallery pics into a single field, then edit the photo info and press save once. Even a repeater would be a bit too much. I will monitor the siutation though and if I think they can deal with it, will switch to a one-page (or repeater item) per photo (thanks to the API, converting will be no problem!). @dragan - I wonder if you see a label because you are using multi-language? It is definitely not visible in my single language version, althpough it is present, just hidden with CSS ('pw-hidden' class). In fact, if somebody is happy with the default label "Description", then it would be possible to override it in a custom admin CSS file, but I want to change the text itself. However -- ta daaa! -- after digging around Captain Hook, I have found a simple solution using a hook in ready.php This is very specific to my needs -- but it could be made more generic. $pages->addHookAfter('InputfieldImage::renderItem', function (HookEvent $event){ $obj = $event->object; if ($obj->name != 'quilt_images') return; // stop if the field isn't the specific one we want $rtn = $event->return; // get the return value // The string we are looking for (regex because the unique ID part in the label's 'for' attribute) $regex = "/<label for='description_quilt_images_([^']+)' class='detail pw-hidden'>Description<\/label>/i"; // What we want to replace it with (remove the 'pw-hidden' class and change label text) $replacement = "<label for='description_quilt_images_$1' class='detail'>Give a brief description/title for the quilt.</label>"; // Do the replacement $newRtn = preg_replace($regex, $replacement, $rtn); $event->return = $newRtn; // replace return value with new string }); And the result:
  • Create New...