MarkE
Members-
Posts
1,047 -
Joined
-
Last visited
-
Days Won
12
Everything posted by MarkE
-
The hooks are attached in ready(). They don't work at all in init() - it is too early. But there is no duplicate calling of the hooks, so how is priority relevant?
-
Thanks for that. Aside from the problem with calling the hook from the module ready(), the listable hook I am using works perfectly, with additional code I added to display the correct number of children.
-
Yes - it makes no difference. On the dev site, I can track exactly what happens by stepping through with Xdebug. The only code between the two bardumps is a couple of WireData 'gets' for 'return' returning true. It would be great to be able to do the same in the live environment!
-
Thanks @flydev and I will follow up on the priority matter. I have been using profiling as well as AI, and it has been more helpful. As you can see from this other post I have boiled the issue down to one of purely where the hooks are called from - ready.php or the module ready(). So I am 99.99% certain it is nothing to do with roles and permissions.
-
I have boiled this down even further and posted the findings here:
-
I have now looked into this in more depth and am even more puzzled. In ready.php, I have two hooks: wire()->addHookBefore('ProcessPageList::execute', function(HookEvent $event) { $process = $event->object; $listable = $process->page->listable; bd([$process->page, $listable], 'page, listable'); if(!$listable) bd(debug_backtrace(), 'backtrace not listable'); }); wire()->addHookAfter('Page::listable', function(HookEvent $event) { $event->return = true; bd($event->return, 'listable return'); }); In the dev environment, this always yields the correct result, regardless of whether the hooks are called from /site/ready.php or my module ready(): In the live environment, the result is correct only if the hooks are called from /site/ready.php. If the hooks are called from my module ready(), the result is:
-
A bit of an update. Firstly, the problem was caused by ready(), not init(), so my comment above was incorrect. What I did was Moved all the code from init.php into a similar file in my module folder, then added include('init.php'); in the module's init() method. That seems to work OK. Did a similar thing with ready.php (including it from the module's ready() method). That worked OK in the dev environment, but in the live environment gave the following error on viewing the page tree: You don't have access to list page / So I put some hooks in ready.php to track the listable status: wire()->addHookBefore('ProcessPageList::execute', function(HookEvent $event) { $process = $event->object; bd($process, 'process'); bd(debug_backtrace(), 'backtrace in processPageList execute'); bd($process->page->listable, 'listable'); if(!$process->page->listable) bd(debug_backtrace(), 'backtrace not listable'); }); and wire()->addHookAfter('Page::listable', function(HookEvent $event) { $page = $event->object; $currentUser = $page->wire()->user; bd([$page->path, $currentUser->id], 'PagePath, UserId In listable hook'); bd(Debug::backtrace(), 'backtrace in listable hook'); .... I then ran the code in two ways: (a) including the module ready.php from the site ready.php file (otherwise empty) and (b) including it in the module's ready() method. In the dev environment, both these work. In the live environment, (a) yields the following: whereas, with (b), I get : The only difference is that in (a), listable is true, whereas in (b) it is false. To repeat, this is running exactly the same ready.php code on exactly the same database - the only difference is in how it is called. The calls are made from ProcessWire.php (lines 664 to 676): // call any relevant internal methods if($status == self::statusInit) { $this->init(); } else if($status == self::statusReady) { $config->admin = $this->isAdmin(); $this->ready(); if($this->debug) Debug::saveTimer('boot', 'includes all boot timers'); } // after status include file, names like 'init', 'ready', etc. $file = empty($files[$name]) ? null : $path . basename($files[$name]); if($file !== null) $this->includeFile($file, $data); The only significant difference seems to be that the module ready is called earlier, but there is no processing between the calls. GitHub copilot is completely stumped (as am I), so I am hoping that some expert humans might be able to shine a little light ?
-
The problem is caused by moving all my ready.php into a module ready(), which works in the dev environment, but not in live. See my second post. I'm still working on this one!
-
Version 0.0.22 adds the ability to use the inputfield in module config fields (i.e. in a module's getModuleConfigInputfields method) but you need to create a measurement object and convert it to an array before it is saved. For example: public function getModuleConfigInputfields(InputfieldWrapper $inputfields) { $modules = $this->wire()->modules; ..... //Timeouts $m = $modules->get('FieldtypeMeasurement'); $f= $modules->InputfieldMeasurement; $field = new Field(); $field->setFieldtype($m); $field->set('quantity', 'Time'); $field->set('units', ['second', 'minute', 'hour', 'day']); $f->setField($field); $f->setPage($this->page); $f_name = 'sessionTimeOut'; $f->name = $f_name; $f->label = $this->_('Session Timeout'); $f->description = $this->_('Set the session timeout period'); $f->notes = $this->_('Max recommended = 1 day'); $f->columnWidth = 50; // Get the posted values $postedMagnitude = $this->input->post["{$f_name}_magnitude"]; $postedUnit = $this->input->post["{$f_name}_unit"]; if($postedMagnitude && $postedUnit) { // Create a new Measurement object $measurement = $m->measurement("Time", $postedUnit, $postedMagnitude); // Save the posted value $f->value = $m->measurement_to_array($measurement); $this->$f_name = $f->value; } else { // Set the value of the input field to the saved value $f->value = $m->array_to_measurement($this->$f_name); } $inputfields->add($f); } ..... }
-
-
Version 2.0.16 now released. This comprises mosly bug fixes after extensive testing on some really complex migrations involving many templates fields and pages. In particular, the module will automatically carry out multiple migration installations to deal with the problem of circular dependencies between changed objects. Not sure how many people are using this module, but any feedback is welcome.
-
I think I may have identified the problem with the 'getFresh'. It seems that PageAutosave was turned on. I have refined the module settings to only use it for certain templates. I guess it saves with ‘no hooks’ so it wasn’t showing up. I’ll check it out more thoroughly in the morning. UPDATE. I can confirm that the problem was caused by having autosave inadvertantly turned on (+ not realising that it suppressed hooks). This was not helped by the editing taking place in a modal, so it was not obvious. I assume that initially it was turned on in the live environment but not in the dev. The problem was intermittent because, if you hit 'save' quickly enough it does the user-initiated save before the autosave. One to watch!!
-
Thanks @poljpocket. That seemed to be the case initially. However, with this problem it seems to be occurring intermittently in both environments (mostly it goes wrong, but occasionally it is right). I don't think Page has a 'changed' method - hookable or not. I assume you mean $page->isChanged('tickets'). That suffers from exactly the same problem, which is not a great surprise because using $event->arguments(1) was failing to register the change sometimes too - see the top (last) entry in the screen dump of the log.
-
I don't think this is related to the module init() problem. It seems to be to do with the 'oldPage' code above (or getFresh, which is simpler and has the same effect). The problem is, that in my 'before save' hook, the 'getFresh' seems to have the updated field, not the previous one. HSince I am hooking both before and after the page save, I can see that there is no save occurring in between which might update the database version. However, the problem is intermittent with no cause that I can identify. See the log screenshot below, where the sequence of user actions were to change 'tickets' from 3 to 5 and save then from 5 to 6 and save. Reading in time sequence (bottom up), the first update is correct: In the 'before' hook the old tickets field is 3. The next 4 entries are from the 'after' hook. I am getting the changed fields from '$event->arguments(1)' and it shows the tickets field as changed (to 5 tickets). The next line is from saving a different page Then in the next 'before' hook we see that the tickets from the 'getFresh' is now 6, although the last save was with 5 tickets, also there are no field changes detected. Any ideas what might be happening?
-
Thanks @flydev. What are you using to show that - xhprof?
-
I'm planning to change provider anyway (to Krystal) so might just accelerate this! Not always, but I have tried this (as superuser and with debug=true restricted to my IP address). I'll investigate some of the latest problems with these on. Not quite sure of the best way of doing this. The code is part of a fully-integrated membership administration and website system that I initially built for one organisation and am generalising for wider use. A demo system will be available when I have changed provider, but the code is proprietary. I could just do a simple site with the code, but it may be that it all works in a much simpler set-up. If I can buid a simple reproducible example then it probably means that I can fix it too!
-
BTW, I am having another problem with differences between the dev and live environments. This may be unrelated to the module issues, but here goes anyway: I have a couple of hooks - ...addHookBefore('Pages::saveReady'... and ...addHookAfter('Pages::saved'... - and am using these to track changes to a specific field. I do this by this code in the before saveReady hook $p = $event->arguments(0); if ($p->id) { // Don't carry out this hook for new pages - only for edits // Get the previous version of the page $oldPage = wire('pages')->getById($p->id, array( 'cache' => false, // don't let it write to cache 'getFromCache' => false, // don't let it read from cache 'getOne' => true, // return a Page instead of a PageArray )); In that hook I can save a temporary field (e.g. $p->oldTickets = $oldPage->tickets) and compare that with the new field in the 'after saved' hook. This works fine in the dev system. However, in the live system $oldPage seems be the same as $p in the before hook and $p does not have the updated field in the after hook. I tried getFresh as well as the above code, but that doesn't work either.
-
Thanks @BrendonKoz, @flydev, @poljpocket. I have actually had the problems with two separate modules, both of which are autoloaded. I have ProDev tools so will try the profiler, but I suspect that stepping through with xdebug may be necessary. I have a feeling it will take a while to diagnose and am under some time pressure to complete this project, so I might need to accept a less-than-ideal solution for now and come back and investigate in more detail when I have time. But thanks for the thoughts and I will post back with any findings.
-
I'm not sure that there is an obvious answer to this as it seems to be related to a bunch of other inexplicable problems I have been having which are peculiar to my live envronment. See my comments here.
-
In order to keep all my module-related code in one place and encouraged by the experience of others (particularly @bernhard), I moved the contents of site/init.php into a module's init() method thinking that the code would be called at a similar time. However, I am now not convinced this is a great idea, but I don't understand enough about PW's inner workings to see what is actually happening. The problem is that, while everything works fine in my dev environment, various random things break in the live system. I have no idea why, as the code, database, PW version and PHP version are the same. I have already documented here the problem that occurred with page classes. I fixed that by moving the include for the page classes from init() to __construct(). I then had another problem noted here plus a few other minor issues with functions declared in init(). The problems were resolved by moving the code back into init.php. None of these problems occurred in the dev environment. (BTW I had done something similar with 'ready' - I'm not sure that caused the problems but I moved it all back into ready.php just in case). This has been very frustrating as it has broken my site several times despite testing everything carefully in the dev system first. It also means that I can't gather all the module-related code together. I'm wondering whether there might be something odd with my hosting service (which I am thinking of changing anyway) but without a detailed understanding of the flow of control in PW I don't even know the right questions to ask. Any thoughts, comments or pointers to where I can look to get a better understanding of the PW processes would be most welcome.
-
For some reason no users other than superusers can see the page tree on a site. The message is "You don't have access to list page /" That seems to imply they don't have access to the home page, but looking at the access to the home page, there are no restrictions. Any ideas?
-
Just a brief 'sort of' tutorial based on my experiences documented here. I have built a couple of modules in the past which created and used their own specific templates and which had page classes associated with these. My approach has been to put the scripts containing the page classes in the same directory as the module (rather than the classes directory) so as to keep all the module-related stuff together. I would then'include/require' those scripts in my module init() method. This worked very well until recently. Then, as documented in the above link, I found that in some circumstances (the details of which still escape me but might be to do with the number of templates involved) the page classes were not always being applied in time for all templates. My solution is to put the include/require in the module's __construct() method as it is called before init(). This seems to work, but bear in mind that the full API is not available at this time.
-
Dev and live are both on 8.1 I've now put the 'includes' in the module __construct() method rather than init() or config.php. That seems to work. I'll do a sort of mini-tutorial on this.
-
In case anyone is interested ( @Andi, @szabesz, @bernhard?) I may have found a (sort of) solution to this. As I said I moved the include (actually require_once) to config.php rather than in init() and that seems to fix it (still testing...). Though why it was working before when called from init() but not now, I do not know. Also, I really want to call the includes from my module as the custom pages are module-specific - I don't want them hanging out in config.php. Any ideas?
-
I've often done that with templates (habit from early days with PW, so I could easily distiguish template and field names) without problems. The system was working fine with those template names until I changed some legacy custom page classes which I had 'included' via config.php to use the 'new' $config->usePageClasses = true; - this involved adding 'Page' to the end of the file names and classes. All of those work correctly! It's (some of ) the classes that were being added via an include in my module init() that have stopped working (and just on the live site), even though I did not change those! FWIW, this problem happened after my hosting service had a major SQL corruption problem which caused sites to be down for some time until they restored from a backup. However I checked the site after that problem and before I made the changes and everything seemed OK. I can do this for the site as a whole using cPanel's JetBackup. But that is a last resort as it undoes the improvements I was trying to make. Before that, I was going to try catching the pages before rendering them and changing the class, if wrong, using reflectionObject() - see code below - although that is a hack and doesn't fix the underlying problem. /** * Class casting * * @param string|object $destination * @param object $sourceObject * @return object */ public static function cast($sourceObject, $destination) { if(is_string($destination)) { $destination = new $destination(); // or replace by below to avoid calling constructor // $destination = (new \ReflectionClass($destination))->newInstanceWithoutConstructor(); } $sourceReflection = new \ReflectionObject($sourceObject); $destinationReflection = new \ReflectionObject($destination); $sourceProperties = $sourceReflection->getProperties(); foreach($sourceProperties as $sourceProperty) { $sourceProperty->setAccessible(true); $name = $sourceProperty->getName(); $value = $sourceProperty->getValue($sourceObject); if($destinationReflection->hasProperty($name)) { $propDest = $destinationReflection->getProperty($name); $propDest->setAccessible(true); $propDest->setValue($destination, $value); } else { $destination->$name = $value; } } return $destination; }