classified.scheduled.inc in Classified Ads 6.3
Same filename and directory in other branches
Scheduled operations for classified.module
Can be invoked from the web UI or from Drush:
- purge
- expiration
- notifications
@copyright (c) 2010-2011 Ouest Systemes Informatiques (OSInet)
@license General Public License version 2 or later
Original code, not derived from the ed_classified module.
File
classified.scheduled.incView source
<?php
/**
* @file
* Scheduled operations for classified.module
*
* Can be invoked from the web UI or from Drush:
* - purge
* - expiration
* - notifications
*
* @copyright (c) 2010-2011 Ouest Systemes Informatiques (OSInet)
*
* @license General Public License version 2 or later
*
* Original code, not derived from the ed_classified module.
*/
/**
* Helper to return current time or a chosen time.
*
* @param int $time
* A UNIX timestamp.
*
* @return int
* A UNIX timestamp.
*/
function _classified_get_time($time = NULL) {
return isset($time) ? $time : $_SERVER['REQUEST_TIME'];
}
/**
* Unpublish nodes past their expiration date.
*
* The test on n.type is not required, since the inner join will only return
* such nodes anyway, but allows the query to take advantage of the core index
* on node.type
*
* Reset notify time to 0 because this field is only used for interim
* notifications, not expire/purge.
*
* No db_rewrite_sql: this query can be run at any time by anyone.
*
* @TODO check whether the update/join syntax works on non-MySQL DB engines
*
* @param int $time
* A UNIX timestamp. Normally not set: this was added for testing purposes.
*
* @return array
* A per-user array of per-nid expired nodes titles.
*/
function _classified_scheduled_build_expire($time = NULL) {
// Obtain the list of ads to expire.
$expires = _classified_get_time($time);
$sq = <<<EOT
SELECT n.nid, n.title, n.uid
FROM {node} n
INNER JOIN {classified_node} cn ON n.nid = cn.nid
WHERE
n.type = 'classified' AND n.status = 1
AND cn.expires < %d
EOT;
$q = db_query($sq, $expires);
$count1 = 0;
$expired = array();
while ($o = db_fetch_object($q)) {
$expired[$o->uid][$o->nid] = $o->title;
$count1++;
}
// Now perform the expiration.
$sq = <<<EOT
UPDATE {node} n
INNER JOIN {classified_node} cn ON n.nid = cn.nid
SET n.status = 0, cn.notify = 0
WHERE
n.type = 'classified' AND n.status = 1
AND cn.expires < %d
EOT;
$sts = db_query($sq, $expires);
$count2 = db_affected_rows();
// should match $count1
if ($count1 || $count2) {
watchdog('classified', 'Expiration unpublished @count2 ads (@count1 scheduled): @expired', array(
'@count2' => $count2,
'@count1' => $count1,
'@expired' => var_export($expired, TRUE),
), $count1 == $count2 ? WATCHDOG_INFO : WATCHDOG_WARNING);
}
else {
watchdog('classified', 'Expiration check did not find any ad to expire.', NULL, WATCHDOG_INFO);
}
drupal_alter('classified_expire', $expired);
return $expired;
}
/**
* Build one of the various notification lists.
*
* All this work can be skipped if no module implements
* hook_classified_notify(): in such a case, notifications do not happen, and
* there is no reason to update the notify date since notifications are not
* being sent.
*
* @param string $kind
* The kind of notification to send, from the module-defined list of kinds.
* @param int $time
* A UNIX timestamp. Normally not set: this was added for testing purposes.
*
* @return array
* A per-user array of per-nid node titles to be notified.
*/
function _classified_scheduled_build_notify($kind, $time = NULL) {
$notified = array();
$modules = module_implements('classified_notify_alter');
if (empty($modules)) {
return $notified;
}
$now = _classified_get_time($time);
switch ($kind) {
case 'half-life':
// cn.notify < half-life, now > half-life.
$sq = <<<EOT
SELECT n.nid, n.uid, n.title, cn.notify
FROM {node} n
INNER JOIN {classified_node} cn ON n.nid = cn.nid
WHERE
n.type = 'classified' AND n.status != 0
AND cn.notify < (n.changed + cn.expires) / 2
AND (n.changed + cn.expires) / 2 < %d
EOT;
// Clean up whitespace for regexps.
$sq = trim(str_replace("\n", ' ', $sq));
$sq = db_rewrite_sql($sq);
$q = db_query($sq, $now);
break;
case 'pre-expire':
// cn.notify < expiration - 1 day, now > expiration - 1 day.
$sq = <<<EOT
SELECT n.nid, n.uid, n.title, cn.notify
FROM {node} n
INNER JOIN {classified_node} cn ON n.nid = cn.nid
WHERE
n.type = 'classified' AND n.status != 0
AND cn.notify < cn.expires - 86400
AND cn.expires < %d
EOT;
// Clean up whitespace for regexps.
$sq = trim(str_replace("\n", ' ', $sq));
$sq = db_rewrite_sql($sq);
$q = db_query($sq, $now + 86400);
break;
case 'pre-purge':
// cn.notify < purge - 1 day, now > purge - 1 day
$sq = <<<EOT
SELECT n.nid, n.uid, n.title, cn.notify
FROM {node} n
INNER JOIN {classified_node} cn ON n.nid = cn.nid
WHERE
n.type = 'classified' AND n.status = 0
AND cn.notify < cn.expires + %d - 86400
AND cn.expires + %d < %d
EOT;
// Clean up whitespace for regexps.
$sq = trim(str_replace("\n", ' ', $sq));
$sq = db_rewrite_sql($sq);
// 'grace' is in days.
$grace = (_classified_get('grace') - 1) * 24 * 60 * 60;
$q = db_query($sq, $grace, $grace, $now);
break;
default:
watchdog('classified', 'Invalid notify type requested: @kind', array(
'@kind' => $kind,
), WATCHDOG_WARNING);
break;
}
$notified = array();
while ($result = db_fetch_object($q)) {
$notified[$result->uid][$result->nid] = $result->title;
}
// Alter before updating: allow modules to modify the notification list.
drupal_alter('classified_notify', $notified, $kind);
// Avoid building an empty update query.
if (empty($notified)) {
return $notified;
}
$updated = array();
foreach ($notified as $uid => $user_notified) {
foreach (array_keys($user_notified) as $nid) {
$updated[] = $nid;
}
}
$placeholders = db_placeholders($updated);
$sq = <<<EOT
UPDATE {classified_node}
SET notify = %d
WHERE nid IN ({<span class="php-variable">$placeholders</span>})
EOT;
// Single array format query parameter needed with a placeholders array.
array_unshift($updated, $now);
$q = db_query($sq, $updated);
$touched = db_affected_rows();
watchdog('classified', 'Updated notification timestamp on @count ads.', array(
'@count' => $touched,
WATCHDOG_INFO,
));
return $notified;
}
/**
* Purge nodes past their expiration date + grace period
*
* Selected nodes: expires + grace < now => expires < now - grace
*
* The test on n.type is not required, since the inner join will only return
* such nodes anyway, but allows the query to take advantage of the core index
* on node.type
*
* No db_rewrite_sql: this query can be run at any time by anyone.
*
* @param int $time
* A UNIX timestamp. Normally not set: this was added for testing purposes.
*
* @return array
* A per-user array of per-nid node titles to be deleted.
*/
function _classified_scheduled_build_purge($time = NULL) {
$grace = _classified_get('grace');
if ($grace == -1) {
return array();
}
$limit = _classified_get_time($time) - $grace * 24 * 60 * 60;
$sq = <<<EOT
SELECT n.nid, n.title, n.uid
FROM {node} n
INNER JOIN {classified_node} cn ON n.nid = cn.nid
WHERE
n.type = 'classified' AND cn.expires < %d
EOT;
$q = db_query($sq, $limit);
$ads = array();
$count = 0;
// Hide message information, since the page can be triggered by any user,
// but needs to run as admin, and protect misc session content as well.
$messages = isset($_SESSION['messages']) ? $_SESSION['messages'] : array();
global $user;
$saved_account = $user;
session_save_session(FALSE);
$user = user_load(1);
while ($o = db_fetch_object($q)) {
$ads[$o->uid][$o->nid] = $o->title;
node_delete($o->nid);
// invokes drupal_set_message(), hence the hiding code
$count++;
}
$user = $saved_account;
session_save_session(TRUE);
$_SESSION['messages'] = $messages;
if ($count) {
watchdog('classified', "Deleted @count nodes: @deleted", array(
'@count' => $count,
'@deleted' => var_export($ads, TRUE),
), WATCHDOG_INFO);
}
else {
watchdog('classified', 'Purge did not find any ad to delete.', NULL, WATCHDOG_INFO);
$ads = array();
}
drupal_alter('classified_purge', $ads);
return $ads;
}
/**
* Page callback for expirations
*
* @return string
* Not much to say.
*/
function _classified_scheduled_page_expire() {
// Do not display results.
_classified_scheduled_build_expire();
drupal_set_breadcrumb(_classified_get_breadcrumb_by_term(NULL));
return t('<p>Thanks for triggering our expiration process.</p>');
}
/**
* Page callback for notifications
*
* @param string $kind
* A notification kind from _classified_get_notify_kinds().
*
* @return string
* Not much to say.
*/
function _classified_scheduled_page_notify($kind) {
_classified_scheduled_build_notify($kind);
drupal_set_breadcrumb(_classified_get_breadcrumb_by_term(NULL));
return t('<p>Thanks for triggering our @kind notification process.</p>', array(
'@kind' => $kind,
));
}
/**
* Page callback for purges.
*
* @return string
* Not much to say.
*/
function _classified_scheduled_page_purge() {
// Do not display results.
_classified_scheduled_build_purge();
drupal_set_breadcrumb(_classified_get_breadcrumb_by_term(NULL));
return t('<p>Thanks for triggering our purge process.</p>');
}
Functions
Name![]() |
Description |
---|---|
_classified_get_time | Helper to return current time or a chosen time. |
_classified_scheduled_build_expire | Unpublish nodes past their expiration date. |
_classified_scheduled_build_notify | Build one of the various notification lists. |
_classified_scheduled_build_purge | Purge nodes past their expiration date + grace period |
_classified_scheduled_page_expire | Page callback for expirations |
_classified_scheduled_page_notify | Page callback for notifications |
_classified_scheduled_page_purge | Page callback for purges. |