adrian

Admin Actions

Recommended Posts

Hi @adrian,

Have you given any thought to the idea of making some of your modules PW3-only?

For most modules I think it's nice to keep PW2 support (I have been doing that for my modules so far) but in the case of Admin Actions where the idea is that users can code their own actions it would be good to be able to declare the ProcessWire namespace in the action files, for the sake of code completion and avoiding false code warnings in an IDE.

I could add the namespace to ProcessAdminActions.module manually (to avoid fatal PHP errors), but then it will get lost when the module is upgraded.

This isn't a big issue and I can just comment out the namespace declaration in my action files after I've finished working on them. Just thought I'd ask for your view on things.

  • Like 1

Share this post


Link to post
Share on other sites
5 hours ago, Robin S said:

Have you given any thought to the idea of making some of your modules PW3-only?

Not yet - I haven't come across any issues that the file compiler (or some small minor conditional) doesn't take care of.

Not sure I am ready to ditch 2.x support just yet, but I'll definitely keep in mind your argument re this module.

Maybe I should just ask here - anyone using this module on 2.x? Would anyone mind if it was made 3.x only?

 

  • Like 1

Share this post


Link to post
Share on other sites

Just installed your module, because I wanted to copy the contents of a repeater matrix field from one page to another.

It does not seem to work for repeater matrix fields though.

Could you add that as an option?

Thanks!

Share this post


Link to post
Share on other sites

Hi @OllieMackJames - I'm afraid I don't have the time to do this at the moment, but I am sure you can extend the "Copy Repeater Items to Other Page" action to support RepeaterMatrix fields - probably just a matter of another loop. If you make those changes, I'd really appreciate a PR :)

Let me know if you have any questions.

  • Like 1

Share this post


Link to post
Share on other sites

@adrian, I created a pull request for adding support for Repeater Matrix to the "Copy Repeater Items to Other Page" action.

It only required a small modification to the field select options so I thought it best to keep this within the existing action rather than create a new action just for copying Repeater Matrix fields. I thought about allowing all fieldtypes that extend FieldtypeRepeater but decided it would be better to add fieldtypes on a case-by-case basis just in case someone has a custom fieldtype that extends FieldtypeRepeater in some weird and wonderful way that wouldn't be compatible with the action.

I also added a couple of other enhancements to the action - failure conditions for if the selected repeater field doesn't exist on the source or destination pages (previously the action reported a success in those cases). And copying repeater items now also copies any files or images included in the repeater items.

One last thing I added, to the module itself this time, is a link to the module config settings in the notice that advises that new actions are available.

I think it would also be handy to have a link the config settings in the process page - I often want to jump there to adjust allowed roles and the "in menu" option. I didn't include this in the pull request though - just something for you to consider.

  • Like 4

Share this post


Link to post
Share on other sites

Thanks @Robin S - looks like it is was much easier than I expected.

I also added your request for a link to the module settings from the Process page.

Really appreciate all the other tweaks and fixes - mucho appreciado!

Thanks again!

  • Like 3

Share this post


Link to post
Share on other sites

@Robin S wow! @adrian thanks for your answer.

I feel privileged to stand on the shoulders of all you coding giants.

I am just a user who runs into stuff and here I am within a day this wonderful community makes things happen.

What I am doing with my sites would not be possible without you coding giants.

So processwire coding giants, hats off to all of you and a big heartfelt thanks.

To all processwire coding giants: may your days be blessed with incredible coding insights that make things happen for all  of your personal and business endeavors and may this result in joy and whatever results you wish for in your lives!

THANKS!!!

  • Like 2

Share this post


Link to post
Share on other sites

Hello

Suggestion for a new one if anyone thinks it would be useful : change values of a checkbox.

For now, unless I'm wrong, only text fields could be setORreplace.

Thanks!!

Share this post


Link to post
Share on other sites
9 hours ago, mel47 said:

Hello

Suggestion for a new one if anyone thinks it would be useful : change values of a checkbox.

