public function SchedulerManager::publish in Scheduler 8
Same name and namespace in other branches
- 2.x src/SchedulerManager.php \Drupal\scheduler\SchedulerManager::publish()
Publish scheduled nodes.
Return value
bool TRUE if any node has been published, FALSE otherwise.
Throws
\Drupal\scheduler\Exception\SchedulerMissingDateException
\Drupal\scheduler\Exception\SchedulerNodeTypeNotEnabledException
File
- src/
SchedulerManager.php, line 131
Class
- SchedulerManager
- Defines a scheduler manager.
Namespace
Drupal\schedulerCode
public function publish() {
$result = FALSE;
$action = 'publish';
// Select all nodes of the types that are enabled for scheduled publishing
// and where publish_on is less than or equal to the current time.
$nids = [];
$scheduler_enabled_types = array_keys(_scheduler_get_scheduler_enabled_node_types($action));
if (!empty($scheduler_enabled_types)) {
$query = $this->entityTypeManager
->getStorage('node')
->getQuery()
->exists('publish_on')
->condition('publish_on', $this->time
->getRequestTime(), '<=')
->condition('type', $scheduler_enabled_types, 'IN')
->latestRevision()
->sort('publish_on')
->sort('nid');
// Disable access checks for this query.
// @see https://www.drupal.org/node/2700209
$query
->accessCheck(FALSE);
$nids = $query
->execute();
}
// Allow other modules to add to the list of nodes to be published.
$nids = array_unique(array_merge($nids, $this
->nidList($action)));
// Allow other modules to alter the list of nodes to be published.
$this->moduleHandler
->alter('scheduler_nid_list', $nids, $action);
// In 8.x the entity translations are all associated with one node id
// unlike 7.x where each translation was a separate node. This means that
// the list of node ids returned above may have some translations that need
// processing now and others that do not.
/** @var \Drupal\node\NodeInterface[] $nodes */
$nodes = $this
->loadNodes($nids);
foreach ($nodes as $node_multilingual) {
// The API calls could return nodes of types which are not enabled for
// scheduled publishing, so do not process these. This check can be done
// once, here, as the setting will be the same for all translations.
if (!$node_multilingual->type->entity
->getThirdPartySetting('scheduler', 'publish_enable', $this
->setting('default_publish_enable'))) {
throw new SchedulerNodeTypeNotEnabledException(sprintf("Node %d '%s' will not be published because node type '%s' is not enabled for scheduled publishing", $node_multilingual
->id(), $node_multilingual
->getTitle(), node_get_type_label($node_multilingual)));
}
$languages = $node_multilingual
->getTranslationLanguages();
foreach ($languages as $language) {
// The object returned by getTranslation() behaves the same as a $node.
$node = $node_multilingual
->getTranslation($language
->getId());
// If the current translation does not have a publish on value, or it is
// later than the date we are processing then move on to the next.
$publish_on = $node->publish_on->value;
if (empty($publish_on) || $publish_on > $this->time
->getRequestTime()) {
continue;
}
// Check that other modules allow the action on this node.
if (!$this
->isAllowed($node, $action)) {
continue;
}
// $node->setChangedTime($publish_on) will fail badly if an API call has
// removed the date. Trap this as an exception here and give a
// meaningful message.
// @todo This will now never be thrown due to the empty(publish_on)
// check above to cater for translations. Remove this exception?
if (empty($node->publish_on->value)) {
$field_definitions = $this->entityTypeManager
->getFieldDefinitions('node', $node
->getType());
$field = (string) $field_definitions['publish_on']
->getLabel();
throw new SchedulerMissingDateException(sprintf("Node %d '%s' will not be published because field '%s' has no value", $node
->id(), $node
->getTitle(), $field));
}
// Trigger the PRE_PUBLISH event so that modules can react before the
// node is published.
$event = new SchedulerEvent($node);
$this
->dispatch($event, SchedulerEvents::PRE_PUBLISH);
$node = $event
->getNode();
// Update 'changed' timestamp.
$node
->setChangedTime($publish_on);
$old_creation_date = $node
->getCreatedTime();
$msg_extra = '';
// If required, set the created date to match published date.
if ($node->type->entity
->getThirdPartySetting('scheduler', 'publish_touch', $this
->setting('default_publish_touch')) || $node
->getCreatedTime() > $publish_on && $node->type->entity
->getThirdPartySetting('scheduler', 'publish_past_date_created', $this
->setting('default_publish_past_date_created'))) {
$node
->setCreatedTime($publish_on);
$msg_extra = $this
->t('The previous creation date was @old_creation_date, now updated to match the publishing date.', [
'@old_creation_date' => $this->dateFormatter
->format($old_creation_date, 'short'),
]);
}
$create_publishing_revision = $node->type->entity
->getThirdPartySetting('scheduler', 'publish_revision', $this
->setting('default_publish_revision'));
if ($create_publishing_revision) {
$node
->setNewRevision();
// Use a core date format to guarantee a time is included.
$revision_log_message = rtrim($this
->t('Published by Scheduler. The scheduled publishing date was @publish_on.', [
'@publish_on' => $this->dateFormatter
->format($publish_on, 'short'),
]) . ' ' . $msg_extra);
$node
->setRevisionLogMessage($revision_log_message)
->setRevisionCreationTime($this->time
->getRequestTime());
}
// Unset publish_on so the node will not get rescheduled by subsequent
// calls to $node->save().
$node->publish_on->value = NULL;
// Invoke all implementations of hook_scheduler_publish_action() to
// allow other modules to do the "publishing" process instead of
// Scheduler.
$hook = 'scheduler_publish_action';
$processed = FALSE;
$failed = FALSE;
foreach ($this->moduleHandler
->getImplementations($hook) as $module) {
$function = $module . '_' . $hook;
$return = $function($node);
$processed = $processed || $return === 1;
$failed = $failed || $return === -1;
}
// Log the fact that a scheduled publication is about to take place.
$view_link = $node
->toLink($this
->t('View node'));
$node_type = $this->entityTypeManager
->getStorage('node_type')
->load($node
->bundle());
$node_type_link = $node_type
->toLink($this
->t('@label settings', [
'@label' => $node_type
->label(),
]), 'edit-form');
$logger_variables = [
'@type' => $node_type
->label(),
'%title' => $node
->getTitle(),
'link' => $node_type_link
->toString() . ' ' . $view_link
->toString(),
'@hook' => 'hook_' . $hook,
];
if ($failed) {
// At least one hook function returned a failure or exception, so stop
// processing this node and move on to the next one.
$this->logger
->warning('Publishing failed for %title. Calls to @hook returned a failure code.', $logger_variables);
continue;
}
elseif ($processed) {
// The node had 'publishing' processed by a module implementing the
// hook, so no need to do anything more, apart from log this result.
$this->logger
->notice('@type: scheduled processing of %title completed by calls to @hook.', $logger_variables);
}
else {
// None of the above hook calls processed the node and there were no
// errors detected so set the node to published.
$this->logger
->notice('@type: scheduled publishing of %title.', $logger_variables);
$node
->setPublished();
}
// Invoke the event to tell Rules that Scheduler has published the node.
if ($this->moduleHandler
->moduleExists('scheduler_rules_integration')) {
_scheduler_rules_integration_dispatch_cron_event($node, 'publish');
}
// Trigger the PUBLISH event so that modules can react after the node is
// published.
$event = new SchedulerEvent($node);
$this
->dispatch($event, SchedulerEvents::PUBLISH);
// Use the standard actions system to publish and save the node.
$node = $event
->getNode();
$action_id = 'node_publish_action';
if ($this->moduleHandler
->moduleExists('workbench_moderation_actions')) {
// workbench_moderation_actions module uses a custom action instead.
$action_id = 'state_change__node__published';
}
$this->entityTypeManager
->getStorage('action')
->load($action_id)
->getPlugin()
->execute($node);
$result = TRUE;
}
}
return $result;
}