Jump to content

MarkE

Members
  • Posts

    1,051
  • Joined

  • Last visited

  • Days Won

    12

Everything posted by MarkE

  1. The hook seems to be working OK. RockFrontend is a js variable. Where is it supposed to be set?
  2. Thanks @bernhard. I did that, but I still get this: Alfred.js?m=1660123300:179 Uncaught ReferenceError: RockFrontend is not defined at Alfred.js?m=1660123300:179:3 at Alfred.js?m=1660123300:282:3 livereload.js?m=1660123300:11 Uncaught ReferenceError: RockFrontend is not defined at startStream (livereload.js?m=1660123300:11:14) at livereload.js?m=1660123300:47:3
  3. Hi @bernhard, Any idea why I am getting this error in the dev tools console after installing the latest version? Uncaught ReferenceError: RockFrontend is not defined at RockFrontend.js?m=1660203134:11:12 at RockFrontend.js?m=1660203134:48:3 I updated from an earlier version (I forget which, but about 2 months ago) because font-awsome was not loading properly - I was getting <link rel="stylesheet" href="undefinedwire/templates-admin/styles/font-awesome/css/font-awesome.min.css" type="text/css"> rather than the correct url. Now I don't even get that link. Looks like a similar issue though as the url is given by RockFrontend.rootUrl
  4. Preventing a page from being moved is not necessarily a straightforward matter of trapping the change in a save hook in the same way as a page edit. A problem occurs if the page is only to be 'blocked' conditionally - when the page path is in some given array, for example. In hooking before page save, the page path is the new page path after the move, rather than before it, so you need to do something like this: // .... In the before page:saveReady hook // $page is the page :) The code below operates when (isset($page->parentPrevious) && $object->parentPrevious != $object->parent) $name = $page->parentPrevious->path . $page->name . '/'; // need the path before it was moved if(in_array($name, $blockedNames)) { // $blockedNames are the ones where we don't want moves // Because this hook operates after the move, we need to reverse the move $page->parent = $page->parentPrevious; // Alternatively, to completely stop the save //$event->replace = true; //$event->return = false; } //... rest of hook This prevents the page being moved, both in the page hierarchy (by dragging) or in the page editor by changing the parent in the settings tab. It is also possible to hook before Page:moveable with something like this /** @var Page $page */ $page = $event->object; $moveable = $event->return; // ... code to set $moveable to false if conditions met $event->return = $moveable; Interestingly, the (not documented?) moveable method is created as a hook by PagePermissions and so is hookable. However, this method appears to catch only the situation where the move is effected by dragging the page in the tree, not when the parent is changed in the settings.
  5. Thanks @gebeer. In reality it's not quite as simple as that because my code is inside a method that is called by six different hooks, rather than in a single hook. However, passing the object argument to that method by reference, rather than by value, means that I can then avoid the unnecessary saves, as you suggest. I'll post an amended version in the tutorials thread.
  6. I have been trying to add some hooks to prevent pages from being moved in certain circumstances. It seems (to me) that this is not entirely straightforward. First off, just a simple hook before the page save does not always work. In my use case, the 'circumstances' are when the page path is in some given array. In hooking before page save, the page path is the new page path after the move, rather than before it, so I need to do something like this: // .... In the before page:saveReady hook // $page is the page :) The code below operates when (isset($page->parentPrevious) && $object->parentPrevious != $object->parent) $name = $page->parentPrevious->path . $page->name . '/'; // need the path before it was moved // Because this hook operates after the move, we need to reverse the move if(in_array($name, $blockedNames)) { // $blockedNames are the ones where we don't want moves $page->parent = $page->parentPrevious; $page->of(false); $this->wire()->session->set('bypassSaveHook', true); // to prevent loops $page->save(); $this->wire()->session->remove('bypassSaveHook'); } //... rest of hook This prevents the page being moved, both in the page hierarchy (by dragging) or in the page editor by changing the parent in the settings tab. I also tried hooking before Page:moveable with something like this /** @var Page $page */ $page = $event->object; $moveable = $event->return; // ... code to set $moveable to false if conditions met $event->return = $moveable; Interestingly, the (not documented?) moveable method is created as a hook by PagePermissions and so is hookable. However, this appears to catch only the situation where the move is effected by dragging the page in the tree, not when the parent is changed in the settings. If anyone knows of any smarter way of doing this stuff, I would be interested, otherwise maybe this could be moved to a tutorial?
  7. But then why not use a fieldset, in which case it doesn’t depend on the screen layout?
  8. I agree. I always considered the behaviour slightly odd as the inputfields are not necessarily related and even differ depending on the screen size.
  9. That's exactly what it is. It is because the repeater page is a child of admin, not of its ’get for' page. per a response from @ryan in another post:
  10. Many many thanks @kongondo! It's still a bit of a hack, as it seems I have to add it in the module ready() like this: public function ready() { /* * Make sure all the assets are there for a page with repeaters */ $page = $this->page(); if($page and $page->template == 'admin') { $pId = $this->wire()->input->get('id'); $pId = $this->wire('sanitizer')->int($pId); if(is_int($pId) && $pId > 0) { $p = $this->wire('pages')->get($pId); $this->getInputfieldAssets($p); } } } protected function getInputfieldAssets($p) { foreach($p->getFields() as $field) { $inputfield = $field->getInputfield($p); $type = $inputfield->className; $name = $inputfield->attr('name'); if($type == 'InputfieldRepeaterMatrix' || $type == 'InputfieldRepeater') { foreach($p->$name as $repeaterItem) { $this->getInputfieldAssets($repeaterItem); } } else { $inputfield->renderReady(); } } } Ideally, I would add it as part of the migration process, rather than have it execute on each ready(), but I haven't found a way of doing that and at least the code above is a lot simpler than what I was trying (and it seems to work fully). EDIT: To avoid complications, the first part of the above code was moved to a before hook on ProcessPageEdit::execute
  11. Additional info: This is only a problem with fields inside repeaters and repeater matrix fields. In fact, if field types inside repeaters also exist in the page outside repeaters then they will load the assets automatically and the fields will look fine inside and outside the repeaters. More context: The problem is occurring with my ProcessDbMigrate module, which I am enhancing to handle repeater matrix fields and nested repeaters, but so far I have not tested the nested feature. I'm not sure where @bernhard's RockMigrations has got to with these features or whether similar problems occur there, but I'll take a look. PS. Don't download the current version of my module, it has some bugs which I hope to fix in this new version (once it works fully!).
  12. I have a strange situation in which the required js and css for the inputfields on an admin page are not being loaded. The main field in question is called 'motif_layout_components' and its matrix items are updated via the API. The image below shows an example of what the page should look like: However, the image below is what I get: As you can see, the toggle field, page ref and CKE fields have not been styled. On inspecting with the browser dev tools, it is clear that this is because the files InputfieldToggle.css etc. have not been loaded. Saving the page has no effect. However, if I go to the field/edit page for the motif_laout_components field and save that then, on reloading the illustrated page, the required sources appear and the page is rendered correctly as shown in the first image. Any ideas? Why are the assets not loaded? How can I ensure that they are loaded (i.e. via the API rather than manually saving the related edit/field page)? Any insights gracefully received!
  13. Any more context and documentation for that teaser? I had problems with laragon and the support is not great. Ddev works fine (and much faster with mutagen on my windows setup).
  14. I use it on one site and it is quite neat, but I have yet to move that site to php8, so thanks for the heads-up @tires
  15. Have you tried setting output formatting to true @cosmicsafari?
  16. I would pretty much agree with all the above comments. Having built a number of web apps, I would say that PW is by far my favourite tool to turn a conceptual design into a working application, quickly and in a fairly β€˜natural’ way. Obviously you need some reasonable php knowledge. Also don’t underestimate the work in getting the look and feel that you want for the front end - it’s not like Wordpress in that regard (but I hate WP for apps). You will need to give some careful thought as to whether you do one or two sites. If there is a lot of interaction between the front end and the admin functions then one site is probably best. Otherwise two interacting sites gives you a β€˜cleaner’ CMS for the public site but is slightly more complex to implement. Last, but not least, the PW forum members are really helpful if you get stuck ? Ps. Not sure if the op is still interested but that’s my twopennorth anyway.
  17. The issue is a bit more complex than that, maybe because of the way my menus work. I tend to have (viewable) pages which have (viewable) children, so the top-level menu items can also be pages, in which case they also appear in the dropdown and can be selected either by clicking the top-level item or the one on the dropdown. If you just fix the home page highlighting then, in my case, the parent will be highighted in the dropdown as well as on the top level. See pics below: What I want (& my code above gives): What isActive() gives me (currently): Aagh! Of course you are right. I changed it to find() so that the home page was also included. Your menu assumed that home was not a menu item (hence also the issue with isActive() ). So now I have changed it to $home->children($selector) and the order is correct and all my complications go away (but I do then need to prepend $first to $items). Never mind it was an interesting exercise and may come in useful one day.
  18. Sure! I've posted that in the Latte thread since it makes more sense (maybe?) there. Maybe I misunderstood. I see now that alfred is a substitute for Option D only. Thanks for that - works well. I get an error "Call to a member function hasField() on null" which seems to be generated from Latte (RuntimeChecker.php line 38). I guess it is something to do with $img being a PageImage class and not a Page. "theme_summary" is a custom image field and I'm struggling finding out how to use a front end editor on those. The image is one of a number in an images field within a repeater, which I guess doesn't make it any easier. Before custom image fields came along, I used to put each image on a page and link to them with a multi-value page ref field - I'm sort-of wishing I'd carried on that way. And BTW, I the lightbox fix doesn't work in this context - the fix I use is uk-lightbox="toggle: .gallery-item a" but that catches the figcaption too and trying anything more specific messes up the lightbox.
  19. isActive() selects the parent page as active as well as the current page. I have a structure where the home ('/') page is the landing page and the (immediate) children need to be at the same level as Home. isActive() always shows Home as active whenever one of the other menu items is active. I mean that the menu hierarchy (other than Home) looks exactly like the page tree (but just for pages which are menu items). So if the page tree is: Home - Child 1 - Child 2 -- Grandchild 1 -- Grandchild 2 - Child 3 etc. the menu will be: Home Child1 Child2 Child3 - Grandchild 1 - Grandchild 2 Not always. If no sort order is specified, they will be in id order (I think) - which is fine if you created them in the order you want. If you specify "sort=sort" then the top level will be correct, but lower levels will get mixed in - see So those give a sort order as it appears in the page tree hierarchy: like 1, 2, 3, 4, 4.33, 4.66, 5 where 4.33 and 4.66 are the grandchildren. That way you don't encounter a child in the list before you have had its parent (which messes up the menu). EDIT: BTW, the $shown var is to prevent children being shown subsequently as higher-level items - not sure if that was fixing a problem in the original code or was just needed in my version.
  20. Thanks for the menu suggestion @bernhard. I have done a variation on this to do a hierarchical menu which relects the page tree structure. The code below should be considered illustrative, rather than a recipe. In particular, you will need to adapt it to whatever css framework you are using. Also, my "nav.latte" is in the context of a repeater matrix item (part of my page builder framework), so the called methods live in the custom page class of the getForPage() (custom page classes don't work with repeaters directly). {* in nav.latte *} {var $selector = ($page->theme_selector) ?: "template=ThemeDisplay"} {* specific to my page builder. Define whatever selector you want for menu items here. *} {* define block that is used for recursion *} {define items, $items, $first, $selector, $shown = new ProcessWire\PageArray} {do $items = $page->getForPage()->treeSort($items)} // or you could put the function in init.php, suitably modified {foreach $items as $item}{* loop all items*} {* define variables for inside the loop *} {var $subid = "tm-menu-" . $item->id} {var $numc = ($item->numChildren($selector) && $item !== $first)} {var $active = (($item == $rockfrontend->wire->page) || ($numc && $item->children()->has($rockfrontend->wire->page)))} {* I had a problem with $rrockfrontend->isActive() not highlighting what I wanted, hence the above custom code *} {* list item markup (NB Bulma css) *} <a n:if="!$numc && !($shown->has($item) && $item !== $first)" href="{$item->url}" n:class="navbar-item, $active ? 'is-active'"> {$item->title} {do $shown->add($item)} </a> <div n:if=$numc class="navbar-item has-dropdown is-hoverable"> <a n:class="navbar-link, $active ? 'is-active'" href="{$item->url}">{$item->title} </a> {do $shown->add($item)} {* list for child-items *} <div class="navbar-dropdown"> {var $children = $item->children($selector)} {include items, $children->prepend($item), $item, $selector, $shown} {do $shown->add($children)} </div> </div> {/foreach} {/define} <nav class="navbar is-fixed-top is-transparent" role="navigation" style="{$page->theme_style|noescape}" aria-label="main navigation"> <div class="navbar-brand"> <a class="navbar-item" href="{$urls->root}"> <img src="{$logo->url}" alt="{$logo->description}"> </a> <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarMain"> <span aria-hidden="true"></span> <span aria-hidden="true"></span> <span aria-hidden="true"></span> </a> </div> <div id="navbarMain" class="navbar-menu"> <div class="navbar-start"> {* now include the block for the first level of items *} {include items, $pages->find($selector), $home, $selector} </div> </div> </nav> /* In the custom page class */ /** * Sort pages by how they appear in the page tree hierarchy * * @param $pages * @return void */ public function treeSort($pageItems) { foreach($pageItems as $index => $item) { $item->treeSortVal = $this->treeSortVal($item)[0]; } $pageItems->sort('treeSortVal'); return $pageItems; } private function treeSortVal($item) { $val = $item->sort + 1; $increment = 1; if ($item->numParents() > 0 && $item->parent() !== $this->pages()->get('/')) { $siblingNum = max($item->siblings()->explode('sort')); $parentVal = $this->treeSortVal($item->parent()); $increment = (1 / ($siblingNum + 2)) * $parentVal[1]; // (add 2 because we added 1 to sort to get $val, then we want 1 more than the greatest $val so that the fraction is always less than 1) $val = ($val * $increment) + $parentVal[0]; } else { } return [$val, $increment]; }
  21. Latte & RockFrontend make a great combination. I have done a couple of variations: indented repeaters - as per above: and also a menu which mirrors the page hierarchy (for pages matching a given selector) - I can supply an example if anyone is interested. I'm now getting to know Alfred ? and have a few queries: Is in-line editing possible? I have a gallery (using Uikit lightbox). When I use alfred with that - <div uk-lightbox {alfred($page, theme_images)}> - the lightbox is opened when I click the edit button and I have to close it before editing - is there any way round that? Also, in the gallery, I would like to use alfred to edit the indiviual image <figcaption>. Sort of like <figcaption {alfred($img, theme_summary)>{$img->theme_summary}</figcaption> but obviously that doesn't work. Any way this is possible? Many thanks for any suggestions.
  22. From what I can tell, this is just a problem with the PW module download/install code ( @ryan?), owing to a comment in the array. If you download separately then unzip and install it is OK. However, the lack of ongoing maintenance/support does seem to be an issue.
  23. Looks good @ryan I’ve already built 2 apps with invoicing as part of them. The approach is pretty similar. I used hanna codes quite a bit for the invoice layout. To my mind, the important thing would be the ability to integrate into a wider app (which might, for example, handle products and stock levels). In some ways I think the profile approach is better than a module for this. However, if someone wants to use it in an existing app (or one already in development) we once again hit the issue of β€œmigrations” about which I know there are various views. Ideally you need a way to incorporate the code and database components into another project.
  24. Yes and no. I couldn't get your code to work. It seems like you need to put the matching <div> in the php file, not the latte file. So, with your example (where<div id="foo"> matches an id in _main.php) // home.php <div id='foo'> <?= $rockfrontend->render("sections/home.latte"); ?> </div> // sections/home.latte <h1>{$page->title} - foo!</h1> Afterthought: However, it seems that Latte makes markup regions all rather unnecessary and confusing - easiest just to directly render latte files from _main.php. For flexibility, I have // In _main.php: <body> <?= $rockfrontend->render("layouts/main.latte"); ?> </body> // In layouts/main.latte: {include "$page->template.latte"} // plus whatever else you want (sections etc) // Then whatever in the template latte file Unfortunately, it seems you still need the php template file, even if empty. It would be nice to get rid of that...
  25. Just starting to use this and I like what I see ?. I have always used markup regions for my front-end rendering and am wondering what is the best way of making these play with RockFrontend and Latte (if at all)? I like the renderMatrix method, but it just renders flat html when the repeaters have structure (depth), so I built an analogue - renderMatrixIndented, which renders the structure via <div> tags. Here is some code (which lives in my custom page class) in case it is of any use. /** * Render RepeaterMatrix fields with depth * Each level receives a div with a class name equal to the item type * @return string */ public function renderMatrixIndented($items, $vars, $options, $rockfrontend) { $out = ''; $depth = -1; $divCount = 0; foreach ($items as $item) { $field = $item->getForField(); $type = $item->type; $depthChange = $depth - $item->depth + 1; if ($depthChange > 0) { while($depthChange--) { $out.= '</div>'; $divCount--; } } $out.= "<div class={$type} style='{$item->theme_style}'>"; $divCount += 1; $file = "fields/$field/$type"; $vars = array_merge($vars, ['page' => $item]); $out .= $rockfrontend->render($file, $vars, $options); $depth = $item->depth; } while($divCount--) { $out.= "</div>"; } // if renderMatrix was called from a latte file we return HTML instead // of a string so that we don't need to call |noescape filter $trace = Debug::backtrace()[1]['file']; if (strpos($trace, "/site/assets/cache/Latte/") === 0) $out = new Html($out); bd($out, 'OUT'); return $out; } No doubt this could be generalised somewhat. BTW, it also adds some styling to each item if it has a field 'theme_style' but that could probably live elsewhere. I am using this in a simple page builder where class names can correspond to grid areas.
Γ—
Γ—
  • Create New...