For now, unless I'm wrong, only text fields could be setORreplace.

Thanks!!

Hi @mel47 - contributions are very welcome!

Share this post


Link to post
Share on other sites

I did a search to see if this had been requested before. If it has sorry, for double posting, but I was wondering whether it would be possible to save actions. For instance with the EmailBatcher, I might want certain fields to always be the same and not have to fill them in each time I run the action.

Share this post


Link to post
Share on other sites
Posted (edited)

I want to update the Email Batcher action to use pageListSelect instead of selector. Everything seems to work fine in that when I choose my page, the email field then shows the email field attached to the template I've chosen but on submission I get "The Email Batcher action was completed successfully" and no emails sent, whereas if I use 'type' => 'selector' I get "X emails were sent successfully" and the emails are received.

What else do I need to do to be able to use pageListSelect?

Edited by kongondo
Moved post here to the module's support forum. Also, please, no double posting.

Share this post


Link to post
Share on other sites
2 hours ago, Tyssen said:

I did a search to see if this had been requested before. If it has sorry, for double posting, but I was wondering whether it would be possible to save actions. For instance with the EmailBatcher, I might want certain fields to always be the same and not have to fill them in each time I run the action.

Not quite sure how a save feature would work at the moment without a significant change to the interface. One possibility might be to store the last used options in a cookie and load those again if available. Would that suit your needs, or do you want something more permanent?

If we can come up with an approach that works, I'd be happy to implement at some point though - any thoughts?

The quickest option for you might be to simply clone the action into /site/templates/AdminActions and edit the defineOptions() method to hardcode the values you need. Or you could remove that method entirely and hardcode them into the executeAction() method so you wouldn't even get the option to change anything - this would be a good option if you have site editors using this - prevents them from breaking anything. Let me know if this works for your needs and if you need any help implementing.

  • Like 1

Share this post


Link to post
Share on other sites

Actually creating separate action files which have hard-coded values will probably be the easiest and quickest; didn't think of that!

  • Like 2

Share this post


Link to post
Share on other sites
19 hours ago, Tyssen said:

Edited 11 hours ago by kongondo
Moved post here to the module's support forum. Also, please, no double posting.

Sorry, whenever I've posted similar questions on other forums, I've always been told to not hijack threads and to start new ones.

Share this post


Link to post
Share on other sites
Posted (edited)
7 minutes ago, Tyssen said:

Sorry, whenever I've posted similar questions on other forums, I've always been told to not hijack threads and to start new ones.

No worries. I get your point . In this case, though, the module in question has its own support forum, hence, all questions regarding it should be directed to its support forum.

Edit: I think our forum guidelines could be clearer in this regard, though. I'll have a word with the other mods.

Edited by kongondo
  • Like 1

Share this post


Link to post
Share on other sites
19 hours ago, Tyssen said:

What else do I need to do to be able to use pageListSelect?

Thanks @kongondo for moving here - I wouldn't have seen that post otherwise!

@Tyssen - I just changed the email batcher to use pageListSelect and the email came through just fine. Not sure what might have gone wrong, but can you try again please?

If it still doesn't work, then you should try to debug the executeAction() method in that action to see if the recipients are being populated correctly. 

I would ask though how you are using this because a single page with one email field will only have one recipient - I don't really understand your use case - can you explain in a little more detail how a single selected page can work for your needs?

Share this post


Link to post
Share on other sites
10 minutes ago, adrian said:

I would ask though how you are using this because a single page with one email field will only have one recipient - I don't really understand your use case - can you explain in a little more detail how a single selected page can work for your needs?

That's where I'm going wrong then. I'm choosing the parent page and assuming that it was going to work in the same way as it does when using selector, i.e. selecting all the parent's children. Not sure why it shows the email field though as there is no email field attached to the parent's template.

