It would be nice if the list of nodes that are about to be published or unpublished can be altered. It is already possible to add additional nodes to the list with hook_scheduler_nid_list(), but it is not possible to reorder the list or remove nodes from the list.

Example use cases:

  • Limit the number of nodes to be (un)published per cron run to reduce server load.
  • Making sure nodes are published in the right order, so that nodes that contain references to other nodes are not published (and potentially cached) in an incomplete state.

The use case of preventing nodes to be published has been split off to #1979460: Allow other modules to prevent scheduled nodes from being published / unpublished.

Comments

pfrenssen’s picture

Status: Active » Needs review
StatusFileSize
new1.2 KB
jonathan1055’s picture

Title: Allow to alter the list of nodes to be published or unpublished » Allow other modules to alter the list of nodes to be published or unpublished

Hi pfrenssen

This is a really nice idea, and so simple to implement! I did not know that implementations of hook_TYPE_alter() could be used to alter anything. The documentation talks about using it to alter content types and forms, but of course, if a module implements it corrctely then there is no reason that we cannot utilise the power of drupal_alter to do anything.

Your patch does not use the 4th parameter of drupal_alter. Can you think of anything that we could usefully pass for $context2?

Also, could you post your current implementation of hook_scheduler_node_list_alter, for interest, and to allow others to see why this is such a nice idea. I will also write my own hook_scheduler_node_list_alter to test your patch.

Jonathan

pfrenssen’s picture

Yes drupal_alter() is awesome, it makes interoperability between modules really easy. I don't think we need to use the 4th parameter, this is used to pass additional contextual information, but we basically have only two use cases: publishing and unpublishing, and these can be passed in one variable.

My implementation is based on #1955938: Publish only "approved" nodes . I will continue on that issue soon and post a patch when it is ready, but the basic idea is as follows:

/**
 * Implements hook_scheduler_node_list_alter().
 */
function MYMODULE_scheduler_node_list_alter(&$nids, $action) {
  // Only allow to publish nodes authored by UID1.
  if ($action == 'publish') {
    foreach ($nids as $key => $nid) {
      $node = node_load($nid);
      if ($node->uid != 1) {
        unset($nids[$key]);
      }
    }
  }
}
jonathan1055’s picture

Hi,
Just trying this, and I get

Fatal error: Only variables can be passed by reference in /sites/all/modules/scheduler/scheduler.module on line 754

As you might have guessed, this is the call to drupal_alter, which I implemented as copied from your example above. $nids is an array but I presume you had this working, so is there something I need to do to enable it?

Just realised I am still using D7.17 so I will upgrade to 7.22 then test again.

Jonathan

jonathan1055’s picture

Think I've fixed it. Nothing to do with old drupal version. The code in your patch needs to be:

  // Allow other modules to alter the node list.
  $context = 'publish';
  drupal_alter('scheduler_node_list', $nids, $context);

ie the context should be a variable not a fixed string.

Jonathan

jonathan1055’s picture

Just discovered that the variable $context is already used in _scheduler_publish() and _scheduler_unpublish() - so it would need to be a different name, eg $context1, or $scheduler_context or something.

jonathan1055’s picture

Status: Needs review » Needs work

Forgot to change the status as the patch needs amending.
My tests are working now and this will be a very useful (and powerful) addition. Thanks for the idea pfrenssen.

pfrenssen’s picture

Thanks for the review! Your remarks look valid, it should pass a variable instead of a fixed string.

I will update the patch when I get on the train home, in a few hours.

pfrenssen’s picture

Status: Needs work » Needs review
StatusFileSize
new1.26 KB

Ok I have updated the patch, this should work better. I have opted for the variable name $action, this is consistent with hook_scheduler_api() and hook_scheduler_nid_list().

jonathan1055’s picture

Tested the patch in #9 and it works. Yes, $action is a good choice.

Just one minor question - we already implement the function hook_scheduler_nid_list() so is hook_scheduler_node_list_alter() ok? I guess the word 'alter' makes it distinct enough, just. I thought I would mention it now, as we will never be able to change it after other users/modules start implementing their hooks.

I am kind-of ok with the name as it stands, but would like you to think about it, and also to get the opinion of the co-maintainers and others.

Jonathan

pfrenssen’s picture

StatusFileSize
new1.32 KB

Good idea, that would be more consistent. I have renamed the alter hook to hook_scheduler_nid_list_alter() and improved the documentation a bit.

jonathan1055’s picture

Status: Needs review » Reviewed & tested by the community

Excellent. Reviewed and tested. Rick has already just made a 7.x-1.1 but hopefully release 1.2 will not be too far away.

To add to your example in #3 here is what I used to test the hook:

function d7testing_scheduler_nid_list_alter(&$nids, $action) {
  // Only allow to publish nodes authored by uid 1 or who has administer role.
  foreach ($nids as $key => $nid) {
    $node = node_load($nid);    $user = user_load($node->uid);
    dd('node ' . $node->nid . ' author is ' . $node->name . ' (' . $node->uid . ')');
    if ($node->uid != 1 && !isset($user->roles['3'])) {
      watchdog('d7testing', 'Blocked @action of @author @type %title.',
               array('@author' => $node->name . ' (' . $node->uid . ')', '@action' => $action, '%title' => $node->title, '@type' => $node->type), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));

      // Move the time on by one hour, because this node is not going to be actioned.
      $arr = array($action . '_on' => time() + 3600);
      db_merge('scheduler')
      ->key(array('nid' => $node->nid))
      ->fields($arr)
      ->execute();
      dd('removed node ' . $node->nid . ' and set new ' . $action . ' time of ' . format_date(time() + 3600));

      // Remove this node from the list.
      unset($nids[$key]);
    }
  }
}


