From ea4c17fef5a29fc5dae06e3d4e722b6c901df597 Mon Sep 17 00:00:00 2001
From: zilverdistel <zilverdistel@zilverdistel.be>
Date: Tue, 15 Mar 2011 08:14:57 +0100
Subject: [PATCH] Added support for scheduling promote/unpromote of nodes

---
 scheduler.info       |    2 +-
 scheduler.install    |   47 +++++
 scheduler.module     |  453 +++++++++++++++++++++++++++++++++++++++++++------
 scheduler.tokens.inc |   26 +++
 scheduler.views.inc  |   54 ++++++
 5 files changed, 525 insertions(+), 57 deletions(-)

diff --git a/scheduler.info b/scheduler.info
index f2145d6..b6a14f1 100644
--- a/scheduler.info
+++ b/scheduler.info
@@ -1,5 +1,5 @@
 name = Scheduler
-description = This module allows nodes to be published and unpublished on specified dates and time.
+description = This module allows nodes to be published/unpublished and promoted/unpromoted on specified dates and time.
 core = 7.x
 files[] = scheduler.install
 files[] = scheduler.module
diff --git a/scheduler.install b/scheduler.install
index 4584f80..c7acd04 100644
--- a/scheduler.install
+++ b/scheduler.install
@@ -33,10 +33,26 @@ function scheduler_schema() {
           'not null' => TRUE,
           'default' => 0,
         ),
+        'promote_on' => array(
+          'description' => 'The UNIX UTC timestamp when to promote',
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'unpromote_on' => array(
+          'description' => 'The UNIX UTC timestamp when to unpromote',
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+          'default' => 0,
+        ),
       ),
       'indexes' => array(
         'scheduler_publish_on' => array('publish_on'),
         'scheduler_unpublish_on' => array('unpublish_on'),
+        'scheduler_promote_on' => array('promote_on'),
+        'scheduler_unpromote_on' => array('unpromote_on'),
       ),
       'primary key' => array('nid'),
     ),
@@ -63,6 +79,13 @@ function scheduler_uninstall() {
     $variables[] = "scheduler_unpublish_enable_" . $type_name;
     $variables[] = "scheduler_unpublish_required_" . $type_name;
     $variables[] = "scheduler_unpublish_revision_" . $type_name;
+    $variables[] = "scheduler_promote_enable_" . $type_name;
+    $variables[] = "scheduler_promote_touch_" . $type_name;
+    $variables[] = "scheduler_promote_required_" . $type_name;
+    $variables[] = "scheduler_promote_revision_" . $type_name;
+    $variables[] = "scheduler_unpromote_enable_" . $type_name;
+    $variables[] = "scheduler_unpromote_required_" . $type_name;
+    $variables[] = "scheduler_unpromote_revision_" . $type_name;
   }
 
   foreach ($variables as $variable) {
@@ -111,3 +134,27 @@ function scheduler_update_6101() {
   }
   return $ret;
 }
+
+function scheduler_update_7100() {
+  $ret = array();
+  
+  db_add_field('scheduler', 'promote_on', array(
+    'description' => 'The UNIX UTC timestamp when to promote',
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'default' => 0,
+  ));
+  db_add_field('scheduler', 'unpromote_on', array(
+    'description' => 'The UNIX UTC timestamp when to unpromote',
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'default' => 0,
+  ));
+  
+  db_add_index('scheduler', 'scheduler_promote_on', array('promote_on'));
+  db_add_index('scheduler', 'scheduler_unpromote_on', array('unpromote_on'));
+  
+  return $ret;
+}
diff --git a/scheduler.module b/scheduler.module
index 922fc02..11c9172 100644
--- a/scheduler.module
+++ b/scheduler.module
@@ -14,6 +14,10 @@ function scheduler_permission() {
       'title' => t('Schedule content publication'),
       'description' => t('Allows users to set a start and end time for content publication'),
     ),
+    'schedule promoting/demoting of nodes' => array(
+      'title' => t('Schedule content promotion'),
+      'description' => t('Allows users to set a start and end time for content promotion'),
+    ),
   );
 }
 