The reason I want to use pageListSelect is because even though this module is aimed at site admins, I'm hoping I can use the Batch Emailer at least for users with restricted access so that they can batch send emails to people assigned to pages that they can manage, and a pageListSelect is going to be more user-friendly for the less tech-savvy than them trying to work out what the page ID is etc.

Share this post


Link to post
Share on other sites

@Tyssen - using a selector doesn't select the parent's children, it selects whatever matches. If you choose a "parent" selector then that's what you'll get, but it's not automatically behavior to get the children of a single selected page. 

The email field is shown because that dropdown just shows all defined email fields on the site - it's not limiting to the ones on a specific template. Remember that with the page selector approach we could be sending to pages with different templates.

Yes, it is meant as more of a site administrator tool, but you could easily make it simpler by cloning this and placing in /site/templates/AdminActions - just adjust this line:

            $recipients = $this->wire('pages')->find($options['pages']);

to:

            $recipients = $this->wire('pages')->find($options['pages'])->children();

and you'll get the children of the selected page.

You might also want to remove the email field from the options phase, and maybe also the Roles option. 

Basically pare it down to the bare minimum of options that your users will need - does that make sense?

Remember that AdminActions is more about the interface it provides for building and presenting an admin interface than the actual core actions that come with it. It's a tool to make your development of custom admin actions really easy - go forward and conquer :)

  • Like 2

Share this post


Link to post
Share on other sites

Yes, thanks very much. 👍

Share this post


Link to post
Share on other sites

I've decided to just use a select fieldtype for choosing the pages and pass in some options so that it starts from a certain parent (using pageListSelect means showing the whole tree which is overkill). But I'm having trouble with the sending of emails. This is what I have so far:

<?php

class EmailBatcherCoordinator extends ProcessAdminActions {

    protected function defineOptions() {

        $pagesOptions = array();
        foreach($this->wire('pages')->get(3767)->children() as $age) $pagesOptions[$age->id] = $age->name;

        return array(
            array(
                'name' => 'from',
                'label' => 'Who is sending',
                'description' => '',
                'notes' => '',
                'type' => 'email',
                'type' => 'select',
                'options' => array(
                    'user1@email.com'=>'User 1',
                    'user2@email.com'=>'User 2',
                ),
            ),
            array(
                'name' => 'pages',
                'label' => 'Pages',
                'description' => 'Select the parent of pages that contain the recipients\' email addresses.',
                'notes' => '',
                'type' => 'select',
                'options' => $pagesOptions,
                'columnWidth' => 50,
            ),
            // All the other options are the same
        );
    }

    protected function executeAction($options) {

        if($options['testAddress']) $testAddress = $options['testAddress'];

        $recipients = $this->wire('pages')->find($options['pages'])->children();
        $emailField = $options['email'];
        print_r($recipients);

        $i = 1;
        foreach($recipients as $recipient) {
            if(isset($testAddress)) {
                // if a test email, then only send first match from selected Pages or Users Roles
                if(isset($testAddress) && $i > 1) break;
                $toEmail = $testAddress;
            }
            else {
                $toEmail = isset($emailField) ? $recipient->$emailField : $recipient;
            }
            print_r($toEmail);

            //replace curly braces codes with matching PW field names
            $htmlBody = $options['body'];
            $htmlBody = $this->parseBody($htmlBody, $options['fromEmail'], $recipient);

            $sent = $this->sendNewUserEmail($toEmail, $options['from'], $options['from'], $options['subject'], $htmlBody);
            if($sent) {
                $this->successMessage = $i . ' email successfully sent.';
            }
            else {
                $this->failureMessage = 'Sorry, no emails could be sent.';
            }
            $i++;
        }
        return true;
    }

	// Everything else is the same
}

So essentially all I've done so far is change how the $recipients are selected and change the from values in sendNewUserEmail to come from a dropdown rather than text inputs. Everything else is the same.

When I print $recipients, it shows the correct child pages but $toEmail prints as NULL and no email gets sent and the success message is The Email Batcher action was completed successfully instead of X emails sent.

