? scheduler-promotion.patch
Index: scheduler.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/scheduler/Attic/scheduler.install,v
retrieving revision 1.4.4.1
diff -u -p -r1.4.4.1 scheduler.install
--- scheduler.install	14 May 2008 17:25:53 -0000	1.4.4.1
+++ scheduler.install	27 Jun 2008 00:18:08 -0000
@@ -9,6 +9,8 @@ function scheduler_install() {
                   nid int(10) unsigned NOT NULL,
                   publish_on int(11) NOT NULL default '0',
                   unpublish_on int(11) NOT NULL default '0',
+                  promote_on int(11) NOT NULL default '0',
+                  demote_on int(11) NOT NULL default '0',
                   PRIMARY KEY (nid)
                 ) /*!40100 DEFAULT CHARACTER SET utf8 */;"
               );
@@ -18,6 +20,8 @@ function scheduler_install() {
                   nid integer NOT NULL default '0',
                   publish_on integer NOT NULL default '0',
                   unpublish_on integer NOT NULL default '0',
+                  promote_on integer NOT NULL default '0',
+                  demote_on integer NOT NULL default '0',
                   PRIMARY KEY (nid));"
               );
       break;
@@ -58,3 +62,18 @@ function scheduler_update_3() {
   }
   return $ret;
 }
