Jump to content

thetuningspoon

Members
  • Content Count

    653
  • Joined

  • Last visited

  • Days Won

    3

thetuningspoon last won the day on May 18 2020

thetuningspoon had the most liked content!

Community Reputation

557 Excellent

3 Followers

About thetuningspoon

  • Rank
    Hero Member
  • Birthday 11/03/1986

Contact Methods

  • Website URL
    https://si.design

Profile Information

  • Gender
    Male
  • Location
    CT, USA
  • Interests
    Design, Programming, Tiny Houses

Recent Profile Visitors

13,061 profile views
  1. Posted by: <?php if($page->insight_author): ?> <a href='<?= $page->insight_author->url ?>'><?= $page->insight_author->title ?></a> <?php endif ?> , <?php foreach ($page->insight_author->roles as $role): ?> <?= $role->staff_role ?> <?php endforeach ?> I would suggest something like the above, though there are certainly other templating styles. Like @monollonom said, I'm not exactly sure what role represents or how that is structured.
  2. @ryan I'm trying out the new join feature within a regular page selector. I believe you mentioned that it is possible to join subfields of page fields. I'm wondering if you can explain that in a bit more detail. Does this still all happen within a single SQL query? If I want to join the id and the title of a page in a page reference field, which of the following is the proper way to do this? $pages->find("template=foo, join=pageField|pageField.title"); $pages->find("template=foo, join=pageField.title"); // Can I join pageField.title without also joining the pageField? If so, would I still have access to the page id? $pages->find("template=foo, join=pageField.id|pageField.title") // Is joining pageField.id the same thing as joining pageField? I could answer these questions for myself if I knew of a good way of checking which data has already been loaded in the resulting page. What is the best way to double check what field data is already loaded on the page objects after performing a find (in order to make sure the join worked properly)? Edit: I just realized I wasn't thinking clearly when I wrote this. Being able to join subfields of page reference fields would require loading those pages as their own separate page objects, which would always include the page ID, name, modified, and created dates. I am still wondering whether doing either join=pageField or join=pageField.title would actually cause those pages to be preloaded as part of the find, or whether it would only lazy load them when first requested, per the normal PW behavior.
  3. This is great. Is it possible to hook after trash/delete in the page editor in order to redirect somewhere other than the page list now? This was something I had need to do in a recent project and had to do something pretty hacky to achieve the effect.
  4. No changes here. There's only so much that can be joined in one query per field, so autojoin is not always possible, especially on multi-value (FieldtypeMulti) fields with lots of values. It should be possible on most Page reference and options fields though, so long as they don't have huge amounts of selections. If there's need for it though, I may be able to have fields (where applicable) store a cache of data (like FieldtypeCombo does) that can be autojoined. I recall that in the past auto joining multi-value page fields would fail quietly and simply result in bad data being returned (like a single page instead of all pages). Can this be changed so that it throws an exception (or at least skips trying to auto join it) if you try to auto join a field that can't be auto joined? That would help avoid nasty issues for programmers who aren't familiar with the internal workings here.
  5. @Robin S That looks nice and clean. Would this actually make it appear non-editable, or just prevent the values from being saved?
  6. Here is a cleaner and more thoroughly tested version, which eliminates some of the code duplication. The Field::viewable hook is no longer needed. I'm not sure why the Field::editable is still necessary, but it doesn't work without it. /site/ready.php /** * Implements a custom page-edit-edit-fields permission. By default, all fields will be view only when the page-edit permission is granted. Adding the page-edit-edit-fields permission makes all fields on the page editable. * This could be accomplished by implementing access settings on every field of the template, but this makes it much easier. * * Individual field access settings, if enabled, will override this feature. */ $this->wire()->addHookBefore('Field::getInputfield', function(HookEvent $event) { $page = $event->arguments(0); if($page instanceof User) return; // Exclude any field types we don't want to make uneditable by default $excludedFieldtypes = [ 'FieldtypeFieldsetClose', 'FieldtypeFieldsetOpen', 'FieldtypeFieldsetTabOpen', 'FieldtypeFieldsetPage', 'FieldtypeFieldsetGroup', ]; $field = $event->object; $context = $event->arguments(1); $user = $event->wire('user'); if($context || in_array($field->type, $excludedFieldtypes)) return; // If the current user does not have page-edit-edit-fields permission, disable edit regardless of the field's roles if(!$field->useRoles && !$user->hasPermission('page-edit-edit-fields', $page)) { $field->setRoles('view', [$event->wire('config')->guestUserRolePageID]); // Set all roles to have view permission by passing in the guest role $field->setRoles('edit', []); $field->addFlag(Field::flagAccessAPI); $field->addFlag(Field::flagAccessEditor); $field->set('useRoles', true); $field->set('disabledByHook', true); } }); /** * Only the above Field::getInputfield hook should be required since it sets $field->setRoles('edit', []); but for some reason the main content tab will still appear editable unless we add this hook */ $this->wire()->addHookAfter('Field::editable', function() { $return = $event->return; $field = $event->object; // If the current was field disabled by our other hook then reflect that here as well if($field->disabledByHook) { $return = false; } $event->return = $return; });
  7. I figured out a way to do this ( @bernhard's original request, solution #3), but it wasn't easy. I added a page-edit-edit-fields permission which can be added or revoked per-template, and without which none of the fields are editable in the page editor. It required 3 hooks to make the permission work. /site/ready.php /** * Implements a custom page-edit-edit-fields permission. By default, all fields will be view only when the page-edit permission is granted. Adding the page-edit-edit-fields permission makes all fields on the page editable. * This could be accomplished by implementing access settings on every field of the template, but this makes it much easier. * * Individual field access settings, if enabled, will override this feature. * * Both this hook and the one below are required to make this work. */ $this->wire()->addHookAfter('Field::editable', 'fieldEditableHook'); function fieldEditableHook(HookEvent $event) { $return = $event->return; $page = $event->arguments(0); $user = $event->arguments(1); $field = $event->object; if($return === true && $page !== null) { // No need to check our page-edit-edit-fields permission if the field is already not editable or we're not checking page context if(!$user) $user = $event->wire('user'); // If no user passed in, use the current user // If the current user does not have page-edit-edit-fields permission, disable edit if(!$user->hasPermission('page-edit-edit-fields', $page)) { $return = false; } } $event->return = $return; } $this->wire()->addHookAfter('Field::viewable', 'fieldViewableHook'); function fieldViewableHook(HookEvent $event) { $return = $event->return; $page = $event->arguments(0); $user = $event->arguments(1); $field = $event->object; if($return === false && $page !== null) { // No need to check our page-edit-edit-fields permission if the field is already viewable if(!$user) $user = $event->wire('user'); // If no user passed in, use the current user // If the current user does not have page-edit-edit-fields permission, enable viewable permission if(!$user->hasPermission('page-edit-edit-fields', $page)) { $return = true; } } $event->return = $return; } $this->wire()->addHookBefore('Field::getInputfield', function(HookEvent $event) { // Exclude any field types we don't want to make uneditable by default $excludedFieldtypes = [ 'FieldtypeLeafletMapMarker', ]; $field = $event->object; $page = $event->arguments(0); $user = $event->wire('user'); if(in_array($field->type, $excludedFieldtypes)) return; // If the current user does not have page-edit-edit-fields permission, disable edit regardless of the field's roles if(!$field->useRoles && !$user->hasPermission('page-edit-edit-fields', $page)) { $field->set('useRoles', true); //$field->addFlag(Field::flagAccess); // May not be necessary //$field->addFlag(Field::flagAccessAPI); // May not be necessary $field->addFlag(Field::flagAccessEditor); } });
  8. I'm curious what the rationale is for the default behavior of a page save uncaching all pages in the system, as this sometimes causes problems when editing and saving more than one page during the same request. I'm finding myself having to add ['uncacheAll' => false] to most of my page saves. But if there is some important reason for clearing the cache, I want to be aware of it!
  9. Hi @antpre. Making the module permissions-aware may be possible, but it would be a bit tricky since it would involve hooking the markup for each of the inputfield option types and injecting a data attribute to signify whether the page is editable/addable. If I get some free time I will see if this is possible. This sounds like it would be an issue with any modal window in the back end. Unfortunately, iframes are basically unusable on iOS. On all of my sites that use iframe modals, I use browser detection to disable them on iOS.
  10. @horst Thank you! The image access control was indeed the problem. At some point the client made a copy of the page and trashed the original, and the clone was still referencing the images on the original page. Is this how it's supposed to work? It seems like if the page clone clones all of the images on the page, it should also update the image references inside of its textareas 🧐
  11. This is a weird one. I have a page with a repeater matrix field on it. Several of the items have CKEditor fields with images in them. When I view the page while logged in, the images are visible. When I log out, all but one of them is stripped from the output. I double checked that the IDs of the repeater items remain the same--they do. The image tags are completely absent when I view source. I used to have ProDrafts installed, but have now remove it. I have ProCache installed, but temporarily disabled it to test this. I'm at a loss. Any ideas?
  12. Hi @bernhard, We've been running up against the limitations of ProcessWire's native method of handling subfields in selectors. Basically, every subfield in a selector results in a separate query that runs to return all IDs that match the subfield, and then those IDs are used in the where of the final sql query (as opposed to joining the tables and then doing a where on the data all in a single query). This fails at scale because the individual subfield queries start returning so many page Ids that memory is exhausted or MySQL even crashes. There are ways to work around this in some cases by using separate selectors instead of subfields, but it's not terribly elegant. My question is, can we use RockFinder to solve this problem? In this particular use case, we would still want to end up with Pages in the end. I am guessing that using subfields in a RockFinder selector would behave the same way as it does in native PW (I assume RockFinder just gets the final query that PW creates for it--which may have taken multiple queries to construct--and proceeds from there). But if the join function would allow us to limit results based on the values of subfields all in a single query, then that would be golden. At that point all we would want back from RockFinder would be an array of page ids, which we could pass to Pages::getById() to get the pages back. Perhaps a method call for doing this is worth putting into RockFinder? This would be much more efficient than using the callback since it would be one SQL query to get all the pages. Another thing which I mentioned back with RockFinder1 (and implemented locally) is the option of getting a WireArray of WireData objects instead of standard objects or a plain array. This is still way faster than Pages but allows you to use runtime selectors on the results. Anyway, thanks for your work on this and looking forward to your thoughts.
  13. I wrote this a while back for my company's internal documentation and just realized it might be a benefit to others on the forum, so here it is! Checking whether a field was changed To check whether a specific field was changed you can do $page->isChanged(‘field’). This can only be done before Pages::save, since the save clears change tracking by default. Getting the values of a page before they were changed, from within a hook before Pages::save Method 1: The hacky way $clone = clone($page); $e->wire('pages')->uncache($clone); $oldP = $e->wire('pages')->get($clone->id); $oldP then represents the page and all its values as they were before any changes were made. Again, this will only work for hooks that run prior to Pages::save. Method 2: Using change tracking to get the old values If change tracking is set to track values, then the above is not necessary. In this case, calling $page->getChanges(true) will return an associative array of fields that changed and each field's prior value. Note that the value itself is also an array, since it is designed to be able to track multiple changes over the course of a single request/response cycle. Tracking of values is turned off by default (for efficiency sake), and there is no global switch to turn it on and off. To enable tracking of values, you must call $page->setTrackChanges(Wire::trackChangesOn | Wire::trackChangesValues) before any changes are made to the page that you want to track. Here is an example of how you can enable value tracking in ProcessWire's page editor: $this->addHookBefore("ProcessPageEdit::execute", null, function($e) { $p = $e->pages->get((int)$e->input->get('id')); if($p->template == 'event-registration') { $p->setTrackChanges(Wire::trackChangesOn | Wire::trackChangesValues); } }); Running hooks on both page saves and field saves Please note that using the following hooks is preferable if you want the hook to run both when individual fields are saved as well as the whole page: Pages::savePageOrFieldReady https://processwire.com/api/ref/pages/save-page-or-field-ready/ (for before a save) Pages::savedPageOrField https://processwire.com/api/ref/pages/saved-page-or-field/ (for after a save) Getting changes in a hook after Pages::save Note the $changes parameter passed to Pages::saved and Pages::savedPageOrField allows you to check which fields were changed during the save, even though the save is already completed. Using this is sometimes preferable to using a Pages::saveReady or Pages::savePageOrFieldReady hook since you don’t have to worry about the page cache or something else in your code preventing the original save from completing. A $values array is also provided, which is populated only if change tracking is set to track values (see above) Page cache issues when hooking before Pages::save Page cache issues may occur when you try to save another page inside of a before save hook (Pages::saveReady or Pages::save). A page save normally clears the entire page cache, which means any page fields on the page you were originally trying to save will have their values reset. So if you have to save another page inside of a before save hook, just use $page->save([‘uncacheAll’ => false]) to prevent the cache from being cleared. Preventing hooks from running on a particular save To prevent any hooks from running on a particular page save, you can use $pages->save($page, [‘noHooks’ => true])
  14. Temporarily comment out the following line in Page.php: throw new WireException("You may not modify '$key' on page '{$this->path}' because it is a system page"); This will allow you to clone the home page via the API: $homePage = $pages->get('/'); $pages->clone($homePage, $homePage, false);
  15. Temporarily comment out the following line in Page.php and you will be able to clone the home page via the API: throw new WireException("You may not modify '$key' on page '{$this->path}' because it is a system page");
×
×
  • Create New...