Share this post


Link to post
Share on other sites

Hey @Tyssen - please install Tracy Debugger - there are several notices that will indicate to you what the problems are. The key one is:

PHP Notice: Undefined index: email in .../AdminActions/EmailBatcherCoordinator.action.php:42

but there are several others. Once you go through those I am sure you'll be able to get it working :)

Share this post


Link to post
Share on other sites

I've got Tracy installed. Which tab are you seeing that error on? I'm seeing several relating to FileCompiler but they're not restricted to Admin Actions and one from Mailgun because the to parameter is incorrect.

Share this post


Link to post
Share on other sites
6 hours ago, Tyssen said:

I've got Tracy installed. Which tab are you seeing that error on? I'm seeing several relating to FileCompiler but they're not restricted to Admin Actions and one from Mailgun because the to parameter is incorrect.

image.png.523e239774165dbe35615d506ad02040.png

Share this post


Link to post
Share on other sites

If you pasted the code I posted above into an action to test, it's probably because I removed all the bits I didn't change to make it shorter and easier to read. I added comments where I'd taken stuff out. So testAddress, email, body, etc. all exist in my version of the action.

I had missed one $options['fromEmail'] which I've now changed to $options['from'] but other than I'm not getting any other errors.

I've attached the action file if it helps.

EmailBatcherCoordinator.action.php

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By dreerr
      TemplateEnginePug (formally TemplateEngineJade)
       
      This module adds Pug templates to the TemplateEngineFactory. It uses https://github.com/pug-php/pug to render templates.
      doctype html html(lang='en') head meta(http-equiv='content-type', content='text/html; charset=utf-8') title= $page->title link(rel='stylesheet', type='text/css', href=$config->urls->templates . 'styles/main.css') body include header.pug h1= $page->title if $page->editable() p: a(href=$page->editURL) Edit Project on GitHub: github.com/dreerr/TemplateEnginePug
      Project in modules directory: modules.processwire.com/modules/template-engine-pug/
       
      For common problems/features/questions about the Factory, use the TemplateEngineFactory thread.
       
    • By tpr
      ProcessNetteTester
      Run Nette Tester tests within ProcessWire admin.
      (continued from here)

      Features
      AJAX interface for running Nette Tester tests, in bulk or manually display counter, error message and execution time in a table run all tests at once or launch single tests show formatted test error messages and report PHP syntax errors stop on first failed test (optional) hide passed tests (optional) display failed/total instead passed/total (optional) re-run failed tests only (optional) auto scroll (optional) include or exclude tests based on query parameters start/stop all tests with the spacebar reset one test or all tests (ctrl+click) https://modules.processwire.com/modules/process-nette-tester/
      https://github.com/rolandtoth/ProcessNetteTester
    • By bernhard
      Some of you might have followed the development of this module here: https://processwire.com/talk/topic/15524-previewdiscussion-rockdatatables/ . It is the successor of "RockDataTables" and requires RockFinder to get the data for the grid easily and efficiently. It uses the open source part of agGrid for grid rendering.
       
      WHY?
      ProcessWire is awesome for creating all kinds of custom backend applications, but where it is not so awesome in my opinion is when it comes to listing this data. Of course we have the built in page lister and we have ListerPro, but none of that solutions is capable of properly displaying large amounts of data, for example lists of revenues, aggregations, quick and easy sorts by the user, instant filter and those kind of features. RockGrid to the rescue 😉 
       
      Features/Highlights:
      100k+ rows Instant (client side) filter, search, sort (different sort based on data type, eg "lower/greater than" for numbers, "contains" for strings) extendable via plugins (available plugins at the moment: fullscreen, csv export, reload, batch-processing of data, column sum/statistics, row selection) all the agGrid features (cell renderers, cell styling, pagination, column grouping etc) vanilla javascript, backend and frontend support (though not all plugins are working on the frontend yet and I don't plan to support it as long as I don't need it myself)  
      Limitations:
      While there is an option to retrieve data via AJAX the actual processing of the grid (displaying, filtering, sorting) is done on the client side, meaning that you can get into troubles when handling really large datasets of several thousands of rows. agGrid should be one of the most performant grid options in the world (see the official example page with a 100k row example) and does a lot to prevent problems (such as virtual row rendering), but you should always have this limitation in mind as this is a major difference to the available lister options that do not have this limitation.
      Currently it only supports AdminThemeUikit and I don't plan to support any other admin theme.
       
      Download: https://gitlab.com/baumrock/RockGrid
      Installation: https://gitlab.com/baumrock/RockGrid/wikis/Installation
      Quikckstart: https://gitlab.com/baumrock/RockGrid/wikis/quickstart
      Further instructions: https://gitlab.com/baumrock/RockGrid/wikis/quickstart#further-instructions
       
      Module status: alpha, License: MIT
      Note that every installation and uninstallation sends an anonymous google analytics event to my google analytics account. If you don't want that feel free to remove the appropriate lines of code before installation/uninstallation.
       
      Contribute:
      You can contribute to the development of this and other modules or just say thank you by
      testing, reporting issues and making PRs at gitlab liking this post buying me a drink: paypal.me/baumrock/5 liking my facebook page: facebook.com/baumrock hiring me for pw work: baumrock.com  
      Support: Please note that this module might not be as easy and plug&play as many other modules. It needs a good understanding of agGrid (and JavaScript in general) and it likely needs some looks into the code to get all the options. Please understand that I can not provide free support for every request here in the forum. I try to answer all questions that might also help others or that might improve the module but for individual requests I offer paid support for 60€ per hour.
       
      Changelog
      180711 bugfix (naming issue) 180630 alpha realease  
      Use Cases / Examples:
      Colored grid cells, Icons, Links etc. The Grid also has a "batcher" feature built in that helps communicating with the server via AJAX and managing resource intensive tasks in batches:

      Filters, PW panel links and instant reload on panel close:

      You can combine the grid with a chart library like I did with the (outdated) RockDataTables module:

    • By tpr
      Update 2018-07-09: ProcessNetteTester module is available in the Modules Directory and on GitHub.

      This is a short tutorial on how to use Nette Tester with ProcessWire.
      As you will see it's very easy to setup and use and it's perfect for testing your code's functionality. With bootstrapping ProcessWire it's also possible to check the rendered markup of pages using the API, checking page properties, etc. It's also a great tool for module developers for writing better code. 
      While there will be nothing extraordinary here that you couldn't find in Tester's docs this can serve as a good starting point.
      Prerequisites: PHP 5.6+
      01 Download Tester
      Go to https://github.com/nette/tester/releases and download the latest release (currently 2.0.2). Download from the link reading "Source code (zip)". You can use composer also if you wish.
      02 Extract Tester files
      Create a new directory in your site root called "tester". Extract the zip downloaded here, so it should look like this:
      /site /tester/src /tester/tools /tester/appveyor.yml /tester/composer.json /tester/contributing.md /tester/license.md /tester/readme.md /wire ... 03 Create directory for test files
      Add a new directory in "/tester" called "tests". Tester recognizes "*.Test.php" and "*.phpt" files in the tests directory, recursively. 
      04 Create your first test
      In the "tests" directory create a new "MyTest.php" file.
      The first test is a very simple one that bootstraps ProcessWire and checks if the Home page name is "Home". This is not the smartest test but will show you the basics.
      Add this to "/tester/tests/MyTest.php":
      <?php namespace ProcessWire; use \Tester\Assert; use \Tester\DomQuery; use \Tester\TestCase; use \Tester\Environment; require __DIR__ . '/../src/bootstrap.php'; // load Tester require __DIR__ . '/../../index.php'; // bootstrap ProcessWire Environment::setup(); class MyTest extends TestCase {     // first test (step 04)     public function testHomeTitle()     {         $expected = 'Home'; // we expect the page title to be "Home"         $actual = wire('pages')->get(1)->title; // check what's the actual title Assert::equal($expected, $actual); // check whether they are equal     }     // second test will go here (step 06)     // third test will go here (step 07) } // run testing methods (new MyTest())->run(); I've added comment placeholders for the second and third tests that we will insert later.
      05 Run Tester
      Tester can be run either from the command line or from the browser. The command line output is more verbose and colored while in the browser it's plain text only (see later).
      Running from the command line
      Navigate to the "/tester" directory in your console and execute this:
      php src/tester.php -C tests This will start "/tester/src/tester.php" and runs test files from the "/tester/tests" directory. The "-C" switch tells Tester to use the system-wide php ini file, that is required here because when bootstrapping ProcessWire you may run into errors (no php.ini file is used by default). You may load another ini file with the "-c <path>" (check the docs).
      If the title of your Home page is "Home" you should see this:

      If it's for example "Cats and Dogs", you should see this:

      Running from the browser
      First we need to create a new PHP file in ProcessWire's root, let's call it "testrunner.php". This is because ProcessWire doesn't allow to run PHP files from its "site" directory.
      The following code runs two test classes and produces a legible output. IRL you should probably iterate through directories to get test files (eg. with glob()), and of course it's better not allow tests go out to production.
      <?php ini_set('html_errors', false); header('Content-type: text/plain'); echo 'Starting tests.' . PHP_EOL; echo '--------------------------' . PHP_EOL; $file = __DIR__ . '/PATH_TO/FirstTest.php'; echo basename($file) . ' '; require $file; echo '[OK]' . PHP_EOL; $file = __DIR__ . '/PATH_TO/SecondTest.php'; echo basename($file) . ' '; require $file; echo '[OK]' . PHP_EOL; echo '--------------------------' . PHP_EOL; echo 'Tests finished.'; exit; Navigate to "DOMAIN/testrunner.php" in your browser to execute the file. If every test succeeds you should get this:

      If there are failed tests the execution stops and you can read the error message. If there were more tests (eg. ThirdTest), those won't be displayed under the failed test.

      06 DOM test
      This test will check if a page with "basic-page" template has a "h1" element. We will create the page on the fly with ProcessWire's API. To keep things simple we will add the new test as a new method to our MyTest class.
      Add this block to the MyTest class:
      public function testBasicPageHeadline() {     $p = new Page();     $p->template = 'basic-page';     $html = $p->render();     $dom = DomQuery::fromHtml($html);     Assert::true($dom->has('h1')); } This will most likely be true but of course you can check for something more specific, for example "div#main". Note that we have used the DomQuery helper here (check the "use" statement on the top of the file).
      07 Custom function test
      You will probably want to make sure your custom functions/methods will work as they should so let's write a test that demonstrates this.
      I don't want to complicate things so I'll check if the built-in "pageName" sanitizer works as expected. Add this to the myTest class:
      public function testPageNameSanitizer() {     $expected = 'hello-world';     $actual = wire('sanitizer')->pageName('Hello world!', true);     Assert::equal($expected, $actual); } This should also be true. Try to change the expected value if you are eager to see a failure message.
       
      08 Next steps
      You can add more methods to the MyTest class or create new files in the "tests" directory. Check out the range of available Assertions and other features in the docs and see how they could help you writing more fail-safe code.
      Once you make a habit of writing tests you'll see how it can assist making your code more bulletproof and stable. Remember: test early, test often 🙂
      If you find out something useful or cool with Tester make sure to share.
    • By bernhard
      @Sergio asked about the pdf creation process in the showcase thread about my 360° feedback/survey tool and so I went ahead and set my little pdf helper module to public.
      Description from PW Weekly:
       
      Download & Docs: https://gitlab.com/baumrock/RockPdf
       
      You can combine it easily with RockReplacer: 
      See also a little showcase of the RockPdf module in this thread: