Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by MarkE

  1. Check out the Options Fieldtype. Sometimes I find a page ref field is better than an Options field - easier to add/change. It depends whether this is something that needs to be flexible or not.
  2. That looks like the way to go to me. There's no way you can hack the Access Roles form as shown in your image, as all the relevant methods are protected an unhookable.
  3. Sorry @jploch, but its not really clear to me what you want this new permission to do and how you want it to operate. Pretty much anything is do-able in PW ๐Ÿ˜€
  4. Not sure exactly what you are wanting to achieve but, if it helps, here is some code I used to hook User::hasPagePermission. Obviously the context is specific to my app (a membership system), but maybe there are some useful ideas. /** * PAGE PERMISSIONS * Allow member users to edit etc. pages relevant to them (access via AIM in My NCOG) **/ wire()->addHookAfter('User::hasPagePermission', function(HookEvent $event) { // Get the object the event occurred on, if needed $user = $event->object; // An 'after' hook can retrieve and/or modify the return value $return = $event->return; // Get values of arguments sent to hook (if needed) $permission = $event->arguments(0); $p = $event->arguments(1); /* Your code here, perhaps modifying the return value */ // Is it a page we need to customise access for? $templates = ['Membership', 'Member', 'Profile', 'Subscription', 'Payment', 'MemberShop', 'Booking', 'NewsItem']; $permissions = ['page-edit', 'page-add']; $currentUser = $this->users->getCurrentUser(); if ($p and in_array($p->template, $templates) and $permission and in_array($permission, $permissions)) { if ($currentUser and $currentUser->isLoggedin()) { $this->log->save('debug', 'In hasPagePermission hook for Page = ' . $p . ', Permission = ' . $permission . '. Current user is ' . $currentUser); if ($currentUser->hasRole('member')) { $email = $currentUser->email; $memberPage = ($email and $email != '') ? $this->pages->get("has_parent=/memberships/, template=Member, email=$email") : null; if ($currentUser->memberOnly != 0) { $return = false; $this->log->save('debug', 'Member only'); } // Member-only users can only access pages as defined below // Admin members will also have their normal permissions if ($memberPage and $memberPage->id) { $membershipPage = $memberPage->parent; $this->log->save('debug', 'Membership page = ' . $membershipPage->id); $profilePage = $membershipPage->id ? $membershipPage->get("template=Profile, include=hidden") : null; $siblings = $memberPage->siblings("template=Member, include=hidden"); // includes member page itself $shops = $membershipPage->id ? $membershipPage->find("template=MemberShop, include=all") : null; $currentSub = $membershipPage->id ? $membershipPage->latestSubscription() : null; $subsPaymentPages = $membershipPage->id ? $membershipPage->find("template=Payment, parent=[template=Subscription], include=hidden") : null; $draftNewsPages = $membershipPage->id ? $memberPage->find("template=NewsItem, include=all") : null; $bookingPages = $membershipPage->id ? $membershipPage->find("template=Booking, include=hidden") : null; $editablePages = new PageArray(); $addablePages = new PageArray(); $editablePages = $editablePages->add($membershipPage)->add($profilePage)->add($siblings)->add($currentSub)->add($shops)->add($subsPaymentPages)->add($draftNewsPages)->add($bookingPages); $addablePages = $addablePages->add($membershipPage)->add($memberPage); // pages where creation of children is allowed $this->log->save('debug', 'Editable pages = ' . $editablePages->implode(', ', 'id')); $this->log->save('debug', 'Addable pages = ' . $addablePages->implode(', ', 'id')); if ($editablePages->has($p) and $permission == 'page-edit') $return = true; if ($addablePages->has($p) and $permission == 'page-add') $return = true; } } $retStr = $return ? 'true' : 'false'; $this->log->save('debug', 'Returning ' . $retStr); } } // Populate back return value, if you have modified it $event->return = $return; });
  5. I guess this is largely a matter of personal preference. My personal preference is for a written description that can be accessed other than just while coding. This means that one can get an at-a-glance overview and dip in for more detail, without having downloaded the module and fired up the IDE. Call me sad, but I sometimes read this stuff sat in an armchair, not at the workstation ๐Ÿ™‚. I gave the example above of the readme for FieldtypeMeasurement. Perhaps the ideal approach is to have all the documentation in PHPDoc. This is pretty much the approach of PW, so that then the help documentation (API ref) can be generated automatically from the code. If that approach is chosen, then a bit more explanation in the PHPDocs would be helpful. For example, the PHPDoc for alfred is /** * ALFRED - A Lovely FRontend EDitor * @return string */ There is no description of the options and their defaults (although these can be seen by inspecting the code). PW PHPDocs tend to include option descriptions. See $pages->find for a (very full) example /** * Given a Selector string, return the Page objects that match in a PageArray. * * - This is one of the most commonly used API methods in ProcessWire. * - If you only need to find one page, use the `Pages::get()` or `Pages::findOne()` method instead (and note the difference). * - If you need to find a huge quantity of pages (like thousands) without limit or pagination, look at the `Pages::findMany()` method. * * ~~~~~ * // Find all pages using template "building" with 25 or more floors * $skyscrapers = $pages->find("template=building, floors>=25"); * ~~~~~ * * #pw-group-retrieval * * @param string|int|array|Selectors $selector Specify selector (standard usage), but can also accept page ID or array of page IDs. * @param array|string $options One or more options that can modify certain behaviors. May be associative array or "key=value" selector string. * - `findOne` (bool): Apply optimizations for finding a single page (default=false). * - `findAll` (bool): Find all pages with no exclusions, same as "include=all" option (default=false). * - `findIDs` (bool|int): 1 to get array of page IDs, true to return verbose array, 2 to return verbose array with all cols in 3.0.153+. (default=false). * - `getTotal` (bool): Whether to set returning PageArray's "total" property (default=true, except when findOne=true). * - `loadPages` (bool): Whether to populate the returned PageArray with found pages (default=true). * The only reason why you'd want to change this to false would be if you only needed the count details from * the PageArray: getTotal(), getStart(), getLimit, etc. This is intended as an optimization for $pages->count(). * Does not apply if $selector argument is an array. * - `cache` (bool): Allow caching of selectors and loaded pages? (default=true). Also sets loadOptions[cache]. * - `allowCustom` (boolean): Allow use of _custom="another selector" in given $selector? For specific uses. (default=false) * - `caller` (string): Optional name of calling function, for debugging purposes, i.e. "pages.count" (default=blank). * - `include` (string): Optional inclusion mode of 'hidden', 'unpublished' or 'all'. (default=none). Typically you would specify this * directly in the selector string, so the option is mainly useful if your first argument is not a string. * - `stopBeforeID` (int): Stop loading pages once page matching this ID is found (default=0). * - `startAfterID` (int): Start loading pages once page matching this ID is found (default=0). * - `lazy` (bool): Specify true to force lazy loading. This is the same as using the Pages::findMany() method (default=false). * - `loadOptions` (array): Optional associative array of options to pass to getById() load options. * @return PageArray|array PageArray of that matched the given selector, or array of page IDs (if using findIDs option). * * Non-visible pages are excluded unless an "include=x" mode is specified in the selector * (where "x" is "hidden", "unpublished" or "all"). If "all" is specified, then non-accessible * pages (via access control) can also be included. * @see Pages::findOne(), Pages::findMany(), Pages::get() * */ I use PHPStorm, not VSCode. It has a structure view similar to VSCode's outline. However, that just lists the method names etc. - I assume VSCode is similar* - so you need to go to the actual code to get the PHPDoc. In any case, you do need to be at the workstation and to have downloaded the module to see this. As I said, I appreciate that this is a personal thing, so please don't take it as a criticism, but you did ask whether a readme would be just as good, to which my answer is The video is very useful to give an introduction, but is longer than it would take to view a readme. Ideally there would be both, but the readme would be more complete, but less wordy (as described above). *PS I downloaded VSCode and I see that the outline does give variables as a drop-down, but not PHPDoc. Perhaps I should investigate it a bit more...
  6. What do you mean? I mean a summary of the functions and methods, including description of the arguments / options, what the method does / returns and any other notes regarding usage. Either a bit like the main PW API ref or what I did for my FieldtypeMeasurement module (not that I'm holding that up as a great example). Similar comment applies to RockMigrations ๐Ÿ˜‰
  7. Great video. Really comprehensive (but I had to view it in more than one sitting ๐Ÿ™‚ ). I would appreciate a separate guide to the API that one can dip into. And a really great-looking module ๐Ÿ˜€
  8. Methods have parentheses, properties do not. Check the API reference if you are not sure.
  9. I'm trying to use the owner selector to select on the id of the 'owner' page for a repeater. For most of the repeaters I have, this seems to work fine. However, there is one instance where it doesn't work and I can't see why not. As a simple test, I used a selector like: $p = $pages->get("template=repeater_sale_item, sale_item.owner.id=25348"); That works, but $p = $pages->get("template=repeater_stage, stage.owner.id=19363"); returns a null page. Investigating more closely, the generated 'owner selector' for this is 'templates_id=43|53, include=all, get_total=0, id=19363, stage.count>0' It is stage.count>0 that is causing the problem - if I remove that from the selector then it finds the owner page correctly. However this owner page DOES have 9 'stage' repeaters and executing d($page->stage->count); on the page in the Tracy console yields the correct result of 9. None of the stages are hidden/unpublished etc. So why does stage.count>0 fail? ๐Ÿค” See the image below to illustrate the issue:
  10. Good plan. I was also thinking that you could use config inputfields to define the mapping (as types allow) and to specify a custom form layout. Not sure if I will get around to trying it*, but maybe @Kiwi Chris might like to. My attempt at documenting these types of modules might help. *I am pondering rewriting ProcessDbMigrate to operate with its own database (a bit like TextformatterHannaCode) but, even if I do, it is unlikely to happen in the near future.
  11. I'm wondering if this could be achieved with a module...๐Ÿค”
  12. MarkE

    Using Latte

    But he did mention that he would be releasing a module shortly.....
  13. MarkE

    Using Latte

    Have you read this @Greg Lumley?
  14. This has attractions. But it would require PW to update the file path every time the page was moved or renamed - there would be other complications too - e.g. including an image in a RTE field in another page would mean that the html would need to be updated when the host page is moved/renamed. It might be possible to test out the idea using hooks, but my guess is that it would cause more problems than it solves. The method I use in DbMigrate is to do all the referencing in the generation of the migration using the path/name rather than the id and then using that to get the corrrect id for the files. For RTE fields, an id map is maintained $idMapArray[$page->meta('origId')] = $page->id; so that the html can be changed. This only needs to be done for a migration, not every time a page gets moved. So I think, on balance, my vote is to keep the id-based naming.
  15. I'm not sure how this would be implemented. "Universal" implies it would be independent of the environment, but if a new page (and associated files) is created in the dev env, how can you create a universal id that you know will be unique in the production environment (unless the dev environment has knowledge of the production environment which, in PW, it does not). I came across this problem in developing ProcessDbMigrate. If you use that to migrate pages, rather than the standard export/import, then hopefully it will resolve the ids correctly. See https://metatunes.github.io/DbMigrate/help.html#field-types. NB: If you do use my module, please take care to take backups of both environments before installation and before any migrations - it is still very much 'alpha'!
  16. Very good advice. You might also like to try my ProcessDbMigrate if you don't want to go the RockMigrations route. However it does struggle with some fields - like nested repeaters. I have temporarily paused developing it while I review the best way forward, but you are welcome to try it and I will try and provide support for any problems. See the support thread here and the github here.
  17. For some reason, this doesn't always work. However, I found that $object->addClass('no-selectize', 'wrapClass'); $object->removeClass('sa-selectize', 'wrapClass'); did the trick when just the first line did not. In fact, just the second line by itself was enough. Not sure if it's relevant, but this was on a field in a repeater matrix item.
  18. This looks a nice idea and I was looking to implement something similar on a site of mine. However, many pages have repeater matrix fields. If these are changed directly, rather than through the host page edit, then the modified date for the host page is not changed (even though the updated results are shown). I guess there are two ways of fixing this: hook on saving the repeater item to set the new mod date for the host page check all mod dates - for host and repeater pages - rather than just the host page Any ideas which approach is more efficient? I guess I'm more inclined to option 1.
  19. I look forward to that. I have one to do coming up, although much simpler than yourโ€™s sounds. Previously, Iโ€™ve just started from scratch, using the WP site as the spec, but maybe there are better ways.
  20. Hmm. I can't see anything wrong with the code you quoted. Do you have Tracy Debug installed (if not then I really recommend it). If so, try your code in the console (when you are on the page in question).
  21. When you create a repeater field, say 'fieldname', PW automatically creates a template called 'repeater_fieldname'. It looks like you are using this, not the actual field name. If you actually called your field 'repeater_agenda' then the template will be called 'repeater_repeater_agenda' (check your templates list with system templates filter turned on). I wanted to eliminate the possibility that you might be calling the template rather than the field.
  22. @lenoir I would expect the field name to be 'agenda' and for 'repeater_agenda' to be the name of the (system) template it uses.
  23. Is repeater_agenda your field name? It looks more like the template name.
  24. I have updated the initial post to suggest how to provide full context flexibility, including for repeater matrix items. See also
  • Create New...