@@ -82,7 +86,7 @@ function scheduler_menu() {
  * Return the users access to the scheduler list page. Separate function required because of the two access values to be checked.
  */
 function scheduler_list_access_callback() {
-  return user_access('administer nodes') && user_access('schedule (un)publishing of nodes');
+  return user_access('administer nodes') && (user_access('schedule (un)publishing of nodes') || user_access('schedule promoting/demoting of nodes'));
 }
 
 /**
@@ -249,6 +253,65 @@ function scheduler_form_node_type_form_alter(&$form, $form_state) {
       '#default_value' => variable_get('scheduler_unpublish_revision_' . $form['#node_type']->type, 0),
       '#description' => t('Check this box if you want a new revision created when unpublishing.')
   );
+
+  $form['scheduler']['promote'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Promotion settings',
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#weight' => 3,
+      '#group' => 'scheduler',
+  );
+  $form['scheduler']['promote']['scheduler_promote_enable'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Enable scheduled promotion'),
+      '#default_value' => variable_get('scheduler_promote_enable_' . $form['#node_type']->type, 0),
+      '#description' => t('Check this box to enable scheduled promotion for this node type.')
+  );
+  $form['scheduler']['promote']['scheduler_promote_touch'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Alter published on time'),
+      '#default_value' => variable_get('scheduler_promote_touch_' . $form['#node_type']->type, 0),
+      '#description' => t('Check this box to alter the published on time to match the promotion time ("touch feature").')
+  );
+  $form['scheduler']['promote']['scheduler_promote_required'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Promotion date/time is required.'),
+      '#default_value' => variable_get('scheduler_promote_required_' . $form['#node_type']->type, 0),
+      '#description' => t('Check this box to if scheduled promotion is required (e.g. the user must enter a date/time).')
+  );
+  $form['scheduler']['promote']['scheduler_promote_revision'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Create a new revision on promotion'),
+      '#default_value' => variable_get('scheduler_promote_revision_' . $form['#node_type']->type, 0),
+      '#description' => t('Check this box if you want a new revision created when promoting.')
+  );
+  $form['scheduler']['unpromote'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Demotion settings',
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#weight' => 4,
+      '#group' => 'scheduler',
+  );
+  $form['scheduler']['unpromote']['scheduler_unpromote_enable'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Enable scheduled Demotion'),
+      '#default_value' => variable_get('scheduler_unpromote_enable_' . $form['#node_type']->type, 0),
+      '#description' => t('Check this box to enable scheduled demotion for this node type.')
+  );
+  $form['scheduler']['unpromote']['scheduler_unpromote_required'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Demotion date/time is required.'),
+      '#default_value' => variable_get('scheduler_unpromote_required_' . $form['#node_type']->type, 0),
+      '#description' => t('Check this box to if scheduled demotion is required (e.g. the user must enter a date/time).')
+  );
+  $form['scheduler']['unpromote']['scheduler_unpromote_revision'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Create a new revision on demotion'),
+      '#default_value' => variable_get('scheduler_unpromote_revision_' . $form['#node_type']->type, 0),
+      '#description' => t('Check this box if you want a new revision created when demoting.')
+  );
 }
 
 /**
@@ -256,12 +319,17 @@ function scheduler_form_node_type_form_alter(&$form, $form_state) {
  */
 function scheduler_form_alter(&$form, $form_state) {
   // Is this a node form and scheduling has been enabled for this node type
-  if (!empty($form['#node_edit_form']) && user_access('schedule (un)publishing of nodes')) {
+  if (!empty($form['#node_edit_form'])) {
     $publishing_enabled = variable_get('scheduler_publish_enable_'. $form['type']['#value'], 0) == 1;
     $unpublishing_enabled = variable_get('scheduler_unpublish_enable_'. $form['type']['#value'], 0) == 1;
-
+    $promotion_enabled = variable_get('scheduler_promote_enable_'. $form['type']['#value'], 0) == 1;
+    $demotion_enabled = variable_get('scheduler_unpromote_enable_'. $form['type']['#value'], 0) == 1;
+    
+    $schedule_publishing = user_access('schedule (un)publishing of nodes') && $publishing_enabled || $unpublishing_enabled;
+    $schedule_promotion = user_access('schedule promoting/demoting of nodes') && $promotion_enabled || $demotion_enabled;
+    
     // if scheduling has been enabled for this node type
-    if ($publishing_enabled || $unpublishing_enabled) {
+    if ($schedule_publishing || $schedule_promotion) {
 
       $node = $form['#node'];
 
@@ -273,20 +341,27 @@ function scheduler_form_alter(&$form, $form_state) {
       // if this is a preview then get the values from the form, not the db
       if (isset($form_state['values']['op']) && $form_state['values']['op'] == t('Preview')) {
         $defaults = new StdClass;
-        $defaults->publish_on = $publishing_enabled ? $form_state['values']['publish_on'] : NULL;
-        $defaults->unpublish_on = $unpublishing_enabled ? $form_state['values']['unpublish_on'] : NULL;
+        if ($schedule_publishing) {
+          $defaults->publish_on = $publishing_enabled ? $form_state['values']['publish_on'] : NULL;
+          $defaults->unpublish_on = $unpublishing_enabled ? $form_state['values']['unpublish_on'] : NULL;
+        }
+        if ($schedule_promotion) {
+          // @todo: make sure the installer handles these fields (hook_schema / hook_update)
+          $defaults->promote = $promotion_enabled ? $form_state['values']['promote_on'] : NULL;
+          $defaults->unpromote = $demotion_enabled ? $form_state['values']['unpromote_on'] : NULL;
+        }
       }
       elseif (isset($node->nid) && $node->nid > 0) {
         // Load the values from the db if we are viewing an existing node.
         $query = db_select('scheduler', 's');
-        $query->fields('s', array('publish_on', 'unpublish_on'));
+        $query->fields('s', array('publish_on', 'unpublish_on', 'promote_on', 'unpromote_on'));
         $query->condition('s.nid', $node->nid, '=');
         $defaults = $query->execute()->fetchObject();
       }
       else {
         // init standard values
         $defaults = new StdClass;
-        $defaults->publish_on = $defaults->unpublish_on = NULL;
+        $defaults->publish_on = $defaults->unpublish_on = $defaults->promote_on = $defaults->unpromote_on = NULL;
       }
 
       // if there is a text value then convert it to a unix timestamp
@@ -296,15 +371,27 @@ function scheduler_form_alter(&$form, $form_state) {
       if (isset($defaults->unpublish_on) && $defaults->unpublish_on && !is_numeric($defaults->unpublish_on)) {
         $defaults->unpublish_on = _scheduler_strtotime($defaults->unpublish_on);
       }
+      if (isset($defaults->promote_on) && $defaults->promote_on && !is_numeric($defaults->promote_on)) {
+        $defaults->promote_on = _scheduler_strtotime($defaults->promote_on);
+      }
+      if (isset($defaults->unpromote_on) && $defaults->unpromote_on && !is_numeric($defaults->unpromote_on)) {
+        $defaults->unpromote_on = _scheduler_strtotime($defaults->unpromote_on);
+      }
 
       $publishing_required = variable_get('scheduler_publish_required_'. $form['type']['#value'], 0) == 1;
       $unpublishing_required = variable_get('scheduler_unpublish_required_'. $form['type']['#value'], 0) == 1;
+      $promotion_required = variable_get('scheduler_promotion_required_'. $form['type']['#value'], 0) == 1;
+      $demotion_required = variable_get('scheduler_demotion_required_'. $form['type']['#value'], 0) == 1;
 
       $fieldset_extended = (
         (isset($defaults->publish_on) && $defaults->publish_on != 0)
         || (isset($defaults->unpublish_on) && $defaults->unpublish_on != 0)
         || $publishing_required
         || $unpublishing_required
+        || (isset($defaults->promote_on) && $defaults->promote_on != 0)
+        || (isset($defaults->unpromote_on) && $defaults->unpromote_on != 0)
+        || $promotion_required
+        || $demotion_required
       );
 
       $form['scheduler_settings'] = array(
@@ -330,52 +417,104 @@ function scheduler_form_alter(&$form, $form_state) {
       }
 
       $description_format = t('Format: %time.', array('%time' => format_date(time(), 'custom', $date_format)));
-      if ($publishing_enabled) {
-        $description_blank = '';
-        if (!$publishing_required) {
-          $description_blank .= ' '.t('Leave blank to disable scheduled publishing.');
+      if ($schedule_publishing) {
+        if ($publishing_enabled) {
+          $description_blank = '';
+          if (!$publishing_required) {
+            $description_blank .= ' '.t('Leave blank to disable scheduled publishing.');
+          }
+  
+          $form['scheduler_settings']['publish_on'] = array(
+            '#type' => 'textfield',
+            '#title' => t('Publish on'),
+            '#maxlength' => 25,
+            '#required' => $publishing_required,
+            '#default_value' => isset($defaults->publish_on) && $defaults->publish_on ? format_date($defaults->publish_on, 'custom', $internal_date_format) : '',
+            '#description' => $description_format.$description_blank,
+          );
         }
-
-        $form['scheduler_settings']['publish_on'] = array(
-          '#type' => 'textfield',
-          '#title' => t('Publish on'),
-          '#maxlength' => 25,
-          '#required' => $publishing_required,
-          '#default_value' => isset($defaults->publish_on) && $defaults->publish_on ? format_date($defaults->publish_on, 'custom', $internal_date_format) : '',
-          '#description' => $description_format.$description_blank,
-        );
-      }
-
-      if ($unpublishing_enabled) {
-        $description_blank = '';
-        if (!$unpublishing_required) {
-          $description_blank .= ' '.t('Leave blank to disable scheduled unpublishing.');
+  
+        if ($unpublishing_enabled) {
+          $description_blank = '';
+          if (!$unpublishing_required) {
+            $description_blank .= ' '.t('Leave blank to disable scheduled unpublishing.');
+          }
+          $form['scheduler_settings']['unpublish_on'] = array(
+            '#type' => 'textfield',
+            '#title' => t('Unpublish on'),
+            '#maxlength' => 25,
+            '#required' => $unpublishing_required,
+            '#default_value' => isset($defaults->unpublish_on) && $defaults->unpublish_on ? format_date($defaults->unpublish_on, 'custom', $internal_date_format) : '',
+            '#description' => $description_format.$description_blank,
+          );
+        }
+  
+        if ($use_date_popup) {
+          // Make this a popup calendar widget if Date Popup module is enabled.
+          if ($publishing_enabled) {
+            $form['scheduler_settings']['publish_on']['#type'] = 'date_popup';
+            $form['scheduler_settings']['publish_on']['#date_format'] = $date_format;
+            $form['scheduler_settings']['publish_on']['#date_year_range'] = '0:+10';
+            if (!$publishing_required) $form['scheduler_settings']['publish_on']['#description'] = t('Leave blank to disable scheduled publishing.');
+            unset($form['scheduler_settings']['publish_on']['#maxlength']);
+          }
+          if ($unpublishing_enabled) {
+            $form['scheduler_settings']['unpublish_on']['#type'] = 'date_popup';
+            $form['scheduler_settings']['unpublish_on']['#date_format'] = $date_format;
+            $form['scheduler_settings']['unpublish_on']['#date_year_range'] = '0:+10';
+            if (!$unpublishing_required) $form['scheduler_settings']['unpublish_on']['#description'] = t('Leave blank to disable scheduled unpublishing.');
+            unset($form['scheduler_settings']['unpublish_on']['#maxlength']);
+          }
         }
-        $form['scheduler_settings']['unpublish_on'] = array(
-          '#type' => 'textfield',
-          '#title' => t('Unpublish on'),
-          '#maxlength' => 25,
-          '#required' => $unpublishing_required,
-          '#default_value' => isset($defaults->unpublish_on) && $defaults->unpublish_on ? format_date($defaults->unpublish_on, 'custom', $internal_date_format) : '',
-          '#description' => $description_format.$description_blank,
-        );
       }
-
-      if ($use_date_popup) {
-        // Make this a popup calendar widget if Date Popup module is enabled.
-        if ($publishing_enabled) {
-          $form['scheduler_settings']['publish_on']['#type'] = 'date_popup';
-          $form['scheduler_settings']['publish_on']['#date_format'] = $date_format;
-          $form['scheduler_settings']['publish_on']['#date_year_range'] = '0:+10';
-          if (!$publishing_required) $form['scheduler_settings']['publish_on']['#description'] = t('Leave blank to disable scheduled publishing.');
-          unset($form['scheduler_settings']['publish_on']['#maxlength']);
+      if ($schedule_promotion) {
+        if ($promotion_enabled) {
+          $description_blank = '';
+          if (!$promotion_required) {
+            $description_blank .= ' '.t('Leave blank to disable scheduled promotion.');
+          }
+  
+          $form['scheduler_settings']['promote_on'] = array(
+            '#type' => 'textfield',
+            '#title' => t('Promote on'),
+            '#maxlength' => 25,
+            '#required' => $promotion_required,
+            '#default_value' => isset($defaults->promote_on) && $defaults->promote_on ? format_date($defaults->promote_on, 'custom', $internal_date_format) : '',
+            '#description' => $description_format.$description_blank,
+          );
         }
-        if ($unpublishing_enabled) {
-          $form['scheduler_settings']['unpublish_on']['#type'] = 'date_popup';
-          $form['scheduler_settings']['unpublish_on']['#date_format'] = $date_format;
-          $form['scheduler_settings']['unpublish_on']['#date_year_range'] = '0:+10';
-          if (!$unpublishing_required) $form['scheduler_settings']['unpublish_on']['#description'] = t('Leave blank to disable scheduled unpublishing.');
-          unset($form['scheduler_settings']['unpublish_on']['#maxlength']);
+  
+        if ($demotion_enabled) {
+          $description_blank = '';
+          if (!$demotion_required) {
+            $description_blank .= ' '.t('Leave blank to disable scheduled demotion.');
+          }
+          $form['scheduler_settings']['unpromote_on'] = array(
+            '#type' => 'textfield',
+            '#title' => t('Demote on'),
+            '#maxlength' => 25,
+            '#required' => $demotion_required,
+            '#default_value' => isset($defaults->unpromote_on) && $defaults->unpromote_on ? format_date($defaults->unpromote_on, 'custom', $internal_date_format) : '',
+            '#description' => $description_format.$description_blank,
+          );
+        }
+  
+        if ($use_date_popup) {
+          // Make this a popup calendar widget if Date Popup module is enabled.
+          if ($promotion_enabled) {
+            $form['scheduler_settings']['promote_on']['#type'] = 'date_popup';
+            $form['scheduler_settings']['promote_on']['#date_format'] = $date_format;
+            $form['scheduler_settings']['promote_on']['#date_year_range'] = '0:+10';
+            if (!$promotion_required) $form['scheduler_settings']['promote_on']['#description'] = t('Leave blank to disable scheduled promotion.');
+            unset($form['scheduler_settings']['promote_on']['#maxlength']);
+          }
+          if ($demotion_enabled) {
+            $form['scheduler_settings']['unpromote_on']['#type'] = 'date_popup';
+            $form['scheduler_settings']['unpromote_on']['#date_format'] = $date_format;
+            $form['scheduler_settings']['unpromote_on']['#date_year_range'] = '0:+10';
+            if (!$demotion_required) $form['scheduler_settings']['unpromote_on']['#description'] = t('Leave blank to disable scheduled demotion.');
+            unset($form['scheduler_settings']['unpromote_on']['#maxlength']);
+          }
         }
       }
     }
@@ -405,6 +544,14 @@ function scheduler_list() {
       'field' => 's.unpublish_on',
     ),
     array(
+      'data' => t('Promote on'),
+      'field' => 's.promote_on',
+    ),
+    array(
+      'data' => t('Unpromote on'),
+      'field' => 's.unpromote_on',
+    ),
+    array(
       'data' => t('Operations'),
     ),
   );
@@ -419,7 +566,7 @@ function scheduler_list() {
   $query->limit(50);
   $query->addJoin('LEFT ', 'node', 'n', 's.nid = n.nid');
   $query->addJoin('LEFT ', 'users', 'u', 'u.uid = n.uid');
-  $query->fields('s', array('publish_on', 'unpublish_on'));
+  $query->fields('s', array('publish_on', 'unpublish_on', 'promote_on', 'unpromote_on'));
   $query->fields('n', array('nid', 'uid', 'status', 'title'));
   $query->addField('u', 'name');
   $query = $query
@@ -435,6 +582,8 @@ function scheduler_list() {
         theme('username', array('account' => $node)),
         ($node->publish_on ? format_date($node->publish_on) : '&nbsp;'),
         ($node->unpublish_on ? format_date($node->unpublish_on) : '&nbsp;'),
+        ($node->promote_on ? format_date($node->promote_on) : '&nbsp;'),
+        ($node->unpromote_on ? format_date($node->unpromote_on) : '&nbsp;'),
       l(t('edit'), 'node/' . $node->nid . '/edit', array(), 'destination=admin/content/node/scheduler'),
     );
   }
@@ -558,11 +707,17 @@ function scheduler_node_load($nodes, $types) {
     $nid = $record->nid;
     $nodes[$nid]->publish_on = $record->publish_on;
     $nodes[$nid]->unpublish_on = $record->unpublish_on;
+    $nodes[$nid]->promote_on = $record->promote_on;
+    $nodes[$nid]->unpromote_on = $record->unpromote_on;
     $row = array();
     $row['published'] = $record->publish_on ? date(variable_get('date_format_long', 'l, F j, Y - H:i'), $record->publish_on) : NULL;
     $row['unpublished'] = $record->unpublish_on ? date(variable_get('date_format_long', 'l, F j, Y - H:i'), $record->unpublish_on) : NULL;
     $row['publish_on'] = $record->publish_on;
     $row['unpublish_on'] = $record->unpublish_on;
+    $row['promoted'] = $record->promote_on ? date(variable_get('date_format_long', 'l, F j, Y - H:i'), $record->promote_on) : NULL;
+    $row['unpromoted'] = $record->unpromote_on ? date(variable_get('date_format_long', 'l, F j, Y - H:i'), $record->unpromote_on) : NULL;
+    $row['promote_on'] = $record->promote_on;
+    $row['unpromote_on'] = $record->unpromote_on;
     $nodes[$nid]->scheduler = $row;
   }
 }
@@ -614,10 +769,34 @@ function scheduler_node_validate($node, $form) {
   if (isset($publishtime) && isset($unpublishtime) && $unpublishtime < $publishtime) {
     form_set_error('unpublish_on', t("The 'unpublish on' date must be later than the 'publish on' date."));
   }
+
+  // promotion/demotion
+  if (!empty($node->promote_on) && !is_numeric($node->promote_on)) {
+    $promotetime = _scheduler_strtotime($node->promote_on);
+    if ($promotetime === FALSE) {
+      form_set_error('promote_on', t("The 'promote on' value does not match the expected format of %time", array('%time' => format_date(REQUEST_TIME, 'custom', $date_format))));
+    }
+    elseif ($promotetime && $promotetime < REQUEST_TIME) {
+      form_set_error('promote_on', t("The 'promote on' date must be in the future"));
+    }
+  }
+
+  if (!empty($node->unpromote_on) && !is_numeric($node->unpromote_on)) {
+    $unpromotetime = _scheduler_strtotime($node->unpromote_on);
+    if ($unpromotetime === FALSE) {
+      form_set_error('unpromote_on', t("The 'unpromote on' value does not match the expected format of %time", array('%time' => format_date(REQUEST_TIME, 'custom', $date_format))));
+    }
+    elseif ($unpromotetime && $unpromotetime < REQUEST_TIME) {
+      form_set_error('unpromote_on', t("The 'unpromote on' date must be in the future"));
+    }
+  }
+  if (isset($promotetime) && isset($unpromotetime) && $unpromotetime < $promotetime) {
+    form_set_error('unpromote_on', t("The 'unpromote on' date must be later than the 'promote on' date."));
+  }
 }
 
 function scheduler_node_presave($node) {
-  foreach (array('publish_on', 'unpublish_on') as $key) {
+  foreach (array('publish_on', 'unpublish_on', 'promote_on', 'unpromote_on') as $key) {
     if (empty($node->$key)) {
     // Make sure publish_on and unpublish_on are not empty strings.
       $node->$key = 0;
@@ -633,30 +812,41 @@ function scheduler_node_presave($node) {
     $date_format = variable_get('scheduler_date_format', SCHEDULER_DATE_FORMAT);
     drupal_set_message(t('This post is unpublished and will be published @publish_time.', array('@publish_time' => format_date($node->publish_on, 'custom', $date_format))), 'status', FALSE);
   }
+  // same thing for promotion / demotion
+  if (isset($node->promote_on) && $node->promote_on != '' && is_numeric($node->promote_on) && $node->promote_on > REQUEST_TIME) {
+    $node->promote = 0;
+    $date_format = variable_get('scheduler_date_format', SCHEDULER_DATE_FORMAT);
+    drupal_set_message(t('This post is unpromoted and will be promoted @promote_time.', array('@promote_time' => format_date($node->promote_on, 'custom', $date_format))), 'promote', FALSE);
+  }
 }
 
 function scheduler_node_insert($node) {
   // only insert into database if we need to (un)publish this node at some date
-  if (!empty($node->publish_on) || !empty($node->unpublish_on)) {
+  if (!empty($node->publish_on) || !empty($node->unpublish_on) || !empty($node->promote_on) || !empty($node->unpromote_on)) {
     db_insert('scheduler')
       ->fields(array(
         'nid' => $node->nid,
         'publish_on' => $node->publish_on,
         'unpublish_on' => $node->unpublish_on,
+        'promote_on' => $node->promote_on,
+        'unpromote_on' => $node->unpromote_on,
       ))
       ->execute();
   }
 }
-
+// here
 function scheduler_node_update($node) {
+  dpm($node, '$node on update');
   // 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 (!empty($node->publish_on) || !empty($node->unpublish_on)) {
+  if (!empty($node->publish_on) || !empty($node->unpublish_on) || !empty($node->promote_on) || !empty($node->unpromote_on)) {
     db_merge('scheduler')
       ->key(array('nid' => $node->nid))
       ->fields(array(
           'publish_on' => $node->publish_on,
           'unpublish_on' => $node->unpublish_on,
+          'promote_on' => $node->promote_on,
+          'unpromote_on' => $node->unpromote_on,
         ))
       ->execute();
   }
@@ -679,6 +869,8 @@ function scheduler_cron() {
   $clear_cache = FALSE;
   $clear_cache |= _scheduler_publish();
   $clear_cache |= _scheduler_unpublish();
+  $clear_cache |= _scheduler_promote();
+  $clear_cache |= _scheduler_unpromote();
 
   if ($clear_cache) {
     // Clear the cache so an anonymous poster can see any changes to nodes
@@ -814,6 +1006,131 @@ function _scheduler_unpublish() {
 }
 
 /**
+ * Promote scheduled nodes.
+ * @return
+ *   TRUE if any node has been published, FALSE otherwise.
+ */
+function _scheduler_promote() {
+  $result = FALSE;
+  $date_format = variable_get('scheduler_date_format', SCHEDULER_DATE_FORMAT);
+
+  // If the time now is greater than the time to promote a node, promote it.
+  $query = db_select('scheduler', 's');
+  $query->addField('s', 'nid');
+  $query->addJoin('LEFT ', 'node', 'n', 's.nid = n.nid');
+  $query->condition('s.promote_on', 0, '>');
+  $query->condition('s.promote_on', REQUEST_TIME, '<');
+  //  $query_result = db_query('SELECT s.nid AS nid FROM {scheduler} s LEFT JOIN {node} n ON s.nid = n.nid WHERE n.status = 0 AND s.publish_on > 0 AND s.publish_on < :now ', array(':now' => REQUEST_TIME));
+  $queryResult = $query->execute();
+  $nids = array();
+  while ($node = $queryResult->fetchObject()) {
+    $nids[] = $node->nid;
+  }
+
+  $nids = array_unique(array_merge($nids, _scheduler_scheduler_nid_list('promote')));
+
+  foreach ($nids as $nid) {
+    $n = node_load($nid);
+    $n->changed = $n->promote_on;
+    $old_creation_date = $n->created;
+    if (variable_get('scheduler_promote_touch_'. $n->type, 0) == 1) {
+      $n->created = $n->promote_on;
+    }
+
+    $create_promoting_revision = variable_get('scheduler_promote_revision_'. $n->type, 0) == 1;
+    if ($create_promoting_revision) {
+      $n->revision = TRUE;
+      $n->log = "Node promoted by scheduler module. Original creation date was ".  format_date($old_creation_date, 'custom', $date_format) .".";
+    }
+
+    // Use the actions system to promote the node.
+    watchdog('scheduler', '@type: scheduled promotion of %title.', array('@type' => $n->type, '%title' => $n->title), WATCHDOG_NOTICE, l(t('view'), 'node/'. $n->nid));
+    $actions = array('node_promote_action', 'node_save_action');
+    $context['node'] = $n;
+    actions_do($actions, $n, $context, NULL, NULL);
+
+    // If this node is not to be unpromoted, then we can delete the record.
+    if (isset($n->unpromote_on) && $n->unpromote_on == 0) {
+      db_delete('scheduler')
+        ->condition('nid', $n->nid)
+        ->execute();
+    }
+    // We need to unpromote this node at some time so clear the promote on since
+    // it has been promoted.
+    else {
+      db_update('scheduler')
+        ->fields(array('promote_on' => 0))
+        ->condition('nid', $n->nid)
+        ->execute();
+    }
+
+    // Invoke scheduler API.
+    _scheduler_scheduler_api($n, 'promote');
+
+    $result = TRUE;
+  }
+
+  return $result;
+}
+
+/**
+ * Unpublish scheduled nodes.
+ * @return
+ *   TRUE is any node has been unpublished, FALSE otherwise.
+ */
+function _scheduler_unpromote() {
+  $result = FALSE;
+  $date_format = variable_get('scheduler_date_format', SCHEDULER_DATE_FORMAT);
+
+  // If the time is greater than the time to unpromote a node, unpromote it.
+  $query = db_select('scheduler', 's');
+  $query->addField('s', 'nid');
+  $query->addJoin('LEFT', 'node', 'n', 's.nid = n.nid');
+  $query->condition('s.unpromote_on', 0, '>');
+  $query->condition('s.unpromote_on', REQUEST_TIME, '<');
+  $queryResult = $query->execute();
+  $nids = array();
+  while ($node = $queryResult->fetchObject()) {
+    $nids[] = $node->nid;
+  }
+
+  $nids = array_unique(array_merge($nids, _scheduler_scheduler_nid_list('unpromote')));
+
+  foreach ($nids as $nid) {
+    // If this node is to be unpromoted, we can update the node and remove the
+    // record since it cannot be repromoted.
+    $n = node_load($nid);
+    $old_change_date = $n->changed;
+    $n->changed = $n->unpromote_on;
+
+    if ($n->status == 1) {
+      $create_demotion_revision = variable_get('scheduler_unpromote_revision_'. $n->type, 0) == 1;
+      if ($create_demotion_revision) {
+        $n->revision = TRUE;
+        $n->log = "Node unpromoted by scheduler module. Original change date was ".  format_date($old_change_date, 'custom', $date_format) .".";
+      }
+
+      // Use the actions system to unpromote the node.
+      watchdog('scheduler', '@type: scheduled demotion of %title.', array('@type' => $n->type, '%title' => $n->title), WATCHDOG_NOTICE, l(t('view'), 'node/'. $n->nid));
+      $actions = array('node_unpromote_action', 'node_save_action');
+      $context['node'] = $n;
+      actions_do($actions, $n, $context, NULL, NULL);
+    }
+
+    db_delete('scheduler')
+      ->condition('nid', $n->nid)
+      ->execute();
+
+    // Invoke scheduler API.
+    _scheduler_scheduler_api($n, 'unpromote');
+
+    $result = TRUE;
+  }
+
+  return $result;
+}
+
+/**
  * Gather node IDs for all nodes that need to be $action'ed
  *
  * @param $action
@@ -930,7 +1247,9 @@ function scheduler_content_extra_fields($type_name) {
   $fields = array();
   $publishing_enabled = variable_get('scheduler_publish_enable_'. $type_name, 0) == 1;
   $unpublishing_enabled = variable_get('scheduler_unpublish_enable_'. $type_name, 0) == 1;
-  if ($publishing_enabled || $unpublishing_enabled) {
+  $promoting_enabled = variable_get('scheduler_promote_enable_'. $type_name, 0) == 1;
+  $demoting_enabled = variable_get('scheduler_unpromote_enable_'. $type_name, 0) == 1;
+  if ($publishing_enabled || $unpublishing_enabled || $promoting_enabled || $demoting_enabled) {
     $fields['scheduler_settings'] = array(
       'label' => t('Scheduler'),
       'description' => t('Scheduler module form.'),
@@ -953,6 +1272,12 @@ function scheduler_preprocess_node(&$variables, $hook) {
   if (!empty($node->unpublish_on) && $node->unpublish_on && is_numeric($node->unpublish_on)) {
     $variables['unpublish_on'] = format_date($node->unpublish_on, 'custom', $date_format);
   }
+  if (!empty($node->promote_on) && $node->promote_on && is_numeric($node->promote_on)) {
+    $variables['promote_on'] = format_date($node->promote_on, 'custom', $date_format);
+  }
+  if (!empty($node->unpromote_on) && $node->unpromote_on && is_numeric($node->unpromote_on)) {
+    $variables['unpromote_on'] = format_date($node->unpromote_on, 'custom', $date_format);
+  }
 }
 
 /**
@@ -963,6 +1288,8 @@ function scheduler_feeds_processor_targets_alter(&$targets, $processor, $content
 
   $publishing_enabled = variable_get('scheduler_publish_enable_'. $content_type, 0);
   $unpublishing_enabled = variable_get('scheduler_unpublish_enable_'. $content_type, 0);
+  $promoting_enabled = variable_get('scheduler_promote_enable_'. $content_type, 0);
+  $demoting_enabled = variable_get('scheduler_unpromote_enable_'. $content_type, 0);
 
   if ($publishing_enabled) {
     $targets['publish_on'] = array(
@@ -978,6 +1305,20 @@ function scheduler_feeds_processor_targets_alter(&$targets, $processor, $content
       'callback' => 'scheduler_set_target'
     );
   }
+  if ($promoting_enabled) {
+    $targets['promote_on'] = array(
+      'name' => t('Scheduler: promote on'),
+      'description' => t('The date, when scheduler module should promote node.'),
+      'callback' => 'scheduler_set_target'
+    );
+  }
+  if ($demoting_enabled) {
+    $targets['unpromote_on'] = array(
+      'name' => t('Scheduler: unpromote on'),
+      'description' => t('The date, when scheduler module should unpromote node.'),
+      'callback' => 'scheduler_set_target'
+    );
+  }
 }
 
 /**
diff --git a/scheduler.tokens.inc b/scheduler.tokens.inc
index c35fd70..e26ff75 100644
--- a/scheduler.tokens.inc
+++ b/scheduler.tokens.inc
@@ -19,6 +19,16 @@ function scheduler_token_info() {
     'description' => t("The date the node will be unpublished."),
     'type' => 'date',
   );
+  $info['tokens']['node']['scheduler-promote'] = array(
+    'name' => t('Promote on date'),
+    'description' => t("The date the node will be promoted."),
+    'type' => 'date',
+  );
+  $info['tokens']['node']['scheduler-unpromote'] = array(
+    'name' => t('Unpromote on date'),
+    'description' => t("The date the node will be unpromoted."),
+    'type' => 'date',
+  );
 
   return $info;
 }
@@ -45,6 +55,16 @@ function scheduler_tokens($type, $tokens, array $data = array(), array $options
             $replacements[$original] = format_date($node->unpublish_on, 'medium', '', NULL, $language_code);
           }
           break;
+        case 'scheduler-promote':
+          if (isset($node->promote_on)) {
+            $replacements[$original] = format_date($node->promote_on, 'medium', '', NULL, $language_code);
+          }
+          break;
+        case 'scheduler-unpromote':
+          if (isset($node->unpromote_on)) {
+            $replacements[$original] = format_date($node->unpromote_on, 'medium', '', NULL, $language_code);
+          }
+          break;
       }
     }
 
@@ -55,6 +75,12 @@ function scheduler_tokens($type, $tokens, array $data = array(), array $options
     if (isset($node->unpublish_on) && $unpublish_tokens = token_find_with_prefix($tokens, 'scheduler-unpublish')) {
       $replacements += token_generate('date', $unpublish_tokens, array('date' => $node->unpublish_on), $options);
     }
+    if (isset($node->promote_on) && $promote_tokens = token_find_with_prefix($tokens, 'scheduler-promote')) {
+      $replacements += token_generate('date', $promote_tokens, array('date' => $node->promote_on), $options);
+    }
+    if (isset($node->unpromote_on) && $unpromote_tokens = token_find_with_prefix($tokens, 'scheduler-unpromote')) {
+      $replacements += token_generate('date', $unpromote_tokens, array('date' => $node->unpromote_on), $options);
+    }
   }
 
   return $replacements;
diff --git a/scheduler.views.inc b/scheduler.views.inc
index bae8e26..fa544b4 100644
--- a/scheduler.views.inc
+++ b/scheduler.views.inc
@@ -49,6 +49,40 @@ function scheduler_views_data() {
     ),
   );
 
+  $tables['scheduler']['promote_on'] = array(
+    'title' => t('Promote on'),
+    'help' => t('Date/time on which the article will be automatically promoted'),
+    'field' => array(
+      'handler' => 'views_handler_field_date',
+      'click sortable' => TRUE,
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_date',
+      'label' => t('Promote on'),
+      'allow empty' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+  );
+
+  $tables['scheduler']['unpromote_on'] = array(
+    'title' => t('Unpromote on'),
+    'help' => t('Date/time on which the article will be automatically unpromoted'),
+    'field' => array(
+      'handler' => 'views_handler_field_date',
+      'click sortable' => TRUE,
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_date',
+      'label' => t('Unpromote on'),
+      'allow empty' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+  );
+
   $tables['scheduler']['publish_countdown'] = array(
     'title' => t('Publish countdown'),
     'help' => t('Time until the article will be automatically published'),
@@ -69,6 +103,26 @@ function scheduler_views_data() {
     ),
   );
 
+  $tables['scheduler']['promote_countdown'] = array(
+    'title' => t('Promote countdown'),
+    'help' => t('Time until the article will be automatically promoted'),
+    'field' => array(
+  	  'handler' => 'scheduler_handler_field_scheduler_countdown',
+      'click sortable' => FALSE,
+      'timestamp_field' => 'promote_on',
+    ),
+  );
+
+  $tables['scheduler']['unpromote_countdown'] = array(
+    'title' => t('Unromote countdown'),
+    'help' => t('Time until the article will be automatically unpromoted'),
+    'field' => array(
+  	  'handler' => 'scheduler_handler_field_scheduler_countdown',
+      'click sortable' => FALSE,
+      'timestamp_field' => 'unpromote_on',
+    ),
+  );
+
   return $tables;
 }
 
-- 
1.7.1

