Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Posts posted by da²

  1. @Krit65 Just in case, do you know the common way to translate modules in PW admin? Menu Setup > Languages > Your Language > Find files to translate > Select LoginRegister file > Translate what you need.


    If it's needed to make translations editable from frontend, I would use PW translator to save phrases. I never used this API, I did a test but I don't know why it's breaking my existing translation file (it removes all previous translations and leaves 'file' and 'textDomain' empty):

    $language = wire()->languages->getLanguage("english");
    /** @var LanguageTranslator $translator */
    $translator = wire(new LanguageTranslator($language));
    $textDomain = '/site/templates/account-edition.php'; // Domain for LoginRegister should be '/site/modules/LoginRegister/LoginRegister.module'
    $translator->setTranslation($textDomain, 'Logout', 'My translation');
    $translator->saveTextdomain($textDomain); // Breaking my existing translation file


    Another way, closer from what you're trying to do, is to hook one of the LoginRegister methods that build forms and change text values on the fly using your term-translation page, so in ready.php (code not tested):

    wire()->addHookAfter('LoginRegister::buildLoginForm', function (HookEvent $event) {
      $terms = $pages->get('template=term-translation');
      /** @var InputfieldForm $form */
      $form = $event->arguments(0);
      $myField = $form->getChildByName('Email');
      $myField->set('label', $terms->myTranslationField);

    Take care that there are several hookable methods that build a form in this module, ___buildLoginForm, ___buildRegisterForm... (check source code).


    • Like 2
  2. 8 hours ago, Krit65 said:

    Because the URL of the page I am trying to translate

    Why not using PW translation mechanism? LoginRegister module is ready to translate, you just have to enable languages support, create a new language, search the LoginRegister module in translations admin page and translate it. You may also find an already prepared language pack for your language (not sure it translates LoginRegister module but maybe, at least you'll get lot of PW translations already done).


  3. OK that's because cache is cleared just before the sorted hook callback, but instance given in callback is the "old" one, yes that makes sense when you look at implementation. ^^

    26 minutes ago, poljpocket said:

    What is wrong with your commented-out line instead of $page->save() outside the loop?

    Nothing is wrong, that's a long story.
    Method with children loop is also called by several hooks, including a saveReady, so at first I didn't need to save current page in loop because PW was gonna save it at end.
    So when I added sorted hook, I saw that I had to save() the page myself so did it in the hook because other method was not doing it, and that didn't worked.
    Finally I refactored this method to take in parameter if it's needed to save current page.

    • Like 1
  4. 1 hour ago, poljpocket said:

    You can see that there will be a saveReady and save hook triggered on the page immediately before the sorted hook is triggered.

    Yes, sorted hook comes after saveReady one, so I have to do a save() myself (or use saveReady and check the sort change myself).

    I just reproduced the problem I was talking about in first post:

    wire()->addHookAfter('Pages::sorted(6019)', onSorted(...));
    function onSorted(HookEvent $event): void
        /** @var Page $page */
        $page = $event->arguments(0);
        // This code was in another function used by several hooks.
        // Its job is to update all children in a certain order.
        foreach ($page->parent->children() as $child) {
            if ($child->id == $page->id) {
                $child->title .= "-";
    //            $child->save(); // Save new title
        // And this code was in the hook callback.
        $page->save(); // Does not save new title

    Saving the instance given by hook is not working, I don't know if it's expected, why cache is not giving both times the same instance?

  5. It's getting harder to remember exactly what happened, since code is a bit complex, using multiple hooks, was updated since I posted this topic, and what happens in PW is not totally clear for me.

    I just checked something in my new code (fixed one), and find that the changes I do to the page (in sorted hook) are not saved if I don't save it myself, even if sorted is followed by saveReady and so page should be saved. This changes are done on the instance returned by find() (children() run on parent page, exactly).
    Maybe I have a cache issue in my code and should investigate more. And maybe I should try to reproduce this in an easy code.


    7 hours ago, poljpocket said:

    you always should compare IDs instead of instances of a page

    Sure, I never use instances.

  6. 7 hours ago, MarkE said:

    I have a json based migrations module that I released some time ago (ProcessDbMigrate).

    Did you ever think of using fields and templates data directly from database? ("fields" and "templates" tables)

    On my current project, while the site is still in closed beta I just push my dev database to remote server, so updating is easy. But a time will come when I'll have to replicate my changes without erasing production site. 😄 I think about testing RockMigrations but I'm also thinking about something more targeted to my main needs: most of the time I'll just add and configure new/existing fields and templates, and it looks like it can be done with "fields" and "templates" database tables.

    I don't know how ProcessDbMigrate works, I looked the code very quickly and think you parse PW data using API, like RockMigration does if I'm right. Dunno if directly using the database is a good idea, but that looks faster to implement and more robust to me actually because you don't need to take care of future PW features or changes to add them in your JSON exporter/importer.

    Just sharing some thoughts...

  7. I don't remember exactly why I thought of getFromCache option, probably cause of this in find() documentation:


    loadOptions (array): Optional associative array of options to pass to getById() load options.

    and this in getById():


    getFromCache (bool): Allow use of previously cached pages in memory (rather than re-loading it from DB)? (default=true)

    But I didn't even specified the key 'loadOptions', and anyway true is default... I had a hard day when I posted this message. 😄

    About cache cleared after save, in a hook when I save a page (other than the one triggering the hook) I use: 

    $page->save(options: ['noHooks' => true, 'uncacheAll' => false]);

    It's coming from an advice on this forum, section "Page cache issues...".

    So when I saved the last page I suppose cache has not been cleared, but I didn't investigate more since, I just moved this last save() in the function working on instances found via children() (find()) function.

    But that may be related with that.

  8. @joe_g

    I thought it was easy to solve with a hook, but there's something strange:

    1. I create a page named "test" without unique status,
    2. I add a hook that set next pages as unique,
    3. I create another page "test" in a different parent,
    4. Page is saved without problem with "unique" flag and name "test", even is another page in another parent uses this name,
    5. If I remove the hook and try to do the same by manually checking the box: PW denies because another page uses this name.

    So setting "unique" via a hook doesn't trigger the unique check when saving page, maybe @ryan can help.

    I used this hook so unique flag is set before saveReady:

    $this->addHookBefore('ProcessPageEdit::buildForm', function (HookEvent $event): void {
        /** @var Page $page */
        $page = $event->object->getPage();
        if ($page->template->name == 'hotlap')


  9. Hello,

    EDIT: this original post wasn't clear, code in this post is way more explicit to show the thing I'm talking about.


    I'm not asking how to manage this problem, but is it a bug? And if not, could you explain why this behavior?

    To make it short: why is the Page instance given by HooEvent->arguments(0) not the same as the one given by find() function (children() here)? Shouldn't it be cached and the same?


    My use case: I have a method "foobar" called by several hooks. This method process sibling pages of the one received in parameter (using $page->parent->children()), and save the other pages if modified, but does not save the one received in parameter because it can come from a saveReady hook and so the page is already being saved.

    The problem comes from a Pages::sorted hook, after calling "foobar" method I save myself the page using the Page instance given by hook event ($event->arguments(0))... And so I'm not saving anything because it's not the same instance that was modified in "foobar" method.

    I know how to manage this, but that looks like a cache bug, isn't it?
    When "foobar" is called by a saveReady hook, changes are saved, so instance used internally by PW is same as children() one.
    But when I save with hook instance ($event->arguments(0)), changes are not saved.

    Here is a kind of demonstration, we can see Page instance from HookEvent is not the same as get() or find():

    wire()->addHookAfter('Pages::sorted(6778)', test(...));
    function test($event)
        $eventPage = $event->arguments(0);
        $findPage = wire()->pages->find(6778, ['getFromCache' => true])[0];
        $getPage = wire()->pages->get(6778);
        wire()->log->message('$eventPage === $findPage ' . ($eventPage === $findPage)); // False
        wire()->log->message('$eventPage === $getPage ' . ($eventPage === $getPage)); // False
        wire()->log->message('$findPage === $getPage ' . ($findPage === $getPage)); // True
        foreach ($eventPage->parent->children() as $sibling) {
            if ($sibling->id == $eventPage->id) {
                wire()->log->message('$sibling === $eventPage ' . ($sibling === $eventPage)); // False
                wire()->log->message('$sibling === $findPage ' . ($sibling === $findPage)); // True
                wire()->log->message('$sibling === $getPage ' . ($sibling === $getPage)); // True

    In PW admin, drag/drop the page of id 6778 to change its sort order, and check "messages" logs.
    I force getFromCache just to test but it's already default value.

    Is it the expected behavior? Why? Am I supposed to modify/save the instance from hook event or from find()? Does it even matter?

  10. On 10/31/2023 at 9:23 PM, bernhard said:

    Of course we have now two hooks for the same process, so we can combine those two to one and we end up with two hooks which should all you need:

    Be careful with $page->meta(), I'm using it for another purpose and it throws if page doesn't exist in DB, so at first page save (after giving a title): ProcessPageAdd: WireDataDB sourceID must be greater than 0

    It's necessary to check ID before:

    if ($page->id)


    • Like 1
  11. Hello @fox_digitalanimals

    A solution with a hook and CSS.

    First, in site/templates/admin.php, define a new CSS to include in admin:

    /** @var Config $config */
    $config->styles->add($config->urls->templates . "styles/test.css");
    require($config->paths->core . "admin.php"); // this is already in admin.php, just take care of having it at bottom


    .foobar .InputfieldRepeaterAddItem {
        display: none;

    And add a hook in site/templates/admin.php to hide "add" button by adding a css class to repeater wrapper div:

        function (HookEvent $event) {
            /** @var Page $page */
            $page = $event->object->getPage();
            if ($page->template->name == 'myTemplate') { // Filter on this template
                /** @var InputfieldWrapper $wrapper */
                $wrapper = $event->return;
                /** @var InputfieldRepeater $myRepeater */
                $myRepeater = $wrapper->get('repeat'); // "repeat" is the field name of my repeater

    You can finally add a condition on $user roles to add the css class:

    if ($user->hasRole("theRole")){ // add the css... }


    • Like 1
  12. If there are results to store, that's a whole question. 🙂 I didn't think about that since OP question is a bit vague and we don't know if he needs that. And I was curious about testing repeaters. 😆

    Maybe store the results on the quiz page in a separate field (Profields Table ?), or a custom process module, or your RockGrid module. ^^

    Since I bought Profields Table, I would try with it, I know it works with findRaw(). Adding a Table field on quiz template, columns would be "user | questionId | score".

    I don't see a good way to do this with only core PW, but I don't know everything in PW.

  13. 8 minutes ago, bernhard said:

    My only advice for that project is NOT to use repeaters for questions, answers, whatever. Repeaters are tempting because you get some kind of easy UI for your data, but repeaters really don't scale well and they are a pain when it comes to aggregating data, which is likely an important requirement of such a project. It's much better to store everything in pages and create references via page reference fields

    I never used repeaters so my knowledge is poor on this subject. But in my answer the goal is to create a single quiz per page (1000 quiz == 1000 pages with "quiz" template), do you think it's an issue doing that way? And why?

    I'm interested in gathering informations on this subject, as it may be useful in the future. 🙂

  • Create New...