tags:

views:

558

answers:

4

I have a problem with the following implementation of hook_cron in Drupal 6.1.3.

The script below runs exactly as expected: it sends a welcome letter to new members, and updates a hidden field in their profile to designate that the letter has been sent. There are no errors in the letter, all new members are accounted for, etc.

The problem is that the last line -- updating the profile -- doesn't seem to work when Drupal cron is invoked by the 'real' cron on the server.

When I run cron manually (such as via /admin/reports/status/run-cron) the profile fields get updated as expected.

Any suggestions as to what might be causing this?

(Note, since someone will suggest it: members join by means outside of Drupal, and are uploaded to Drupal nightly, so Drupal's built-in welcome letters won't work (I think).)

<?php
function foo_cron() {
    // Find users who have not received the new member letter, 
    // and send them a welcome email

    // Get users who have not recd a message, as per the profile value setting
    $pending_count_sql = "SELECT COUNT(*) FROM {profile_values} v 
     WHERE (v.value = 0) AND (v.fid = 7)"; //fid 7 is the profile field for profile_intro_email_sent
    if (db_result(db_query($pending_count_sql))) { 
     // Load the message template, since we 
     // know we have users to feed into it.
     $email_template_file =  "/home/foo/public_html/drupal/" . 
            drupal_get_path('module', 'foo') . 
            "/emails/foo-new-member-email-template.txt";
     $email_template_data  = file_get_contents($email_template_file);
     fclose($email_template_fh);
     //We'll just pull the uid, since we have to run user_load anyway
     $query = "SELECT v.uid FROM {profile_values} v 
     WHERE (v.value = 0) AND (v.fid = 7)";  
     $result = db_query(($query));
     // Loop through the uids, loading profiles so as to access string replacement variables
     while ($item = db_fetch_object($result)) {
      $new_member = user_load($item->uid);
      $translation_key = array(
       // ... code that generates the string replacement array ...
       );
      // Compose the email component of the message, and send to user
      $email_text = t($email_template_data, $translation_key);
      $language = user_preferred_language($new_member);  // use member's language preference
      $params['subject'] = 'New Member Benefits - Welcome to FOO!';
      $params['content-type'] = 'text/plain; charset=UTF-8; format=flowed;';
      $params['content'] = $email_text;
      drupal_mail('foo', 'welcome_letter', $new_member->mail, $language, $params, '[email protected]');
      // Mark the user's profile to indicate that the message was sent
      $change = array(
       // Rebuild all of the profile fields in this category, 
       // since they'll be deleted otherwise
       'profile_first_name' => $new_member->profile_first_name,
       'profile_last_name' => $new_member->profile_last_name,
       'profile_intro_email_sent' => 1);
      profile_save_profile($change, $new_member, "Membership Data");
     }
    }
}
+2  A: 

Not quite a random guess ... but close ...

When "real" cron runs code, it runs it as a particular user.

Similarly, when you run the Drupal cron code manually, the code will also be running as a particular user.

My suspicion is that the two users are different, with different permissions, and that's causing the failure.

Does the cron job's user have access to write the database, or read only?

Are there any log files generated by the cron job?

Update: By 'user' above, I'm referring to the user accounts on the host server, not Drupal accounts. For example, on a sandbox system I use for testing Drupal changes, Apache runs under the noone account.

Bevan
Thanks for the answer -- database permissions aren't the issue, but I suspect that they may be part of the solution. I'm posting another question on how to get around the limitation that @Houssem cites.
anschauung
+2  A: 

yes i confirm drupal cron user profile is "anonymous" so you have to add the permission de manager user for the "anonymous" user which is not very good in term of security ..

Houssem
Oh, yuck. I did seem Googling based on your answer, and that seems to explain it. Thanks for the insight.I'll post another question on the best way to work around that problem.
anschauung
A: 

profile_save_profile dosn't check for permissions or as far as I can see do 'global $user' so I don't know if this is the real issue.

 $new_member = user_load($item->uid);

This looks wrong user_load should take an array not a uid (unless you are using Drupal7), but if that didn't work the rest of the code would fail too.

Jeremy French
user_load can take UIDs in lieu of an array, if you already know the user's UID. The array is used for locating users by other means, if you don't have easy access to the UID. Note line 6 on the source for user_load: if (is_numeric($array)) {$array = array('uid' => $array);}
anschauung
That'l teach me to look at the docs rather than the code.
Jeremy French
+2  A: 

To safely switch users http://api.drupal.org/api/function/session_save_session/6

<?php
global $user;
$original_user = $user;
session_save_session(FALSE); // D7: use drupal_save_session(FALSE);
$user = user_load(array('uid' => 1)); // D7: use user_load(1);

// Take your action here where you pretend to be the user with UID = 1 (typically the admin user on a site)
// If your code fails, it's not a problem because the session will not be saved
$user = $original_user;
session_save_session(TRUE); // // D7: use drupal_save_session(TRUE);

// From here on the $user is back to normal so it's OK for the session to be saved
?>

Taken from here: drupal dot org/node/218104

jethro
Perfect ... thanks!
anschauung
Thanks! I has the same question.
farzan