7

I have a site setup which is for subscribers only. I have added a user expiry date field to the users section and everything is working perfectly on the front end in that I can restrict access to accounts that have already passed their expiry date.

However I need to send notification emails to accounts that expire today or will expire in 7 days time.

I am using the mailer plugin in combination with a basic plugin I have made. I have setup a basic plugin with a controller which calls the following function from the plugins service:

public function sendNotificationEmail()
{
    //Create an EmailModel
    $email = new EmailModel;

    //Set the necessary attributes 
    $email->fromName = 'Joe Blogs';
    $email->fromEmail = 'joe.blogs@example.com';
    $email->subject     = 'Email subject';
    $email->htmlBody    = 'The message';


    //RecipientsModel
    $recipients = new Mailer_RecipientsModel();
    $recipients->recipients = array(
        array(
            'to' => 'jane.doe@example.com'
        )
    );

    if( $recipients->validate()){
        //Create mailer
        craft()->mailer_main->newMailer($recipients, $email); //This will start the MailerTask
    }


}

As you can see, at the moment I'm just sending a single test email each time. Everything works as expected but I've noticed that the task does not get triggered until I refresh the CP. This is a problem in my case, I think, because I want to trigger everything from a CRON job.

If anyone has any solution as to how to automatically trigger the task or if there are any alternative methods to solve this problem, I'd love to here them :)

btw, the reason I went down the task route was that the system could be sending to 100s of email addresses at a time and I didn't want to be (a) treated as spam or (b) get a timeout from the request

10

Everything you need to manage tasks is in the TasksService, so you should be able to just use that in your controller action to kick off any pending tasks, using something along the lines of:

if (!craft()->tasks->isTaskRunning())
{
    $task = craft()->tasks->getNextPendingTask();

    if ($task)
    {
        craft()->tasks->runPendingTasks();
    }
}

If you have a look around craft/app/services/TasksService.php you'll see you can also grab and run tasks by type which might be useful in this situation.

  • Wish I'd just looked the TasksService now... – Josh Angell Jul 24 '14 at 11:19
4

With the recent release of 2.3.2632, I've had success with curling the runPendingTask action as an ajax request.

0 2 * * * curl -H "X-Requested-With: XMLHttpRequest" http://example.com/actions/myPlugin/someController/runSomeTask
9 2 * * * curl -H "X-Requested-With: XMLHttpRequest" http://example.com/actions/tasks/runPendingTasks

So queue up the tasks, then 9 minutes later, run them.

The tasks controller will gracefully run the pending tasks. And since the request is an AJAX one, the controller will close the connection so you won't have any open HTTP requests while all of the tasks finish.

  • I think I'm going to use this tactic. Any ideas on how to make this run-able by the server only... i.e. no direct access via url? – H2ONOCK Nov 19 '15 at 10:29
  • Does this mean that accessing the plugin directly doesn't actually run it, it is just added to the task queue? And will not fully excute until CP is accessed, unless you run the pending tasks manually? I'm trying to run a plugin that updates externally loaded data from a cronjob (I used wget, not curl), and it seems not to work reliably. The problem may be elsewhere, but it works totally fine when running it manually from the CP at least. – KSP Jun 5 '17 at 15:32
3

Ok, you are probably nearly there - all you need to do is call that controller action from the outside world. Here is a small example:

<?php
namespace Craft;

class MyPlugin_SomeController extends BaseController
{

  // this lets anyone run the controller actions we specify
  protected $allowAnonymous = = array('actionRunSomeTask');

  // this is the controller function you want to call
  public function actionRunSomeTask()
  {
    craft()->tasks->createTask('MyPlugin_Task');
    echo 'Task started!';
    craft()->end();
  }
}

Then you can access the url http://example.com/actions/myPlugin/someController/runSomeTask however you like. In cron you could then use something like wget to get the url:

0 2 * * * wget http://example.com/actions/myPlugin/someController/runSomeTask

All this information and more can be found in the docs: http://buildwithcraft.com/docs/plugins/controllers.

Edit:

I didn't really grasp the issue which is that Tasks only get set off when the cp is requested. To that end, one solution is to use something like PhantomJS to log in to the cp purely to start off the tasks. I have done this to actually set going something else more complex but here is a cleaned up example using PhantomJS and CasperJS to just access the cp and log in:

// make casper
var casper = require('casper').create();

// log in
casper.start('http://example.com/admin/login', function(){
  this.fill('#login-form', {
    'username' : 'someuser',
    'password' : 'somepassword'
  }, true);
});

// wait for the dashboard
casper.waitForUrl(http://example.com/admin/dashboard', function(){
  this.echo('Yay, logged in!');

  // maybe check for some evidence of the task running here

  casper.exit();
});

// tell casper to run the above sequence
casper.run();

I'm pretty sure at some point P&T will sort something more native out but for now this would work.

  • Hi, thanks for the reply. I actually had the controller setup pretty much the same as you have shown. The only difference is that the task gets created from within the sendNotificationsEmail() function which is in the plugins service. I stripped things back to be like in your example but it seems that tasks only get processed the next time the CP is visited. Is there something I'm missing? – Dave Coggins Jul 24 '14 at 8:40
  • Ah, yes sorry - that is totally true. I just found where Brandon mentioned it on gplus: "you have to access the CP to trigger them in the first place (and even that is probably not going to be necessary down the road when I have a chance to test out an idea I have)" - see the comments here: plus.google.com/+LukeHolder/posts/Tc8w1Lr7Czw. I'm not sure there is a way at present to ping the tasks save for logging into the cp. I have done this with PhantomJS though... – Josh Angell Jul 24 '14 at 8:45
  • Thanks for the code for casperjs, never come across it before, I can see it being useful in the future. I did get it working as you suggested but then saw the answer from Mike which seem the more crafty way of doing things. Thanks for taking the time to respond to the question. I'm only 3 days into developing with Craft and I'm amazed by what can be achieved and the support of the community. I hope to be able to pitch in soon once I've got the hangs of things :) – Dave Coggins Jul 24 '14 at 13:33
  • Yeah, I completely failed to actually look at the TaskService since I first had a dig months back, which was silly really as P&T clearly have introduced those things they said they would! Best of luck with it all :) – Josh Angell Jul 24 '14 at 13:43
  • Hi Josh? Any chance you d share more of the plugin using this casper js integration ? I am very interested by that but I am too much of a plugin dev dummy to make it. – migswd Aug 15 '15 at 2:43
1

You may also want to register your route in your YourPlugin.php file, using the registerSiteRoutes method, example below:

public function registerSiteRoutes()
{
    return array(
        'myroute' => array('action' => 'myPlugin/myAction')
    );
}
1

I made a free plugin that may help. I had trouble running through http when there were many tasks.

The plugin executes tasks purely as cron (i.e. does not internally issue a curl or wget request).

I made the plugin available at: https://straightupcraft.com/craft-plugins/taskrunner

or you can view the code at: https://gitlab.com/24hoursmedia/craft2-plugins/craft2-taskrunner

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.