+
+function scheduler_update_4() {
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      $ret[] = update_sql("ALTER TABLE {scheduler} ADD promote_on int(11) NOT NULL default '0'");
+      $ret[] = update_sql("ALTER TABLE {scheduler} ADD demote_on int(11) NOT NULL default '0'");
+      break;
+    case 'pgsql':
+      db_add_column($ret, 'scheduler', 'promote_on', 'integer', array('not null' => TRUE, 'default' => "0"));
+      db_add_column($ret, 'scheduler', 'demote_on', 'integer', array('not null' => TRUE, 'default' => "0"));
+      break;
+  }
+  return $ret;
+}
Index: scheduler.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/scheduler/Attic/scheduler.module,v
retrieving revision 1.46.4.33
diff -u -p -r1.46.4.33 scheduler.module
--- scheduler.module	31 May 2008 08:57:43 -0000	1.46.4.33
+++ scheduler.module	27 Jun 2008 00:18:08 -0000
@@ -16,7 +16,7 @@ function scheduler_help($section) {
  * Implementation of hook_perm().
  */
 function scheduler_perm() {
-  return array('schedule (un)publishing of nodes');
+  return array('schedule (un)publishing of nodes', 'schedule promoting/demoting of nodes');
 }
 
 /**
@@ -43,7 +43,7 @@ function scheduler_menu($may_cache) {
     'title' => t('Scheduler module settings'),
     'callback' => 'drupal_get_form',
     'callback arguments' => 'scheduler_admin',
-    'access' => user_access('administer content'),
+    'access' => user_access('administer content') || user_access('schedule promoting/demoting of nodes'),
     'type' => MENU_NORMAL_ITEM,
    );
 return $items;
@@ -75,6 +75,12 @@ function scheduler_form_alter($form_id, 
       '#default_value' => variable_get('scheduler_'. $form['#node_type']->type, 0),
       '#description' => t('Check this box to enable scheduled (un)publishing for this node type.')
     );
+    $form['workflow']['scheduler_promote'] = array(
+      '#type' => 'checkbox', 
+      '#title' => t('Enable scheduled front page promoting/demoting'),
+      '#default_value' => variable_get('scheduler_promote_'. $form['#node_type']->type, 0),
+      '#description' => t('Check this box to enable scheduled promoting/demoting to the front page for this node type.')
+    );
     $form['workflow']['scheduler_touch'] = array(
       '#type' => 'checkbox',
       '#title' => t('Alter published on time'),
@@ -142,6 +148,65 @@ function scheduler_form_alter($form_id, 
         );
       }
     }
+
+    if (user_access('schedule promoting/demoting of nodes')) {
+      // if scheduling has been enabled for this node type
+      if (variable_get('scheduler_promote_'. $form['type']['#value'], 0) == 1) {
+        // We add the additional validation function this way to preserve any existing validation function
+        $form['#validate']['_scheduler_form_validate' ] = array();
+
+        //use JScalendar picker for dates if the module exists and is enabled
+        $jscalendar = FALSE;
+        if (module_exists('jscalendar')) {
+          $jscalendar = TRUE;
+        }
+
+        $node = $form['#node'];
+
+        //only load the values if we are viewing an existing node
+        if ($node->nid > 0) {
+          $defaults = db_fetch_object(db_query('SELECT promote_on, demote_on FROM {scheduler} WHERE nid = %d', $node->nid));
+        }
+        else {
+          // init standard values
+          $defaults = new StdClass;
+          $defaults->promote_on = $defaults->demote_on = NULL;
+        }
+
+        if (!user_access('schedule (un)publishing of nodes') || variable_get('scheduler_'. $form['type']['#value'], 0) == 0) {
+          $form['scheduler_settings'] = array(
+            '#type' => 'fieldset',
+            '#title' => t('Scheduling options'),
+            '#collapsible' => TRUE,
+            '#collapsed' => ($defaults->promote_on != 0 || $defaults->demote_on != 0) ? FALSE: TRUE,
+            '#weight' => 35
+          );
+        }
+
+        $date_format = variable_get('scheduler_date_format', SCHEDULER_DATE_FORMAT);
+        $form['scheduler_settings']['promote_on'] = array(
+          '#type' => 'textfield', 
+          '#title' => t('Promote on'),
+          '#maxlength' => 25,
+          '#default_value' => $defaults->promote_on ? format_date($defaults->promote_on, 'custom', $date_format) : '',
+          '#description' => t('Format: %time. Leave blank to disable scheduled promoting.', array('%time' => format_date(time(), 'custom', $date_format))),
+          '#attributes' => $jscalendar ? array('class' => 'jscalendar') : array(),
+          '#jscalendar_timeFormat' => _scheduler_get_jscalendar_time_format(),
+          '#jscalendar_ifFormat' => _scheduler_date_format_in_strftime_syntax(),
+        );
+
+        $form['scheduler_settings']['demote_on'] = array(
+          '#type' => 'textfield',
+          '#title' => t('Demote on'),
+          '#maxlength' => 25,
+          '#default_value' => $defaults->demote_on ? format_date($defaults->demote_on, 'custom', $date_format) : '',
+          '#description' => t('Format: %time. Leave blank to disable scheduled demoting.', array('%time' => format_date(time(), 'custom', $date_format))),
+          '#attributes' => $jscalendar ? array('class' => 'jscalendar') : array(),
+          '#jscalendar_timeFormat' => _scheduler_get_jscalendar_time_format(),
+          '#jscalendar_ifFormat' => _scheduler_date_format_in_strftime_syntax(),
+        );
+      }
+    }
   }
 }
 
@@ -263,6 +328,8 @@ function _scheduler_form_validate($form_
 
   $publishtime = NULL;
   $unpublishtime = NULL;
+  $promotetime = NULL;
+  $demotetime = NULL;
 
   if (isset($form['publish_on'])) {
     $publishtime=_scheduler_strtotime($form['publish_on']);
@@ -278,9 +345,27 @@ function _scheduler_form_validate($form_
     }
   }
 
+  if (isset($form['promote_on'])) {
+    $promotetime = _scheduler_strtotime($form['promote_on']);
+    if ($promotetime === FALSE) {
+      form_set_error('promote_on', t('The entered promotion date is invalid.'));
+    }
+  }
+
+  if (isset($form['demote_on'])) {
+    $demotetime = _scheduler_strtotime($form['demote_on']);
+    if ($demotetime === FALSE) {
+      form_set_error('demote_on', t('The entered demotion date is invalid.'));
+    }
+  }
+
   if (is_integer($publishtime) && is_integer($unpublishtime) && $unpublishtime < $publishtime) {
     form_set_error('unpublish_on', t("The expiration date is before publication date."));
   }
+
+  if (is_integer($promotetime) && is_integer($demotetime) && $demotetime < $promotetime) {
+    form_set_error('demote_on', t("The demotion date is before promotion date."));
+  }
 }
 
 /**
@@ -304,8 +389,23 @@ function scheduler_nodeapi(&$node, $op, 
         }
       }
     }
+
+    if (isset($node->nid) && $node->nid && variable_get('scheduler_promote_'. $node->type, 0) == 1) {
+      $result = db_query('SELECT * FROM {scheduler} WHERE nid = %d', $node->nid);
+      if ($result) {
+        $row = db_fetch_array($result);
+        if (isset($row['nid'])) {
+          unset($row['nid']);
+          $node->promote_on = $row['promote_on'];
+          $node->demote_on = $row['demote_on'];
+          $row['promoted'] = $row['promote_on'] ? date(variable_get('date_format_long', 'l, F j, Y - H:i'), $row['promote_on']) : NULL;
+          $row['demoted'] = $row['demote_on'] ? date(variable_get('date_format_long', 'l, F j, Y - H:i'), $row['demote_on']) : NULL;
+          $node->scheduler = $row;
+        }
+      }
+    }
   }
-  elseif (user_access('schedule (un)publishing of nodes')) {
+  elseif (user_access('schedule (un)publishing of nodes') || user_access('schedule promoting/demoting of nodes')) {
     switch ($op) {
       case 'view':
         if ($page && $node->unpublish_on) {
@@ -317,6 +417,8 @@ function scheduler_nodeapi(&$node, $op, 
         //adjust the entered times for timezone consideration
         $node->publish_on = _scheduler_strtotime($node->publish_on);
         $node->unpublish_on = _scheduler_strtotime($node->unpublish_on);
+        $node->promote_on = _scheduler_strtotime($node->promote_on);
+        $node->demote_on = _scheduler_strtotime($node->demote_on);
 
         // right before we save the node, we need to check if a "publish on" value has been set
         // if it has been set, we want to make sure the node is unpublished
@@ -324,11 +426,17 @@ function scheduler_nodeapi(&$node, $op, 
         if ($node->publish_on != '' && is_numeric($node->publish_on) && ($node->publish_on > time())) {
           $node->status = 0;
         }
+        // we also need to check if a "promote on" value has been set
+        // if it has been set, we want to make sure the node is demoted
+        // since it will be promoted at a later date (but only if the value is in the future).
+        if ($node->promote_on != '' && is_numeric($node->promote_on) && ($node->promote_on > time())) {
+          $node->promote = 0;
+        }
         break;
       case 'insert':
-        //only insert into database if we need to (un)publish this node at some date
-        if (isset($node->nid) && $node->nid && $node->publish_on != NULL || $node->unpublish_on != NULL) {
-          db_query('INSERT INTO {scheduler} (nid, publish_on, unpublish_on) VALUES (%d, %d, %d)', $node->nid, $node->publish_on, $node->unpublish_on);
+        //only insert into database if we need to (un)publish or promote/demote this node at some date
+        if (isset($node->nid) && $node->nid && $node->publish_on != NULL || $node->unpublish_on != NULL || $node->promote_on != NULL || $node->demote_on != NULL) {
+          db_query('INSERT INTO {scheduler} (nid, publish_on, unpublish_on, promote_on, demote_on) VALUES (%d, %d, %d, %d, %d)', $node->nid, $node->publish_on, $node->unpublish_on, $node->promote_on, $node->demote_on);
         }
         break;
       case 'update':
@@ -337,18 +445,18 @@ function scheduler_nodeapi(&$node, $op, 
           
           // if this node has already been scheduled, update its record
           if ($exists) {
-            // only update database if we need to (un)publish this node at some date
-            // otherwise the user probably cleared out the (un)publish dates so we should remove the record
-            if ($node->publish_on != NULL || $node->unpublish_on != NULL) {
-              db_query('UPDATE {scheduler} SET publish_on = %d, unpublish_on = %d WHERE nid = %d', $node->publish_on, $node->unpublish_on, $node->nid);
+            // only update database if we need to (un)publish or promote/demote this node at some date
+            if ($node->publish_on != NULL || $node->unpublish_on != NULL || $node->promote_on != NULL || $node->demote_on != NULL) {
+              db_query('UPDATE {scheduler} SET publish_on = %d, unpublish_on = %d, promote_on = %d, demote_on = %d WHERE nid = %d', $node->publish_on, $node->unpublish_on, $node->promote_on, $node->demote_on, $node->nid);
             }
+            // otherwise the user probably cleared out the (un)publish and promote/demote dates so we should remove the record
             else {
               db_query('DELETE FROM {scheduler} WHERE nid = %d', $node->nid);
             }
           }
-          // node doesn't exist, create a record only if the (un)publish fields are blank
-          else if ($node->publish_on != NULL || $node->unpublish_on != NULL) {
-            db_query('INSERT INTO {scheduler} (nid, publish_on, unpublish_on) VALUES (%d, %d, %d)', $node->nid, $node->publish_on, $node->unpublish_on);
+          // node doesn't exist, create a record only if the (un)publish or promote/demote fields are not blank
+          else if ($node->publish_on != NULL || $node->unpublish_on != NULL || $node->promote_on != NULL || $node->demote_on != NULL) {
+            db_query('INSERT INTO {scheduler} (nid, publish_on, unpublish_on, promote_on, demote_on) VALUES (%d, %d, %d, %d, %d)', $node->nid, $node->publish_on, $node->unpublish_on, $node->promote_on, $node->demote_on);
           }
         }
         break;
@@ -379,8 +487,9 @@ function scheduler_cron() {
     $n->status = 1;
     node_save($n);
     
-    //if this node is not to be unpublished, then we can delete the record
-    if ($n->unpublish_on == 0) {
+    // if this node is not to be unpublished, and has no promtion/demotion
+    // information stored, then we can delete the record
+    if ($n->unpublish_on == 0 && $n->promote_on == 0 && $n->demote_on == 0) {
       db_query('DELETE FROM {scheduler} WHERE nid = %d', $n->nid);
     }
     //we need to unpublish this node at some time so clear the publish on since it's been published
@@ -399,13 +508,20 @@ function scheduler_cron() {
   $nodes = db_query('SELECT * FROM {scheduler} s LEFT JOIN {node} n ON s.nid = n.nid WHERE n.status = 1 AND s.unpublish_on > 0 AND s.unpublish_on < %d', time());
   
   while ($node = db_fetch_object($nodes)) {
-    //if this node is to be unpublished, we can update the node and remove the record since it can't be republished
+    // if this node is to be unpublished, and has no promtion/demotion information stored,
+    // we can update the node and remove the record since it can't be republished
     $n = node_load($node->nid);
     $n->changed = $node->unpublish_on;
     $n->status = 0;
     node_save($n);
 
-    db_query('DELETE FROM {scheduler} WHERE nid = %d', $n->nid);
+    if ($n->promote_on == 0 && $n->demote_on == 0) {
+      db_query('DELETE FROM {scheduler} WHERE nid = %d', $n->nid);
+    }
+    // we need to promte/demote this node at some time so clear the unpublish on since it's been unpublished
+    else {
+      db_query('UPDATE {scheduler} SET unpublish_on = 0 WHERE nid = %d', $n->nid);
+    }
     
     //invoke scheduler API
     _scheduler_scheduler_api($n, 'unpublish');
@@ -414,6 +530,60 @@ function scheduler_cron() {
     $clear_cache = TRUE;
   }
   
+  // if the time now is greater than the time to promote a node, promote it
+  $nodes = db_query('SELECT * FROM {scheduler} s LEFT JOIN {node} n ON s.nid = n.nid WHERE n.promote = 0 AND s.promote_on > 0 AND s.promote_on < %d', time());
+
+  while ($node = db_fetch_object($nodes)) {
+    $n = node_load($node->nid);
+    $n->changed = $node->promote_on;
+    if (variable_get('scheduler_touch_'. $n->type, 0) == 1) {
+      $n->created = $node->promote_on;
+    }
+    $n->promote = 1;
+    node_save($n);
+
+    // if this node is not to be demoted, and has no publishing information stored, then we can delete the record
+    if ($n->publish_on == 0 && $n->unpublish_on == 0 && $n->demote_on == 0) {
+      db_query('DELETE FROM {scheduler} WHERE nid = %d', $n->nid);
+    }
+    // we need to demote this node at some time so clear the promote on since it's been promoted
+    else {
+      db_query('UPDATE {scheduler} SET promote_on = 0 WHERE nid = %d', $n->nid);
+    }
+
+    // invoke scheduler API
+    _scheduler_scheduler_api($n, 'promote');
+
+    watchdog('content', t('@type: scheduled promoting of %title.', array('@type' => $n->type, '%title' => $n->title)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $n->nid));
+    $clear_cache = TRUE;
+  }
+
+  // if the time is greater than the time to demote a node, demote it
+  $nodes = db_query('SELECT * FROM {scheduler} s LEFT JOIN {node} n ON s.nid = n.nid WHERE n.promote = 1 AND s.demote_on > 0 AND s.demote_on < %d', time());
+
+  while ($node = db_fetch_object($nodes)) {
+    // if this node is to be demoted, and has no publishing information stored,
+    // we can update the node and remove the record since it can't be repromoted
+    $n = node_load($node->nid);
+    $n->changed = $node->demote_on;
+    $n->promote = 0;
+    node_save($n);
+
+    if ($n->publish_on == 0 && $n->unpublish_on == 0) {
+      db_query('DELETE FROM {scheduler} WHERE nid = %d', $n->nid);
+    }
+    // we need to (un)publish this node at some time so clear the demote on since it's been demoted
+    else {
+      db_query('UPDATE {scheduler} SET demote_on = 0 WHERE nid = %d', $n->nid);
+    }
+
+    // invoke scheduler API
+    _scheduler_scheduler_api($n, 'demote');
+
+    watchdog('content', t('@type: scheduled demoting of %title.', array('@type' => $n->type, '%title' => $n->title)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $n->nid));
+    $clear_cache = TRUE;
+  }
+
   if ($clear_cache) {
     // clear the cache so an anonymous poster can see the node being published or unpublished
     cache_clear_all();
@@ -434,7 +604,7 @@ function _scheduler_run_cron() {
  * @param $node
  *  The node object
  * @param $action
- *  The action being performed, either "publish" or "unpublish"
+ *  The action being performed, either "publish", "unpublish", "promote" or "demote"
  */
 function _scheduler_scheduler_api($node, $action) {
   foreach (module_implements('scheduler_api') as $module) {
@@ -495,6 +665,20 @@ function scheduler_views_tables() {
         'handler' => views_handler_field_dates(),
         'option' => 'string',
       ),
+      'promote_on' => array(
+        'name' => t('Scheduler: promote on'),
+        'help' => t('Date/time on which the article will be automatically promoted'),
+        'sortable' => TRUE,
+        'handler' => views_handler_field_dates(),
+        'option' => 'string',
+      ),
+      'demote_on' => array(
+        'name' => t('Scheduler: demote on'),
+        'help' => t('Date/time on which the article will be automatically demoted'),
+        'sortable' => TRUE,
+        'handler' => views_handler_field_dates(),
+        'option' => 'string',
+      ),
     ),
     'sorts' => array(
       'publish_on' => array(
@@ -505,6 +689,14 @@ function scheduler_views_tables() {
         'name' => t('Scheduler: unpublish on'),
         'help' => t('Sort by the date/time on which the article will be automatically un-published.'),
       ),
+      'promote_on' => array(
+        'name' => t('Scheduler: promote on'),
+        'help' => t('Sort by the date the article will be automatically promoted.'),
+      ),
+      'demote_on' => array(
+        'name' => t('Scheduler: demote on'),
+        'help' => t('Sort by the date/time on which the article will be automatically demoted.'),
+      ),
     ),
     'filters' => array(
       'publish_on' => array(
@@ -525,6 +717,24 @@ function scheduler_views_tables() {
         'handler' => 'views_handler_filter_timestamp',
         'option' => 'string',
       ),
+      'promote_on' => array(
+        'name' => t('Scheduler: promote on'),
+        'help' => t('This filter allows nodes to be filtered by the date they will be automatically promoted.')
+          .' '. views_t_strings('filter date'),
+        'operator' => 'views_handler_operator_gtlt',
+        'value' => views_handler_filter_date_value_form(),
+        'handler' => 'views_handler_filter_timestamp',
+        'option' => 'string',
+      ),
+      'demote_on' => array(
+        'name' => t('Scheduler: demote on'),
+        'help' => t('This filter allows nodes to be filtered by the date they will be automatically demoted.')
+          .' '. views_t_strings('filter date'),
+        'operator' => 'views_handler_operator_gtlt',
+        'value' => views_handler_filter_date_value_form(),
+        'handler' => 'views_handler_filter_timestamp',
+        'option' => 'string',
+      ),
     ),
   );
   return $tables;
