Jump to content


  • Posts

  • Joined

  • Last visited

Everything posted by artfulrobot

  1. Symptoms: as title says, just a blank page, no content, but successful http response. Nothing logged in any of PW's log files, or in php's error log. Cleared caches, still same problem. Other pages using same template all work fine. Page loadable with pages(1234) but *not* loadable with pages('/path/name-of-page') - the latter gets null page object. Workaround: I changed the page's name, saved, then changed it back again, and it seems to work. Possible(?) confounding factors These news pages live under /news/ however I wanted the URLs to include the published date, like /news/2024-02-21/some-page It is being accessed via URL hook on /news/[0-9-]{10}/(title[^/]+) with $event->pages->get("/news/$event->title"); The page class overrides the `path()` function which contains `return '/news/' . date('Y-m-d', $this->published) . '/' . $this->name . '/';` This has worked fine for all the other pages. Any ideas what might have happened? What could cause pages() to fail to load by page path for this page? Possible asides: I'm not 100% on how to use different urls for pages, but what I have seems/ed to work; for example the template is configured not to use trailing slash, but I found I needed one returned by path(). pages('news/the-page') (without the date) loads the page. So I think path() is only used for rendering. It didn't look like the date-prefix is persisted in the db. EDIT: page_path table When it was broken, I did a mysqldump. And after it was fixed I repeated. I then used neovim to diff the dumps (mmm, fun). Here's what I found. I believe the key difference is in the pages_paths table, where the broken version had 'news/1970-01-01/the-page' and the working version had 'news/2024-02-21/the-page'. My hunch is that something populated the pages_paths table before the page was published, thereby getting the epoch date. So I need to understand the relationship between a class's path() function, and loading with pages($path).
  2. Hi @kaz I began trying to answer you, but it's so hard to talk about these things in text! Not sure if this picture I made to document my setup is any use?
  3. At risk of making myself unpopular, I'm a neovim user who has ethical aversion to AI. I get a great developer experience and I don't have to worry about Kernighan's law making my code buggy and hard to maintain. I am also concerned about climate change, and AI has huge carbon emissions - (re)training an LLM has been estimated to emit the equivalent CO2e as 125 round-trip flights between New York and Beijing [source]. I'm concerned that my open source code has been used against its license to train AI through github (find my stuff on codeberg or project owed gitlabs) and so now will be included in proprietary products. I'm also against having to pay subscriptions to a handful of big tech companies because this accelerates wealth inequality and erodes democracy, reducing our chances of turning the ship around before crossing the runaway climate change event horizon. I do not doubt AI's usefulness, or it's ability to charm and amaze or excite, I do not doubt that it can be used to save lives even (e.g. specific medical use such as identifying cancer early from scans). But I do remember how Nobel thought his invention of dynamite would be great and I'm not going to be taken in by another big tech lie about "don't be evil". We have very weak regulatory powers and a world teetering on the edge of unfathomable suffering. This is a post about a personal choice of text editor, and I'm not judging folx who choose Cursor (or ai plugins for neovim!) - I respect that opinions differ. But I wanted to share my opinions on my choice, too.
  4. I've just made a module that exposes the 'published' date under the page's Settings tab, so you can update it. It appears to be working nicely, but any feedback would be very welcome as I'm new to this still. https://codeberg.org/artfulrobot/EditablePublishedDate/src/branch/main/EditablePublishedDate.module.php
  5. I only have one pw site at the mo, and I can't reproduce it reliably. It's just happened a few times and I wondered if anyone else had the problem. The main bother of it is that, typically I'm in a hurry to debug the crash, not debug the debugger at the time of the crash!
  6. @adrian oh yeah, I'm all about the hard reload. I have basically stuck tape over my Ctrl key. But sadly it didn''t help. It's so weird how it exhibits the error, yet quotes the corrected code!
  7. I've noticed this a couple of times. I load a page and it crashes, Tracy gives me a big red page with helpful info on it. I then fix the problem, and reload the page - but the error won't go away. For example the screenshot below shows and error saying "Call to undefined function ProcessWore\pappub_inlay_request()", and then highlights a line in my code that (now because it's been fixed) doesn't show the code from the error message! One part of the page is lying to me, and it doesn't go away until some timeout. I'm guessing a cache timeout? But I've tried adding a ?something=123 var and that didn't fix anything either: still received the old error that no longer correlates to the actual code; Tracy seems to be intercepting my actual code to show me the old error! Anyone else had this?
  8. Thanks for sharing this @BitPoet I read about it in the newsletter. I love that people publish simple useful solutions like this because it is a great learning aid as someone who still considers themself new to ProcessWire. On looking at your code, I had these observations (which I'm only sharing in case they are interesting!) Wow, how cool that PW lets you invent new 'subfields' as a way to integrate potentially complex SQL. I wonder what I might do with that! Hmmm. Why escape what must only be an integer as a string? I then wondered how MariaDB would handle the comparison since you're requiring an implicit coercion by comparing an int (output of CHAR_LENGTH) to a string. Because '2' > '100' if you're comparing string values, which is not what we want. It seems from my tests that MariaDB picks integer as the shared type for the comparison, which is lucky. I assume the operator does not need validating because we're assuming PW core has already done this and there's no codepath that would allow this function to be called with user input for the operator. SELECT '2' > '100', CHAR_LENGTH('12') > '100', '100' < CHAR_LENGTH('12'); +-------------+---------------------------+---------------------------+ | '2' > '100' | CHAR_LENGTH('12') > '100' | '100' < CHAR_LENGTH('12') | +-------------+---------------------------+---------------------------+ | 1 | 0 | 0 | +-------------+---------------------------+---------------------------+ Anyway, thanks for sharing the code, I'm not sure if I'll need a length search any time soon, but it's helpful to have learnt that it's possible and that similar such extensions are possible.
  9. V interesting article! Thanks all. I have also implemented latte as the main templating engine in a site. I didn't use any of the big module solutions because I think my needs are simple and fewer dependencies leads to easier long term maintenance. I have implemented latte as an api var and used page classes for all the page type specific work. It's been good as I've leveraged inheritance in both page classes (I'm also using pageTableNext) and also latte (`{layout...}`), reducing duplicate code. One observation I had was that I like to use latte to make html templates more **readable**, and do all the heavy lifting **logic** in pure php in page classes. (I've also observed that I'm that committed to markdown that I use it instead of fiddly, non-semantic buttons I'd have to scroll back to get to! Oops)
  10. Came here because I use page table next and my staff users cannot delete a section they've added. They also can't delete any other pages, despite the permissions being allowed. * p1 Home * p2 Some page (ptn field contains p5, p6...) * p3 Admin * p4 Content elements * p5 a ptn section belonging to p2 * p6 another ptn page belonging to p2 Someone with staff role has permission to delete pages, but the delete button next to p5, p6 on the edit screen for p2 does nothing - the response comes back silently with nothing happening (JSON returned includes `success: false, message:""`) But this problem for me was solved by:
  11. @d'Hinnisdaël done, sorry for barking up the wrong tree!
  12. By the way, both the Getting Started link (to an #anchor) and the Documentation link (404: https://raw.githubusercontent.com/wanze/TemplateEngineFactory/v1.1.3/DOCUMENTATION.md) are broken at https://processwire.com/modules/template-engine-factory/
  13. Hi @d'Hinnisdaël This looks interesting, thanks for posting. By the way, both the Getting Started link (to an #anchor) and the Documentation link (404: https://raw.githubusercontent.com/wanze/TemplateEngineFactory/v1.1.3/DOCUMENTATION.md) are broken at https://processwire.com/modules/template-engine-factory/
  14. @BitPoet Are you doing this locally or are you preparing a PR for the project? If so you're brave! I bet someone out there is relying on the existing behaviour. e.g. for cache warming or some other purpose for which my imagination was too limited. There's specific code written to implement the existing behaviour, so that ought to be removed if you're going to throw an exception. Clearly someone, @ryan maybe, had something in mind, although I stand by my opinion that it's super confusing 😆
  15. Thanks both. I think the confusion arises because age is used to mean 2 different things in the cache context. In code, it's $expires in both contexts. For save() and also for get() when a function is also provided $expires means when the data should expire. For get() when a function is NOT provided $expires means "get if the thing expires before this new $expires, oh but also if it never expires (because of that 2010 date that's used to mean never expire...)". The 2nd case is my beef! Typically, when wanting a cached value, you care to fetch it unless it's too old for you to rely on. I can see the use case that you cache something with a long expiry, but that one user might only want to use a value if it's not that old. But that's not what we have. We have sort of the opposite: get something if it's going to expire before the given date. I'd love it if someone could give a scenario in which this was useful. Perhaps the use-case is cache warming? Like you might want to get stuff that's going to expire soon, and update the cache now. We could then use get($name, $expiry) and then recalculate the value and save() with a new expiry? This is the only thing I can think of? The fact that $expiry means 2 completely different things in the same get() api call is a trip hazard that needs flagging in the docs. I spent ages wondering why caching wasn't working and eventually stepped through to look at the SQL to get here.
  16. According to the docs at https://processwire.com/api/ref/wire-cache/get/ for $cache->get($key, $expiry), $expiry means: I thought that meant: at 9am store something with an hour's expiry (so 10am) using $cache->save($k, $v, 60*60); At 9:01am, ask for it, if the value is younger than 30 mins, using $cache->get($k, 30*60); But it doesn't. And it seems to me it can't do as it's documented to do because the date the thing was stored (and therefore how old it is) is not stored in the db table. In fact the SQL that's run is: SELECT * FROM caches WHERE name=$k AND expires <= 9:31am So even though our data is 1 minute old, it's not considered younger than 30 mins old and is not returned. The only way the cache could honour the docs would be if the date the thing was stored in the db was saved. Then we could do whenSaved < 9:31am (though I'd suggest we also specify that it hasn't expired too, to honour the save() contract). But we don't have that field. Just wanted to sanity check these findings with the community. And if I'm right, how do we go about suggesting clarifications for the docs? If I'm just a confused noob, please explain the designed use-case for the documented get/expiry behaviour.
  17. If it's ok to dig up old threads.... @bernhard here, aren't you double encoding url, title? Assuming this is front end code, with `$pages->of(true)` and you have html entities text formatter in place on your title, url fields then a title with - say - an & would come out looking like &amp; as it would actually be &amp;amp;
  18. Thanks again @bernhard ! I started my project without latte, then started to despise what templates looked like with raw php, and liked the look of Latte, so I added it as an aside; I have it as an api variable $latte and use it in some places but not everywhere. I'm aware there are two projects that offer to integrate Latte everywhere, but that looked a bit daunting! I'm currently edging in the opposite direction now! Using Latte's escaping instead of PW's. So now my code is like {do $page->of(false)} <!-- but see comment below --> {var $i = $page->image->first} <h2>{$page->title}</h2> {$page->someLongBodyHTML|noescape} <img url={$i->url} alt={$i->alt} /> ... Which means cleaner templates as I only need to use `|noescape` on HTML fields (so text formatters run). I prefer to do it outside because I like the principle that a template should not have side effects. If some thing that relied on OF being either true or false but called my template to render it, and in doing so the OF got swapped, that could lead to trouble. So I prefer to pass in an object with OF correct; e.g. in my n:foreach loop I know I've got my own page that I can change OF on. The {do ...} syntax is good though, hadn't spotted that.
  19. I like the idea but this means every reference to the field needs to be done this way, which is quite a lot to (a) change and (b) remember for future templates. At least with (1) I can do it once per page object. Also (1) fixes potential problems with other fields as well. I don't think it's relevant, but I'm using Latte, too. So my loop is like this: <li n:foreach="$downloads as $downloadPage"> {var $dummy = $downloadPage->of(true)} <!-- This is the bit I wanted to avoid --> {include 'download-list--teaser.latte', page: $downloadPage} </li> Then in `download-list--teaser.latte` (wish this editor supported semantic inline code samples!) I can just use `$page->image` like normal: {if $page->image && $page->image->width * $page->image->height < 24000000} {var $thumb=$page->image->size(400, 220) } {/if} <a target=_blank href="{$page->url}" > <article > <div class="meta body-text"> <h1>{$page->title|noescape}</h1> {$page->text|noescape} </div> {ifset $thumb} <div class='image'><img src="{$thumb->url}" alt="{$thumb->alt}" /></div> {else} <div class='image bg-stripey'></div> {/ifset} </article> </a> (I may not have all those escaping rules right yet.)
  20. Thanks @bernhard for your reply. Front/back end: it's complex! In this situation, the front end template is being called in a back end context, by PageTableNext (PTN), to render a slice of a page in the shadow DOM. For this reason, I don't want to interfere with the global OF setting as that could risk messing up the operation of the admin forms. As I said, I'm experiencing OF=true for the value being rendered, but OF=false for $page->somePageRefField so I think what's happening is that the global setting will be false, but PTN is presumably setting OF=true on the page it renders, but this does not propagate down. (string) doesn't help me - I think - because the field I'm having trouble with is an image field. But it's useful to know that PW will output-format on toString(). The image field is configured to return a single image (when OF=true) as it will only ever hold one image. However if the template receives the owner of the field with OF=false, then image fields always return an PageImages object, which breaks the template. I think my options are: As I'm doing now: inside the render loop, do $old=$item->of(false); then do $item->of($old); at the end of my code. This means I definitely know that I'm dealing with an output formatted object. In my code set $pages->of(true). And reset that at the end. I feel uncomfortable about this somehow. I suppose I don't know what else it might affect, though it would be nice to do this outside of the field loop. Alternatively, for this specific case, I could reconfigure my image field to always return a PageImages. This impacts quite a few templates though. I think I'll stick with the solution (1) and accept it as a quirk of PTN.
  21. I have a template with a Page Reference field on it called resources. In my template file, I'm noting this: $page->of(); // **true** foreach($page->resources as $resourcePage) { $resourcePage->of(); // **false** } i.e. the output formatting is not passed down from the main $page. This surprised me, as it means - I think - that something like <?php echo $page->resources[0]->title; ?> would be unescaped data. Is this the designed behaviour? Also, is there any way of setting the output formatting on the collection? e.g. so I could call $page->resources->of(true); // ← This bit is made up/pseudocode I'm asking about foreach ($page->resources as $resource) { echo $resource->title; }
  22. nothing shows up? or do you see <div id="content"></div> As in: it's just $x->body that's not outputting anything? It looks right to me; my code that uses repeater is similar. So to debug I would: check that you see the <div> - it's obvious, but I for one often hammer the keyboard for ages and then realise I've edited the wrong file or such...! use Tracy console on the page to inspect $page->sections and $page->sections[0] and $page->sections[0]->body. Is there data in there? not sure what my next step would be if there was data in there but it wasn't output as you have done. Check the text format filters? Sorry, probably not much help, but thought I'd chip in. (I'm relatively new here.)
  23. Sounds good. Ive found myself implementing a cache in front of $cache that stores stuff in ram (with db persistence too) for data you may need a few times in one request. I realized there was no way to replace $cache with my own extended class, so I just do this in another module. I could probably use a hook as alternative.
  24. I've decided that for my use-case, a better solution is to have a 'resources' page and template, and a 'resource' template which has a singular file field and use one page per file. Then to use page reference fields to include that in actual visitable pages. This enables me to include an image field for my files, as well, of course as all the other fields I need for files.
  • Create New...