Thought you might be interested in the idea of pushing back the publish time. I have a calendar view of upcoming nodes to be published and the ones which were blocked were showing as upcoming in the past, which looked all wrong. This way, if the node is rejected it is continually pushed forward until the author is accepted.

Jonathan

pfrenssen’s picture

Nice example! I am now also working on a real life application in #1955938: Publish only "approved" nodes .

pfrenssen’s picture

This creates an interesting problem. As other modules can now prevent content from being scheduled, they should also have the possibility to suppress the status message that is shown when scheduled content is saved:

This post is unpublished and will be published 2013-04-24.

In my use case, scheduled content will only be published if it has been approved by an administrator. So when regular content editors submit a node, they should not receive the above message, since it is not sure at that point if the content will be approved for publication.

I saw that this message is output in hook_node_presave(), which is also not really the right place for this as it fires on every node_save(). More appropriate would be to move this to hook_node_submit(), so that it would only show when nodes are saved through submitting the node form.

jonathan1055’s picture

Yes there might be better ways to put out that message, and also allow its content to be modified by modules which implement hook_scheduler_nid_list_alter()
Maybe that same hook function could be called directly with a specific parameter, instead of having a different hook to implement? Plenty of options to play with.

Maybe, as this issue is RTBC I would suggest opening a new one, not to delay this patch?

pfrenssen’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new3.68 KB

Here is an idea how to solve it. Not yet tested.

jonathan1055’s picture

Hi,
Had a look at your patch, and I've got a few things to discuss

  1.    // 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 since it will be published at a later date
    -  if (isset($node->publish_on) && $node->publish_on != '' && is_numeric($node->publish_on) && $node->publish_on > REQUEST_TIME) {
    +  if (scheduler_is_scheduled($node, 'publish') && $node->publish_on > REQUEST_TIME) {
         $node->status = 0; 
    

    The line $node->status = 0 which ensures a scheduled node does not get immediately published is included in that conditional block. You are affecting too much here, it is only the message that you want to suppress.

  2. You have implemented hook_scheduler_is_scheduled() in addition to hook_scheduler_nid_list_alter(). It is my guess that the code in these two functions would be very similar or at least big chunks repeated. I'm not sure that contrib users would understand why they need to implement two functions. There should be a better way we can do it with one hook.
  3. +  // Convert the action into the node property (e.g. 'publish_on').
    +  $action = $action . '_on';
    +
    +  return $result && !empty($node->$key);
    

    The first line should be $key = $action . '_on';

  4. Actually in that function you could initialise $result to !empty($node->$key) at the top, instead of TRUE. Then the return would be a simple return $result at the end.

Nice work though. It will be good when done.

pfrenssen’s picture

Thanks for the remarks! Sorry for the lack of explanation with the patch, I was in a hurry to catch my train and quickly put my work in progress online before leaving.

1. The line $node->status = 0 which ensures a scheduled node does not get immediately published is included in that conditional block. You are affecting too much here, it is only the message that you want to suppress.

I think this is actually completely covered. The original check was too broad. The is_numeric($node->publish_on) check was not needed because the preceding code in that function already ensures that this is either 0 or a number representing unix time. And isset($node->publish_on) && $node->publish_on != '' can be rewritten as !empty($node->publish_on), which is covered by <code>scheduler_is_scheduled().

You have implemented hook_scheduler_is_scheduled() in addition to hook_scheduler_nid_list_alter(). It is my guess that the code in these two functions would be very similar or at least big chunks repeated. I'm not sure that contrib users would understand why they need to implement two functions. There should be a better way we can do it with one hook.

Yesterday I looked for a way to do this in one hook, but it did not seem to be possible to allow all the use cases in the summary:

  • The reordering / limiting of nodes in the node list needs to be done with an alter hook that provides a list of nodes or nids.
  • Preventing of node publishing / displaying messages needs to be done in a hook that provides a single full node object.

But yeah this needs to be documented more clearly, both in the code, and preferably also in a scheduler.api.php file with some examples.

The first line should be $key = $action . '_on';

Actually in that function you could initialise $result to !empty($node->$key) at the top, instead of TRUE. Then the return would be a simple return $result at the end.

Good points, will address these.

Maybe, as this issue is RTBC I would suggest opening a new one, not to delay this patch?

I agree, I will update the summary to limit the scope of this issue to reordering/limiting the node list and create a new issue for the new hook. The latest patch is trying to solve two problems at once, that is never good.

pfrenssen’s picture

I have updated the issue summary to reduce the scope to only reordering / limiting the list of scheduled nodes, and have created new issues for the second hook and the API documentation:

pfrenssen’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new1.32 KB

This is the exact same patch from #11 that has been RTBC'd by jonathan1055 in #12, so setting status back to RTBC.

The approach discussed in comments #14 to #18 has been moved to a separate issue: #1979460: Allow other modules to prevent scheduled nodes from being published / unpublished

rickmanelius’s picture

Status: Reviewed & tested by the community » Fixed

Hi pfrenssen,

Thanks for your contribution and being willing to split this off into different issues. This has been committed:
http://drupalcode.org/project/scheduler.git/commitdiff/34ea8a2?hp=0d0be0...

-Rick

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

Anonymous’s picture

Issue summary: View changes

Reduced scope