Triggering a function via cron daemon fails (Elgg 1.8)

In porting a plugin to Elgg 1.8 I'm stuck with a cron handler hook problem:

The execution of a function should be triggered via Elgg's cron mechanism. When I call the corresponding cron trigger in the browser (e.g. site.domain/cron/hourly) it works. But when the cron handler is triggered via the crontab daemon, the function seems to fail being executed - though the cron handler gives the "OK" for this plugin's job after being called.

The cron daemon seems to handle other jobs well, i.e. garbagecollector and logrotate are working.

Could someone enlighten me what could cause the failure in executing the function of my plugin?

For reference here the handler (the execution period is defined previously):

elgg_register_plugin_hook_handler('cron', elgg_get_plugin_setting('period', 'expirationdate'), 'expirationdate_cron');

and the function(s):

/**
 * Hook for cron event.
 *
 * @param $event
 * @param $object_type
 * @param $object
 * @return unknown_type
 */
function expirationdate_cron($event, $object_type, $object) {
    $value = expirationdate_expire_entities(false) ? 'Ok' : 'Fail';
    return 'expirationdate: ' . $value;
}

/**
 * Deletes expired entities.
 * @return boolean
 */
function expirationdate_expire_entities($verbose=true) {
    $now = time();
    $context = elgg_get_context();
    elgg_set_context('expirationdate');
    expirationdate_su();

    $access_status = access_get_show_hidden_status();
    access_show_hidden_entities(true);

    if (!$entities = elgg_get_entities_from_metadata(array('metadata_names' => 'expirationdate', 'limit' => 99999))) {
        // no entities that need to expire.
        return true;
    }

    foreach ($entities as $entity) {
        if ($entity->expirationdate < $now) {
            $guid = $entity->guid;
            if (!elgg_trigger_plugin_hook('expirationdate:expire_entity', $entity->type, array('entity'=>$entity), true)) {
                continue;
            }

            // call the standard delete to allow for triggers, etc.
            if ($entity->expirationdate_disable_only == 1) {
                if ($entity->disable()) {
                    $return = expirationdate_unset($entity->getGUID());
                    $msg = "Disabled $guid<br>\n";
                } else {
                    $msg = "Couldn't disable $guid<br>\n";
                }
            } else {
                if ($entity->delete()) {
                    $msg = "Deleted $guid<br>\n";
                } else {
                    $msg = "Couldn't delete $guid<br>\n";
                }
            }
        } else {
            if (!elgg_trigger_plugin_hook('expirationdate:will_expire_entity', $entity->type, array('expirationdate'=>$entity->expirationdate, 'entity'=>$entity), true)) {
                continue;
            }
        }

        if ($verbose) {
            print $msg;
        }
    }
    access_show_hidden_entities($access_status);
    elgg_set_context($context);
    expirationdate_su(true);
    return true;
}

  • Try to change the cronhook as follows.

    function expirationdate_cron($hook, $entity_type, $returnvalue,$params) {
        $value = expirationdate_expire_entities(false) ? 'Ok' : 'Fail';
        return $returnvalue . 'expirationdate: ' . $value;
    }

  • It's most likely a permissions issue.  When you trigger it from a browser are you logged in? as an admin?

    Cron is run anonymously, and I see that in the function there is context changes in place, so maybe look to see that you have a matching permissions hook that is correct.

  • Thanks. I came across this http://docs.elgg.org/wiki/PermissionsCheck last night and then also thought that maybe it's a permission issue. But there is a permission hook included already actually.

    I've tried the change Team Webgalli suggested. And it seems to have made an effect. The expiredate-metadata got deleted via the cronjob. Unfortunately, the entity itself remained. What I think what might be the issue now is that the embedded plugin hook trigger (elgg_trigger_plugin_hook('expirationdate:expire_entity', $entity->type, array('entity'=>$entity), true)) that should result in the entity getting deleted by another plugin (userpoints) might now either stumble across a permission issue or I might need to make the same change in the plugin hook handler in the userpoints plugin. The userpoints plugin also has a permission hook already - let's see if thiss hook works...

  • I have to correct myself. Unfortunately, the change of the cronhook made no difference. So it seems to be a permission issue (deleting the entity is also done in the function, so I don't think that the other plugin interferes here). The permission hook implemented follows the example in http://docs.elgg.org/wiki/PermissionsCheck. Nonetheless, it fails. At the top of the example it says:

    Warning: As stated in the page, this method works ONLY for granting WRITE access to entities. You _cannot_ use this method to retrieve or view entities for which the user does not have read access.

    This seems to contradict what's done in the example:

    // returns all entities regardless of access permissions.
    // will NOT return hidden entities.
    $entities = get_entities();

    Could this mean that already the initial fetching of data with

    $entities = elgg_get_entities_from_metadata(array('metadata_names' => 'expirationdate', 'limit' => 99999))

    fails in my function? How to grant read permission then?

  • Try to use the following block:

    $access = elgg_set_ignore_access(true);

    ... do your stuff

    elgg_set_ignore_access($access);

     

    This should work when trying to retrieve entities you usually not permitted to see by access restrictions.

  • Yes the documentation for that is a little confusing.

     

    elgg_set_ignore_access() overwrites the "read" permissions so you can find objects that you don't have access to

     

    the permission hook overwrites the "write" permissions so you can add/edit/delete things you normally don't have access to

    use the context to determine whether your permission hook should grant access or not.  If it's granting access return TRUE, otherwise return NULL

  • I need to do some more testing but it seems using elgg_set_ignore_access has done the trick. I've modified the function like this

    function expirationdate_expire_entities($verbose=true) {
        $now = time();
        $context = elgg_get_context();
        elgg_set_context('expirationdate');
        expirationdate_su();

        $access = elgg_set_ignore_access(true);

    <...doing the expire stuff...>

        elgg_set_ignore_access($access);

        elgg_set_context($context);
        expirationdate_su(true);
        return true;
    }

    The permission hooks sets the write permission for the 'expirationdate' and it seems I get the read permission via elgg_set_ignore_access. It worked once and now I need to keep it running for a while to see if it works without errors.

    Depending on Brett's response (hopefully soon) there might be an Elgg 1.8 version of the Expirationdate plugin available soon one way or other.

    Thanks guys!