Jump to content

MarkE

Members
  • Posts

    1,047
  • Joined

  • Last visited

  • Days Won

    12

Everything posted by MarkE

  1. TLDR: Use custom selectors in page field selector: - check_access=role1|role2 ... - to control who can see results - field=[item.id] - to select on id of repeater item containing the page field. ------------ This module extends the capabilities of selectors specified in the 'input' tab of a page reference field, specifically when that field is part of a set of dependent selectors which may be inside a repeater item. The readme also attempts to bring together various existing documentation regarding the use of dependent page selectors and the enhancements that were provided by various PW versions. See https://github.com/MetaTunes/CustomDependSelects for full readme and to download. Please note that this is an initial alpha release. Please test in your own context thoroughly before using. Also note that PW3.0.200 is required and 3.0.203 is preferred.
  2. Actually, I am building a little module to fix it, hopefully ? -Edit: You can now find it here.
  3. Thanks @AndZyk. I am using dependent selects. So it needs check_access=0 in the input tab as you suggest for InputfieldPage to get it. However for dependent selects, ProcessPageSearch is called to populate the options and strips out the 'check_access'. Then, when the options don't include the already-selected item provided by InputfieldPage, it is removed ? I think perhaps I should raise this as an issue or feature request.
  4. Thanks @AndZyk. I had tried check_access, but it didn't work. I found out why - because ProcessPageSearch contains the following: // don't allow setting of check_access property, except for superuser if($lowerName == 'check_access' && !$superuser) continue; So I sort-of hacked it with a hook: wire()->addHookAfter('ProcessPageSearch::findReady', function(HookEvent $event) { $selector = $event->return; $user = wire()->users->getCurrentUser(); if($user->hasRole('webmaster')) $selector = 'check_access=0, ' . $selector; $event->return = $selector; }); which works, but I'm not very happy with it!
  5. Thanks @bernhardfor your detailed response. The best of both worlds would be nice if it makes sense. First I'll do a few more improvements to my module and then try and respond to the request for a working example, before considering the way forward from there.
  6. I have a number of pages which host dependent select fields. The contents of these are drawn from various repeater fields (actually repeaterMatrix, but I don't think that's relevant to the problem) - i.e. they are provided by a selector which is something like "template=repeater_field_name, ...". They all work fine in superuser, but in any non-superuser role, they return an empty result, even if the role has the maximum permissions. It seems like every repeater template needs to specifically permit access to the required roles, because they are all children of 'admin'. Is this so? Is there a shortcut way of doing this (there are a lot of such fields)? Edit: I guess what I would like to do is to set the access to the repeater page to be the same its host page (getForPage) rather than admin. Any ideas?
  7. Confused @bernhard? I’m not sure what is confusing. Did you read the manual? I’m happy to try and clarify if I know where the problem is.
  8. In case you hadn't realised, there is extensive documentation of the module at https://metatunes.github.io/DbMigrate/help.html. So you don't need to install and use the module to get an understanding of what it does. Specifically as regards running code on migrations, see the following sections: https://metatunes.github.io/DbMigrate/help.html#snippets and https://metatunes.github.io/DbMigrate/help.html#hooks. I'm happy to supply a full soup-to-nuts example if it helps, but that will take a little time. Perhaps reading the help file and reviewing this (completed) example might be explanantion enough. (Note that this example does not include ready.php code for uninstalling as I had fully tested everything and was confident I didn't need to write it. If for some reason it was needed, I could have modified ready.php to hook before and after uninstallMigration() in a similar manner). Let me know if you want a fuller example; also if anything in the help file is not clear as I'll gladly improve it.
  9. The new version looks good @bernhard and I am very tempted to give it a proper try. However, I have been using my own ProcessDbMigrate module successfully on a number of projects and it is serving me pretty well (and the change in approach would be quite radical). Although the two are quite different in methodology, there are some similarities in that my json files have similarities to your yaml files (btw, not quite sure what advantages yaml has over json in this context - json has coped so far). My module is still very much alpha as something this complex does need extensive testing and bugs do crop up (as well as the need to extend to handle additional field types etc. - the recent version 0.1.5 has been updated to include FieldtypeRepeaterMatrix). Sometimes I think about re-engineering it to use RockMigrations methods rather than the current native ones, but that does lead to some head-scratching. For example, my module will handle uninstallation of migrations (provided they have not been conflicted by subsequent changes) - I don't see how RockMigrations does this (reverting the code would leave the database unchanged?). Also I can attach hooks to run on installation (and uninstallation) to handle any data changes related to the migrations (e.g. say field 'name' is split into 'first_name' and 'last_name', the code can populate the new fields when the installation is run). I assume that RockMigrations can handle that too (at least for the forward install), but not quite sure how. I know you have commented in the past that you don't like my approach (although I'm not sure of the precise reasons**) but I think it is a valid alternative and I would like to somehow get the best of both worlds. ** I can see that using Git to handle conflicts has some advantages. My module has to include its own conflict management which works but has not been tested in a multi-developer environment. Mind you, I can envisage semantic conflicts that might arise which Git would not spot - separate modules changing the same field for example.
  10. It’s quite difficult to debug code from a few snippets (maybe someone smarter than me can…). I suggest you install the TracyDebugger module and insert some bd() statements to see what is going wrong.
  11. 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.
  12. 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.
  13. 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 ?
  14. 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; });
  15. 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...
  16. 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 ?
  17. 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 ?
  18. Methods have parentheses, properties do not. Check the API reference if you are not sure.
  19. 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:
  20. 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.
  21. I'm wondering if this could be achieved with a module...?
  22. MarkE

    Using Latte

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

    Using Latte

    Have you read this @Greg Lumley?
  24. 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.
×
×
  • Create New...