Issue #1846172 by tim.plunkett, damiankloip: Replace the actions API.
parent
f164dcc5fb
commit
b0cf1be964
|
@ -154,6 +154,9 @@ services:
|
|||
plugin.manager.archiver:
|
||||
class: Drupal\Core\Archiver\ArchiverManager
|
||||
arguments: ['@container.namespaces']
|
||||
plugin.manager.action:
|
||||
class: Drupal\Core\Action\ActionManager
|
||||
arguments: ['@container.namespaces']
|
||||
request:
|
||||
class: Symfony\Component\HttpFoundation\Request
|
||||
event_dispatcher:
|
||||
|
|
|
@ -2929,16 +2929,12 @@ function drupal_classloader_register($name, $path) {
|
|||
*
|
||||
* Example:
|
||||
* @code
|
||||
* function actions_do(...) {
|
||||
* // $stack tracks the number of recursive calls.
|
||||
* static $stack;
|
||||
* $stack++;
|
||||
* if ($stack > variable_get('action_max_stack', 35)) {
|
||||
* ...
|
||||
* return;
|
||||
* function system_get_module_info($property) {
|
||||
* static $info;
|
||||
* if (!isset($info)) {
|
||||
* $info = new ModuleInfo('system_info', 'cache');
|
||||
* }
|
||||
* ...
|
||||
* $stack--;
|
||||
* return $info[$property];
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Action\ActionBag.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Action;
|
||||
|
||||
use Drupal\Component\Plugin\PluginBag;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
|
||||
/**
|
||||
* Provides a container for lazily loading Action plugins.
|
||||
*/
|
||||
class ActionBag extends PluginBag {
|
||||
|
||||
/**
|
||||
* The manager used to instantiate the plugins.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Constructs a new ActionBag object.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $manager
|
||||
* The manager to be used for instantiating plugins.
|
||||
* @param array $instance_ids
|
||||
* The ids of the plugin instances with which we are dealing.
|
||||
* @param array $configuration
|
||||
* An array of configuration.
|
||||
*/
|
||||
public function __construct(PluginManagerInterface $manager, array $instance_ids, array $configuration) {
|
||||
$this->manager = $manager;
|
||||
$this->instanceIDs = drupal_map_assoc($instance_ids);
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializePlugin($instance_id) {
|
||||
if (isset($this->pluginInstances[$instance_id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pluginInstances[$instance_id] = $this->manager->createInstance($instance_id, $this->configuration);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Action\ActionBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Action;
|
||||
|
||||
use Drupal\Core\Action\ActionInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginBase;
|
||||
|
||||
/**
|
||||
* Provides a base implementation for an Action plugin.
|
||||
*/
|
||||
abstract class ActionBase extends ContainerFactoryPluginBase implements ActionInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function executeMultiple(array $entities) {
|
||||
foreach ($entities as $entity) {
|
||||
$this->execute($entity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Action\ActionInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Action;
|
||||
|
||||
use Drupal\Core\Executable\ExecutableInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for an Action plugin.
|
||||
*
|
||||
* @see \Drupal\Core\Annotation\Action
|
||||
* @see \Drupal\Core\Action\ActionManager
|
||||
*/
|
||||
interface ActionInterface extends ExecutableInterface {
|
||||
|
||||
/**
|
||||
* Executes the plugin for an array of objects.
|
||||
*
|
||||
* @param array $objects
|
||||
* An array of entities.
|
||||
*/
|
||||
public function executeMultiple(array $objects);
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Action\ActionManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Action;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerBase;
|
||||
use Drupal\Core\Plugin\Discovery\AlterDecorator;
|
||||
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
|
||||
use Drupal\Core\Plugin\Factory\ContainerFactory;
|
||||
|
||||
/**
|
||||
* Provides an Action plugin manager.
|
||||
*
|
||||
* @see \Drupal\Core\Annotation\Operation
|
||||
* @see \Drupal\Core\Action\OperationInterface
|
||||
*/
|
||||
class ActionManager extends PluginManagerBase {
|
||||
|
||||
/**
|
||||
* Constructs a ActionManager object.
|
||||
*
|
||||
* @param \Traversable $namespaces
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces) {
|
||||
$this->discovery = new AnnotatedClassDiscovery('Action', $namespaces, array(), 'Drupal\Core\Annotation\Action');
|
||||
$this->discovery = new AlterDecorator($this->discovery, 'action_info');
|
||||
|
||||
$this->factory = new ContainerFactory($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin definitions for this entity type.
|
||||
*
|
||||
* @param string $type
|
||||
* The entity type name.
|
||||
*
|
||||
* @return array
|
||||
* An array of plugin definitions for this entity type.
|
||||
*/
|
||||
public function getDefinitionsByType($type) {
|
||||
return array_filter($this->getDefinitions(), function ($definition) use ($type) {
|
||||
return $definition['type'] === $type;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Action\ConfigurableActionBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Action;
|
||||
|
||||
use Drupal\Core\Action\ConfigurableActionInterface;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Provides a base implementation for a configurable Action plugin.
|
||||
*/
|
||||
abstract class ConfigurableActionBase extends ActionBase implements ConfigurableActionInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->configuration += $this->getDefaultConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default configuration for this action.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaultConfiguration() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfiguration() {
|
||||
return $this->configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate(array &$form, array &$form_state) {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Action\ConfigurableActionInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Action;
|
||||
|
||||
use Drupal\Core\Action\ActionInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for an Action plugin.
|
||||
*
|
||||
* @see \Drupal\Core\Annotation\Operation
|
||||
* @see \Drupal\Core\Action\OperationManager
|
||||
*/
|
||||
interface ConfigurableActionInterface extends ActionInterface {
|
||||
|
||||
/**
|
||||
* Returns this plugin's configuration.
|
||||
*
|
||||
* @return array
|
||||
* An array of this action plugin's configuration.
|
||||
*/
|
||||
public function getConfiguration();
|
||||
|
||||
/**
|
||||
* Form constructor.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param array $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* The form structure.
|
||||
*/
|
||||
public function form(array $form, array &$form_state);
|
||||
|
||||
/**
|
||||
* Form validation handler.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param array $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*/
|
||||
public function validate(array &$form, array &$form_state);
|
||||
|
||||
/**
|
||||
* Form submission handler.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param array $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*/
|
||||
public function submit(array &$form, array &$form_state);
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Annotation\Action.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines an Action annotation object.
|
||||
*
|
||||
* @see \Drupal\Core\Action\ActionInterface
|
||||
* @see \Drupal\Core\Action\ActionManager
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Action extends Plugin {
|
||||
|
||||
/**
|
||||
* The plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the action plugin.
|
||||
*
|
||||
* @ingroup plugin_translatable
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* The path for a confirmation form for this action.
|
||||
*
|
||||
* @todo Change this to accept a route.
|
||||
* @todo Provide a more generic way to allow an action to be confirmed first.
|
||||
*
|
||||
* @var string (optional)
|
||||
*/
|
||||
public $confirm_form_path = '';
|
||||
|
||||
/**
|
||||
* The entity type the action can apply to.
|
||||
*
|
||||
* @todo Replace with \Drupal\Core\Plugin\Context\Context.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type = '';
|
||||
|
||||
}
|
|
@ -185,6 +185,7 @@ class EntityManager extends PluginManagerBase {
|
|||
$class = $this->getControllerClass($entity_type, 'form', $operation);
|
||||
if (in_array('Drupal\Core\Entity\EntityControllerInterface', class_implements($class))) {
|
||||
$this->controllers['form'][$operation][$entity_type] = $class::createInstance($this->container, $entity_type, $this->getDefinition($entity_type));
|
||||
$this->controllers['form'][$operation][$entity_type]->setOperation($operation);
|
||||
}
|
||||
else {
|
||||
$this->controllers['form'][$operation][$entity_type] = new $class($operation);
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Admin page callbacks for the Action module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Post-deletion operations for deleting action orphans.
|
||||
*
|
||||
* @param $orphaned
|
||||
* An array of orphaned actions.
|
||||
*/
|
||||
function action_admin_delete_orphans_post($orphaned) {
|
||||
foreach ($orphaned as $callback) {
|
||||
drupal_set_message(t("Deleted orphaned action (%action).", array('%action' => $callback)));
|
||||
}
|
||||
}
|
|
@ -5,91 +5,11 @@
|
|||
* Hooks provided by the Actions module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Declares information about actions.
|
||||
*
|
||||
* Any module can define actions, and then call actions_do() to make those
|
||||
* actions happen in response to events.
|
||||
*
|
||||
* An action consists of two or three parts:
|
||||
* - an action definition (returned by this hook)
|
||||
* - a function which performs the action (which by convention is named
|
||||
* MODULE_description-of-function_action)
|
||||
* - an optional form definition function that defines a configuration form
|
||||
* (which has the name of the action function with '_form' appended to it.)
|
||||
*
|
||||
* The action function takes two to four arguments, which come from the input
|
||||
* arguments to actions_do().
|
||||
*
|
||||
* @return
|
||||
* An associative array of action descriptions. The keys of the array
|
||||
* are the names of the action functions, and each corresponding value
|
||||
* is an associative array with the following key-value pairs:
|
||||
* - 'type': The type of object this action acts upon. Core actions have types
|
||||
* 'node', 'user', 'comment', and 'system'.
|
||||
* - 'label': The human-readable name of the action, which should be passed
|
||||
* through the t() function for translation.
|
||||
* - 'configurable': If FALSE, then the action doesn't require any extra
|
||||
* configuration. If TRUE, then your module must define a form function with
|
||||
* the same name as the action function with '_form' appended (e.g., the
|
||||
* form for 'node_assign_owner_action' is 'node_assign_owner_action_form'.)
|
||||
* This function takes $context as its only parameter, and is paired with
|
||||
* the usual _submit function, and possibly a _validate function.
|
||||
* - 'triggers': An array of the events (that is, hooks) that can trigger this
|
||||
* action. For example: array('node_insert', 'user_update'). You can also
|
||||
* declare support for any trigger by returning array('any') for this value.
|
||||
* - 'behavior': (optional) A machine-readable array of behaviors of this
|
||||
* action, used to signal additionally required actions that may need to be
|
||||
* triggered. Modules that are processing actions should take special care
|
||||
* for the "presave" hook, in which case a dependent "save" action should
|
||||
* NOT be invoked.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function hook_action_info() {
|
||||
return array(
|
||||
'comment_unpublish_action' => array(
|
||||
'type' => 'comment',
|
||||
'label' => t('Unpublish comment'),
|
||||
'configurable' => FALSE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('comment_presave', 'comment_insert', 'comment_update'),
|
||||
),
|
||||
'comment_unpublish_by_keyword_action' => array(
|
||||
'type' => 'comment',
|
||||
'label' => t('Unpublish comment containing keyword(s)'),
|
||||
'configurable' => TRUE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('comment_presave', 'comment_insert', 'comment_update'),
|
||||
),
|
||||
'comment_save_action' => array(
|
||||
'type' => 'comment',
|
||||
'label' => t('Save comment'),
|
||||
'configurable' => FALSE,
|
||||
'triggers' => array('comment_insert', 'comment_update'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the actions declared by another module.
|
||||
*
|
||||
* Called by action_list() to allow modules to alter the return values from
|
||||
* implementations of hook_action_info().
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function hook_action_info_alter(&$actions) {
|
||||
$actions['node_unpublish_action']['label'] = t('Unpublish and remove from public view.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes code after an action is deleted.
|
||||
*
|
||||
* @param $aid
|
||||
* The action ID.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function hook_action_delete($aid) {
|
||||
db_delete('actions_assignments')
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the Actions module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function action_schema() {
|
||||
// 'action' is a reserved SQL keyword.
|
||||
$schema['actions'] = array(
|
||||
'description' => 'Stores action information.',
|
||||
'fields' => array(
|
||||
'aid' => array(
|
||||
'description' => 'Primary Key: Unique action ID.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '0',
|
||||
),
|
||||
'type' => array(
|
||||
'description' => 'The object that that action acts on (node, user, comment, system or custom types.)',
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'callback' => array(
|
||||
'description' => 'The callback function that executes when the action runs.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'parameters' => array(
|
||||
'description' => 'Parameters to be passed to the callback function.',
|
||||
'type' => 'blob',
|
||||
'not null' => TRUE,
|
||||
'size' => 'big',
|
||||
),
|
||||
'label' => array(
|
||||
'description' => 'Label of the action.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '0',
|
||||
),
|
||||
),
|
||||
'primary key' => array('aid'),
|
||||
);
|
||||
return $schema;
|
||||
}
|
|
@ -5,26 +5,6 @@
|
|||
* This is the Actions module for executing stored actions.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
|
||||
/**
|
||||
* @defgroup actions Actions
|
||||
* @{
|
||||
* Functions that perform an action on a certain system object.
|
||||
*
|
||||
* Action functions are declared by modules by implementing hook_action_info().
|
||||
* Modules can cause action functions to run by calling actions_do().
|
||||
*
|
||||
* Each action function takes two to four arguments:
|
||||
* - $entity: The object that the action acts on, such as a node, comment, or
|
||||
* user.
|
||||
* - $context: Array of additional information about what triggered the action.
|
||||
* - $a1, $a2: Optional additional information, which can be passed into
|
||||
* actions_do() and will be passed along to the action function.
|
||||
*
|
||||
* @} End of "defgroup actions".
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
|
@ -69,12 +49,17 @@ function action_menu() {
|
|||
'description' => 'Manage the actions defined for your site.',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
);
|
||||
$items['admin/config/system/actions/add'] = array(
|
||||
'title' => 'Create an advanced action',
|
||||
'type' => MENU_VISIBLE_IN_BREADCRUMB,
|
||||
'route_name' => 'action_admin_add',
|
||||
);
|
||||
$items['admin/config/system/actions/configure'] = array(
|
||||
'title' => 'Configure an advanced action',
|
||||
'type' => MENU_VISIBLE_IN_BREADCRUMB,
|
||||
'route_name' => 'action_admin_configure',
|
||||
);
|
||||
$items['admin/config/system/actions/delete/%action'] = array(
|
||||
$items['admin/config/system/actions/configure/%/delete'] = array(
|
||||
'title' => 'Delete action',
|
||||
'description' => 'Delete an action.',
|
||||
'route_name' => 'action_delete',
|
||||
|
@ -83,615 +68,10 @@ function action_menu() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements hook_rebuild().
|
||||
* Implements hook_entity_info().
|
||||
*/
|
||||
function action_rebuild() {
|
||||
// Synchronize any actions that were added or removed.
|
||||
action_synchronize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a given list of actions by executing their callback functions.
|
||||
*
|
||||
* Given the IDs of actions to perform, this function finds out what the
|
||||
* callback functions for the actions are by querying the database. Then
|
||||
* it calls each callback using the function call $function($object, $context,
|
||||
* $a1, $a2), passing the input arguments of this function (see below) to the
|
||||
* action function.
|
||||
*
|
||||
* @param $action_ids
|
||||
* The IDs of the actions to perform. Can be a single action ID or an array
|
||||
* of IDs. IDs of configurable actions must be given as numeric action IDs;
|
||||
* IDs of non-configurable actions may be given as action function names.
|
||||
* @param $object
|
||||
* The object that the action will act on: a node, user, or comment object.
|
||||
* @param $context
|
||||
* Associative array containing extra information about what triggered
|
||||
* the action call, with $context['hook'] giving the name of the hook
|
||||
* that resulted in this call to actions_do(). Additional parameters
|
||||
* will be used as the data for token replacement.
|
||||
* @param $a1
|
||||
* Passed along to the callback.
|
||||
* @param $a2
|
||||
* Passed along to the callback.
|
||||
*
|
||||
* @return
|
||||
* An associative array containing the results of the functions that
|
||||
* perform the actions, keyed on action ID.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function actions_do($action_ids, $object = NULL, $context = NULL, $a1 = NULL, $a2 = NULL) {
|
||||
// $stack tracks the number of recursive calls.
|
||||
static $stack;
|
||||
$stack++;
|
||||
$recursion_limit = config('action.settings')->get('recursion_limit');
|
||||
if ($stack > $recursion_limit) {
|
||||
watchdog('action', 'Stack overflow: recursion limit for actions_do() has been reached. Stack is limited by %limit calls.', array('%limit' => $recursion_limit), WATCHDOG_ERROR);
|
||||
return;
|
||||
}
|
||||
$actions = array();
|
||||
$available_actions = action_list();
|
||||
$result = array();
|
||||
if (is_array($action_ids)) {
|
||||
$conditions = array();
|
||||
foreach ($action_ids as $action_id) {
|
||||
if (is_numeric($action_id)) {
|
||||
$conditions[] = $action_id;
|
||||
}
|
||||
elseif (isset($available_actions[$action_id])) {
|
||||
$actions[$action_id] = $available_actions[$action_id];
|
||||
}
|
||||
}
|
||||
|
||||
// When we have action instances we must go to the database to retrieve
|
||||
// instance data.
|
||||
if (!empty($conditions)) {
|
||||
$query = db_select('actions');
|
||||
$query->addField('actions', 'aid');
|
||||
$query->addField('actions', 'type');
|
||||
$query->addField('actions', 'callback');
|
||||
$query->addField('actions', 'parameters');
|
||||
$query->condition('aid', $conditions, 'IN');
|
||||
foreach ($query->execute() as $action) {
|
||||
$actions[$action->aid] = $action->parameters ? unserialize($action->parameters) : array();
|
||||
$actions[$action->aid]['callback'] = $action->callback;
|
||||
$actions[$action->aid]['type'] = $action->type;
|
||||
}
|
||||
}
|
||||
|
||||
// Fire actions, in no particular order.
|
||||
foreach ($actions as $action_id => $params) {
|
||||
// Configurable actions need parameters.
|
||||
if (is_numeric($action_id)) {
|
||||
$function = $params['callback'];
|
||||
if (function_exists($function)) {
|
||||
$context = array_merge($context, $params);
|
||||
$result[$action_id] = $function($object, $context, $a1, $a2);
|
||||
}
|
||||
else {
|
||||
$result[$action_id] = FALSE;
|
||||
}
|
||||
}
|
||||
// Singleton action; $action_id is the function name.
|
||||
else {
|
||||
$result[$action_id] = $action_id($object, $context, $a1, $a2);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Optimized execution of a single action.
|
||||
else {
|
||||
// If it's a configurable action, retrieve stored parameters.
|
||||
if (is_numeric($action_ids)) {
|
||||
$action = db_query("SELECT callback, parameters FROM {actions} WHERE aid = :aid", array(':aid' => $action_ids))->fetchObject();
|
||||
$function = $action->callback;
|
||||
if (function_exists($function)) {
|
||||
$context = array_merge($context, unserialize($action->parameters));
|
||||
$result[$action_ids] = $function($object, $context, $a1, $a2);
|
||||
}
|
||||
else {
|
||||
$result[$action_ids] = FALSE;
|
||||
}
|
||||
}
|
||||
// Singleton action; $action_ids is the function name.
|
||||
else {
|
||||
if (function_exists($action_ids)) {
|
||||
$result[$action_ids] = $action_ids($object, $context, $a1, $a2);
|
||||
}
|
||||
else {
|
||||
// Set to avoid undefined index error messages later.
|
||||
$result[$action_ids] = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
$stack--;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discovers all available actions by invoking hook_action_info().
|
||||
*
|
||||
* This function contrasts with action_get_all_actions(); see the
|
||||
* documentation of action_get_all_actions() for an explanation.
|
||||
*
|
||||
* @param $reset
|
||||
* Reset the action info static cache.
|
||||
*
|
||||
* @return
|
||||
* An associative array keyed on action function name, with the same format
|
||||
* as the return value of hook_action_info(), containing all
|
||||
* modules' hook_action_info() return values as modified by any
|
||||
* hook_action_info_alter() implementations.
|
||||
*
|
||||
* @see hook_action_info()
|
||||
*/
|
||||
function action_list($reset = FALSE) {
|
||||
$actions = &drupal_static(__FUNCTION__);
|
||||
if (!isset($actions) || $reset) {
|
||||
$actions = module_invoke_all('action_info');
|
||||
drupal_alter('action_info', $actions);
|
||||
}
|
||||
|
||||
// See module_implements() for an explanation of this cast.
|
||||
return (array) $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all action instances from the database.
|
||||
*
|
||||
* This function differs from the action_list() function, which gathers
|
||||
* actions by invoking hook_action_info(). The actions returned by this
|
||||
* function and the actions returned by action_list() are partially
|
||||
* synchronized. Non-configurable actions from hook_action_info()
|
||||
* implementations are put into the database when action_synchronize() is
|
||||
* called, which happens when admin/config/system/actions is visited.
|
||||
* Configurable actions are not added to the database until they are configured
|
||||
* in the user interface, in which case a database row is created for each
|
||||
* configuration of each action.
|
||||
*
|
||||
* @return
|
||||
* Associative array keyed by numeric action ID. Each value is an associative
|
||||
* array with keys 'callback', 'label', 'type' and 'configurable'.
|
||||
*/
|
||||
function action_get_all_actions() {
|
||||
$actions = db_query("SELECT aid, type, callback, parameters, label FROM {actions}")->fetchAllAssoc('aid', PDO::FETCH_ASSOC);
|
||||
foreach ($actions as &$action) {
|
||||
$action['configurable'] = (bool) $action['parameters'];
|
||||
unset($action['parameters']);
|
||||
unset($action['aid']);
|
||||
}
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an associative array keyed by hashes of function names or IDs.
|
||||
*
|
||||
* Hashes are used to prevent actual function names from going out into HTML
|
||||
* forms and coming back.
|
||||
*
|
||||
* @param $actions
|
||||
* An associative array with function names or action IDs as keys
|
||||
* and associative arrays with keys 'label', 'type', etc. as values.
|
||||
* This is usually the output of action_list() or action_get_all_actions().
|
||||
*
|
||||
* @return
|
||||
* An associative array whose keys are hashes of the input array keys, and
|
||||
* whose corresponding values are associative arrays with components
|
||||
* 'callback', 'label', 'type', and 'configurable' from the input array.
|
||||
*/
|
||||
function action_actions_map($actions) {
|
||||
$actions_map = array();
|
||||
foreach ($actions as $callback => $array) {
|
||||
$key = Crypt::hashBase64($callback);
|
||||
$actions_map[$key]['callback'] = isset($array['callback']) ? $array['callback'] : $callback;
|
||||
$actions_map[$key]['label'] = $array['label'];
|
||||
$actions_map[$key]['type'] = $array['type'];
|
||||
$actions_map[$key]['configurable'] = $array['configurable'];
|
||||
}
|
||||
return $actions_map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an action array key (function or ID), given its hash.
|
||||
*
|
||||
* Faster than action_actions_map() when you only need the function name or ID.
|
||||
*
|
||||
* @param $hash
|
||||
* Hash of a function name or action ID array key. The array key
|
||||
* is a key into the return value of action_list() (array key is the action
|
||||
* function name) or action_get_all_actions() (array key is the action ID).
|
||||
*
|
||||
* @return
|
||||
* The corresponding array key, or FALSE if no match is found.
|
||||
*/
|
||||
function action_function_lookup($hash) {
|
||||
// Check for a function name match.
|
||||
$actions_list = action_list();
|
||||
foreach ($actions_list as $function => $array) {
|
||||
if (Crypt::hashBase64($function) == $hash) {
|
||||
return $function;
|
||||
}
|
||||
}
|
||||
$aid = FALSE;
|
||||
// Must be a configurable action; check database.
|
||||
$result = db_query("SELECT aid FROM {actions} WHERE parameters <> ''")->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($result as $row) {
|
||||
if (Crypt::hashBase64($row['aid']) == $hash) {
|
||||
$aid = $row['aid'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $aid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes actions that are provided by modules in hook_action_info().
|
||||
*
|
||||
* Actions provided by modules in hook_action_info() implementations are
|
||||
* synchronized with actions that are stored in the actions database table.
|
||||
* This is necessary so that actions that do not require configuration can
|
||||
* receive action IDs.
|
||||
*
|
||||
* @param $delete_orphans
|
||||
* If TRUE, any actions that exist in the database but are no longer
|
||||
* found in the code (for example, because the module that provides them has
|
||||
* been disabled) will be deleted.
|
||||
*/
|
||||
function action_synchronize($delete_orphans = FALSE) {
|
||||
$actions_in_code = action_list(TRUE);
|
||||
$actions_in_db = db_query("SELECT aid, callback, label FROM {actions} WHERE parameters = ''")->fetchAllAssoc('callback', PDO::FETCH_ASSOC);
|
||||
|
||||
// Go through all the actions provided by modules.
|
||||
foreach ($actions_in_code as $callback => $array) {
|
||||
// Ignore configurable actions since their instances get put in when the
|
||||
// user adds the action.
|
||||
if (!$array['configurable']) {
|
||||
// If we already have an action ID for this action, no need to assign aid.
|
||||
if (isset($actions_in_db[$callback])) {
|
||||
unset($actions_in_db[$callback]);
|
||||
}
|
||||
else {
|
||||
// This is a new singleton that we don't have an aid for; assign one.
|
||||
db_insert('actions')
|
||||
->fields(array(
|
||||
'aid' => $callback,
|
||||
'type' => $array['type'],
|
||||
'callback' => $callback,
|
||||
'parameters' => '',
|
||||
'label' => $array['label'],
|
||||
))
|
||||
->execute();
|
||||
watchdog('action', "Action '%action' added.", array('%action' => $array['label']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Any actions that we have left in $actions_in_db are orphaned.
|
||||
if ($actions_in_db) {
|
||||
$orphaned = array_keys($actions_in_db);
|
||||
|
||||
if ($delete_orphans) {
|
||||
$actions = db_query('SELECT aid, label FROM {actions} WHERE callback IN (:orphaned)', array(':orphaned' => $orphaned))->fetchAll();
|
||||
foreach ($actions as $action) {
|
||||
action_delete($action->aid);
|
||||
watchdog('action', "Removed orphaned action '%action' from database.", array('%action' => $action->label));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$link = l(t('Remove orphaned actions'), 'admin/config/system/actions/orphan');
|
||||
$count = count($actions_in_db);
|
||||
$orphans = implode(', ', $orphaned);
|
||||
watchdog('action', '@count orphaned actions (%orphans) exist in the actions table. !link', array('@count' => $count, '%orphans' => $orphans, '!link' => $link), WATCHDOG_INFO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves an action and its user-supplied parameter values to the database.
|
||||
*
|
||||
* @param $function
|
||||
* The name of the function to be called when this action is performed.
|
||||
* @param $type
|
||||
* The type of action, to describe grouping and/or context, e.g., 'node',
|
||||
* 'user', 'comment', or 'system'.
|
||||
* @param $params
|
||||
* An associative array with parameter names as keys and parameter values as
|
||||
* values.
|
||||
* @param $label
|
||||
* A user-supplied label of this particular action, e.g., 'Send e-mail
|
||||
* to Jim'.
|
||||
* @param $aid
|
||||
* The ID of this action. If omitted, a new action is created.
|
||||
*
|
||||
* @return
|
||||
* The ID of the action.
|
||||
*/
|
||||
function action_save($function, $type, $params, $label, $aid = NULL) {
|
||||
// aid is the callback for singleton actions so we need to keep a separate
|
||||
// table for numeric aids.
|
||||
if (!$aid) {
|
||||
$aid = db_next_id();
|
||||
}
|
||||
|
||||
db_merge('actions')
|
||||
->key(array('aid' => $aid))
|
||||
->fields(array(
|
||||
'callback' => $function,
|
||||
'type' => $type,
|
||||
'parameters' => serialize($params),
|
||||
'label' => $label,
|
||||
))
|
||||
->execute();
|
||||
|
||||
watchdog('action', 'Action %action saved.', array('%action' => $label));
|
||||
return $aid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single action from the database.
|
||||
*
|
||||
* @param $aid
|
||||
* The ID of the action to retrieve.
|
||||
*
|
||||
* @return
|
||||
* The appropriate action row from the database as an object.
|
||||
*/
|
||||
function action_load($aid) {
|
||||
return db_query("SELECT aid, type, callback, parameters, label FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetchObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a single action from the database.
|
||||
*
|
||||
* @param $aid
|
||||
* The ID of the action to delete.
|
||||
*/
|
||||
function action_delete($aid) {
|
||||
db_delete('actions')
|
||||
->condition('aid', $aid)
|
||||
->execute();
|
||||
module_invoke_all('action_delete', $aid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_action_info().
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function action_action_info() {
|
||||
return array(
|
||||
'action_message_action' => array(
|
||||
'type' => 'system',
|
||||
'label' => t('Display a message to the user'),
|
||||
'configurable' => TRUE,
|
||||
'triggers' => array('any'),
|
||||
),
|
||||
'action_send_email_action' => array(
|
||||
'type' => 'system',
|
||||
'label' => t('Send e-mail'),
|
||||
'configurable' => TRUE,
|
||||
'triggers' => array('any'),
|
||||
),
|
||||
'action_goto_action' => array(
|
||||
'type' => 'system',
|
||||
'label' => t('Redirect to URL'),
|
||||
'configurable' => TRUE,
|
||||
'triggers' => array('any'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a form definition so the Send email action can be configured.
|
||||
*
|
||||
* @param $context
|
||||
* Default values (if we are editing an existing action instance).
|
||||
*
|
||||
* @return
|
||||
* Form definition.
|
||||
*
|
||||
* @see action_send_email_action_validate()
|
||||
* @see action_send_email_action_submit()
|
||||
*/
|
||||
function action_send_email_action_form($context) {
|
||||
// Set default values for form.
|
||||
if (!isset($context['recipient'])) {
|
||||
$context['recipient'] = '';
|
||||
}
|
||||
if (!isset($context['subject'])) {
|
||||
$context['subject'] = '';
|
||||
}
|
||||
if (!isset($context['message'])) {
|
||||
$context['message'] = '';
|
||||
}
|
||||
|
||||
$form['recipient'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Recipient'),
|
||||
'#default_value' => $context['recipient'],
|
||||
'#maxlength' => '254',
|
||||
'#description' => t('The e-mail address to which the message should be sent OR enter [node:author:mail], [comment:author:mail], etc. if you would like to send an e-mail to the author of the original post.'),
|
||||
);
|
||||
$form['subject'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Subject'),
|
||||
'#default_value' => $context['subject'],
|
||||
'#maxlength' => '254',
|
||||
'#description' => t('The subject of the message.'),
|
||||
);
|
||||
$form['message'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Message'),
|
||||
'#default_value' => $context['message'],
|
||||
'#cols' => '80',
|
||||
'#rows' => '20',
|
||||
'#description' => t('The message that should be sent. You may include placeholders like [node:title], [user:name], and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates action_send_email_action() form submissions.
|
||||
*/
|
||||
function action_send_email_action_validate($form, $form_state) {
|
||||
$form_values = $form_state['values'];
|
||||
// Validate the configuration form.
|
||||
if (!valid_email_address($form_values['recipient']) && strpos($form_values['recipient'], ':mail') === FALSE) {
|
||||
// We want the literal %author placeholder to be emphasized in the error message.
|
||||
form_set_error('recipient', t('Enter a valid email address or use a token e-mail address such as %author.', array('%author' => '[node:author:mail]')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes action_send_email_action() form submissions.
|
||||
*/
|
||||
function action_send_email_action_submit($form, $form_state) {
|
||||
$form_values = $form_state['values'];
|
||||
// Process the HTML form to store configuration. The keyed array that
|
||||
// we return will be serialized to the database.
|
||||
$params = array(
|
||||
'recipient' => $form_values['recipient'],
|
||||
'subject' => $form_values['subject'],
|
||||
'message' => $form_values['message'],
|
||||
);
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an e-mail message.
|
||||
*
|
||||
* @param object $entity
|
||||
* An optional node entity, which will be added as $context['node'] if
|
||||
* provided.
|
||||
* @param array $context
|
||||
* Array with the following elements:
|
||||
* - 'recipient': E-mail message recipient. This will be passed through
|
||||
* \Drupal\Core\Utility\Token::replace().
|
||||
* - 'subject': The subject of the message. This will be passed through
|
||||
* \Drupal\Core\Utility\Token::replace().
|
||||
* - 'message': The message to send. This will be passed through
|
||||
* \Drupal\Core\Utility\Token::replace().
|
||||
* - Other elements will be used as the data for token replacement.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function action_send_email_action($entity, $context) {
|
||||
if (empty($context['node'])) {
|
||||
$context['node'] = $entity;
|
||||
}
|
||||
|
||||
$recipient = Drupal::token()->replace($context['recipient'], $context);
|
||||
|
||||
// If the recipient is a registered user with a language preference, use
|
||||
// the recipient's preferred language. Otherwise, use the system default
|
||||
// language.
|
||||
$recipient_account = user_load_by_mail($recipient);
|
||||
if ($recipient_account) {
|
||||
$langcode = user_preferred_langcode($recipient_account);
|
||||
}
|
||||
else {
|
||||
$langcode = language_default()->langcode;
|
||||
}
|
||||
$params = array('context' => $context);
|
||||
|
||||
if (drupal_mail('system', 'action_send_email', $recipient, $langcode, $params)) {
|
||||
watchdog('action', 'Sent email to %recipient', array('%recipient' => $recipient));
|
||||
}
|
||||
else {
|
||||
watchdog('error', 'Unable to send email to %recipient', array('%recipient' => $recipient));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the settings form for action_message_action().
|
||||
*
|
||||
* @see action_message_action_submit()
|
||||
*/
|
||||
function action_message_action_form($context) {
|
||||
$form['message'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Message'),
|
||||
'#default_value' => isset($context['message']) ? $context['message'] : '',
|
||||
'#required' => TRUE,
|
||||
'#rows' => '8',
|
||||
'#description' => t('The message to be displayed to the current user. You may include placeholders like [node:title], [user:name], and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes action_message_action form submissions.
|
||||
*/
|
||||
function action_message_action_submit($form, $form_state) {
|
||||
return array('message' => $form_state['values']['message']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the current user's screen.
|
||||
*
|
||||
* @param object $entity
|
||||
* An optional node entity, which will be added as $context['node'] if
|
||||
* provided.
|
||||
* @param array $context
|
||||
* Array with the following elements:
|
||||
* - 'message': The message to send. This will be passed through
|
||||
* \Drupal\Core\Utility\Token::replace().
|
||||
* - Other elements will be used as the data for token replacement in
|
||||
* the message.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function action_message_action(&$entity, $context = array()) {
|
||||
if (empty($context['node'])) {
|
||||
$context['node'] = $entity;
|
||||
}
|
||||
|
||||
$context['message'] = Drupal::token()->replace(filter_xss_admin($context['message']), $context);
|
||||
drupal_set_message($context['message']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the settings form for action_goto_action().
|
||||
*
|
||||
* @see action_goto_action_submit()
|
||||
*/
|
||||
function action_goto_action_form($context) {
|
||||
$form['url'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('URL'),
|
||||
'#description' => t('The URL to which the user should be redirected. This can be an internal URL like node/1234 or an external URL like @url.', array('@url' => 'http://drupal.org')),
|
||||
'#default_value' => isset($context['url']) ? $context['url'] : '',
|
||||
'#required' => TRUE,
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes action_goto_action form submissions.
|
||||
*/
|
||||
function action_goto_action_submit($form, $form_state) {
|
||||
return array(
|
||||
'url' => $form_state['values']['url']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to a different URL.
|
||||
*
|
||||
* Action functions are declared by modules by implementing hook_action_info().
|
||||
* Modules can cause action functions to run by calling actions_do().
|
||||
*
|
||||
* @param object $entity
|
||||
* An optional node entity, which will be added as $context['node'] if
|
||||
* provided.
|
||||
* @param array $context
|
||||
* Array with the following elements:
|
||||
* - 'url': URL to redirect to. This will be passed through
|
||||
* \Drupal\Core\Utility\Token::replace().
|
||||
* - Other elements will be used as the data for token replacement.
|
||||
*
|
||||
* @ingroup actions.
|
||||
*/
|
||||
function action_goto_action($entity, $context) {
|
||||
drupal_goto(Drupal::token()->replace($context['url'], $context));
|
||||
function action_entity_info(&$entity_info) {
|
||||
$entity_info['action']['controllers']['form']['add'] = 'Drupal\action\ActionAddFormController';
|
||||
$entity_info['action']['controllers']['form']['edit'] = 'Drupal\action\ActionEditFormController';
|
||||
$entity_info['action']['controllers']['list'] = 'Drupal\action\ActionListController';
|
||||
}
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
action_admin:
|
||||
pattern: '/admin/config/system/actions'
|
||||
defaults:
|
||||
_content: '\Drupal\action\Controller\ActionController::adminManage'
|
||||
_content: '\Drupal\Core\Entity\Controller\EntityListController::listing'
|
||||
entity_type: 'action'
|
||||
requirements:
|
||||
_permission: 'administer actions'
|
||||
|
||||
action_admin_orphans_remove:
|
||||
pattern: '/admin/config/system/actions/orphan'
|
||||
action_admin_add:
|
||||
pattern: '/admin/config/system/actions/add/{action_id}'
|
||||
defaults:
|
||||
_content: '\Drupal\action\Controller\ActionController::adminRemoveOrphans'
|
||||
_entity_form: 'action.add'
|
||||
requirements:
|
||||
_permission: 'administer actions'
|
||||
|
||||
action_admin_configure:
|
||||
pattern: '/admin/config/system/actions/configure/{action}'
|
||||
defaults:
|
||||
_form: '\Drupal\action\Form\ActionAdminConfigureForm'
|
||||
_entity_form: 'action.edit'
|
||||
requirements:
|
||||
_permission: 'administer actions'
|
||||
|
||||
action_delete:
|
||||
pattern: 'admin/config/system/actions/delete/{action}'
|
||||
pattern: 'admin/config/system/actions/configure/{action}/delete'
|
||||
defaults:
|
||||
_form: '\Drupal\action\Form\DeleteForm'
|
||||
requirements:
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\action\ActionAddFormController.
|
||||
*/
|
||||
|
||||
namespace Drupal\action;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Action\ActionManager;
|
||||
use Drupal\Core\Entity\EntityControllerInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form controller for action add forms.
|
||||
*/
|
||||
class ActionAddFormController extends ActionFormControllerBase implements EntityControllerInterface {
|
||||
|
||||
/**
|
||||
* The action manager.
|
||||
*
|
||||
* @var \Drupal\Core\Action\ActionManager
|
||||
*/
|
||||
protected $actionManager;
|
||||
|
||||
/**
|
||||
* Constructs a new ActionAddFormController.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
|
||||
* The action storage controller.
|
||||
* @param \Drupal\Core\Action\ActionManager $action_manager
|
||||
* The action plugin manager.
|
||||
*/
|
||||
public function __construct(EntityStorageControllerInterface $storage_controller, ActionManager $action_manager) {
|
||||
parent::__construct($storage_controller);
|
||||
|
||||
$this->actionManager = $action_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.entity')->getStorageController($entity_type),
|
||||
$container->get('plugin.manager.action')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string $action_id
|
||||
* The hashed version of the action ID.
|
||||
*/
|
||||
public function buildForm(array $form, array &$form_state, $action_id = NULL) {
|
||||
// In \Drupal\action\Form\ActionAdminManageForm::buildForm() the action
|
||||
// are hashed. Here we have to decrypt it to find the desired action ID.
|
||||
foreach ($this->actionManager->getDefinitions() as $id => $definition) {
|
||||
$key = Crypt::hashBase64($id);
|
||||
if ($key === $action_id) {
|
||||
$this->entity->setPlugin($id);
|
||||
// Derive the label and type from the action definition.
|
||||
$this->entity->set('label', $definition['label']);
|
||||
$this->entity->set('type', $definition['type']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\action\ActionEditFormController.
|
||||
*/
|
||||
|
||||
namespace Drupal\action;
|
||||
|
||||
/**
|
||||
* Provides a form controller for action edit forms.
|
||||
*/
|
||||
class ActionEditFormController extends ActionFormControllerBase {
|
||||
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\action\ActionEditFormController.
|
||||
*/
|
||||
|
||||
namespace Drupal\action;
|
||||
|
||||
use Drupal\Core\Entity\EntityControllerInterface;
|
||||
use Drupal\Core\Entity\EntityFormController;
|
||||
use Drupal\Core\Action\ConfigurableActionInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a base form controller for action forms.
|
||||
*/
|
||||
abstract class ActionFormControllerBase extends EntityFormController implements EntityControllerInterface {
|
||||
|
||||
/**
|
||||
* The action plugin being configured.
|
||||
*
|
||||
* @var \Drupal\Core\Action\ActionInterface
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* The action storage controller.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
|
||||
*/
|
||||
protected $storageController;
|
||||
|
||||
/**
|
||||
* Constructs a new action form.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
|
||||
* The action storage controller.
|
||||
*/
|
||||
public function __construct(EntityStorageControllerInterface $storage_controller) {
|
||||
parent::__construct();
|
||||
|
||||
$this->storageController = $storage_controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.entity')->getStorageController($entity_type)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, array &$form_state) {
|
||||
$this->plugin = $this->entity->getPlugin();
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, array &$form_state) {
|
||||
$form['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Label'),
|
||||
'#default_value' => $this->entity->label(),
|
||||
'#maxlength' => '255',
|
||||
'#description' => t('A unique label for this advanced action. This label will be displayed in the interface of modules that integrate with actions.'),
|
||||
);
|
||||
|
||||
$form['id'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#title' => t('Machine name'),
|
||||
'#default_value' => $this->entity->id(),
|
||||
'#disabled' => !$this->entity->isNew(),
|
||||
'#maxlength' => 64,
|
||||
'#description' => t('A unique name for this action. It must only contain lowercase letters, numbers and underscores.'),
|
||||
'#machine_name' => array(
|
||||
'exists' => array($this, 'exists'),
|
||||
),
|
||||
);
|
||||
$form['plugin'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $this->entity->get('plugin'),
|
||||
);
|
||||
$form['type'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $this->entity->getType(),
|
||||
);
|
||||
|
||||
if ($this->plugin instanceof ConfigurableActionInterface) {
|
||||
$form += $this->plugin->form($form, $form_state);
|
||||
}
|
||||
|
||||
return parent::form($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the action already exists.
|
||||
*
|
||||
* @param string $id
|
||||
* The action ID
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the action exists, FALSE otherwise.
|
||||
*/
|
||||
public function exists($id) {
|
||||
$actions = $this->storageController->load(array($id));
|
||||
return isset($actions[$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, array &$form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
unset($actions['delete']);
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate(array $form, array &$form_state) {
|
||||
parent::validate($form, $form_state);
|
||||
|
||||
if ($this->plugin instanceof ConfigurableActionInterface) {
|
||||
$this->plugin->validate($form, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submit(array $form, array &$form_state) {
|
||||
parent::submit($form, $form_state);
|
||||
|
||||
if ($this->plugin instanceof ConfigurableActionInterface) {
|
||||
$this->plugin->submit($form, $form_state);
|
||||
}
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, array &$form_state) {
|
||||
$this->entity->save();
|
||||
drupal_set_message(t('The action has been successfully saved.'));
|
||||
|
||||
$form_state['redirect'] = 'admin/config/system/actions';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\action\ActionListController.
|
||||
*/
|
||||
|
||||
namespace Drupal\action;
|
||||
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Core\Action\ActionManager;
|
||||
use Drupal\Core\Entity\EntityControllerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListController;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\action\Form\ActionAdminManageForm;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a listing of Actions.
|
||||
*/
|
||||
class ActionListController extends ConfigEntityListController implements EntityControllerInterface {
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasConfigurableActions = FALSE;
|
||||
|
||||
/**
|
||||
* The action plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Action\ActionManager
|
||||
*/
|
||||
protected $actionManager;
|
||||
|
||||
/**
|
||||
* Constructs a new ActionListController object.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
|
||||
* The action storage controller.
|
||||
* @param \Drupal\Core\Action\ActionManager $action_manager
|
||||
* The action plugin manager.
|
||||
*/
|
||||
public function __construct($entity_type, EntityStorageControllerInterface $storage, ActionManager $action_manager) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
|
||||
$this->actionManager = $action_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('plugin.manager.entity')->getStorageController($entity_type),
|
||||
$container->get('plugin.manager.action')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load() {
|
||||
$entities = parent::load();
|
||||
foreach ($entities as $entity) {
|
||||
if ($entity->isConfigurable()) {
|
||||
$this->hasConfigurableActions = TRUE;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$row['type'] = $entity->getType();
|
||||
$row['label'] = String::checkPlain($entity->label());
|
||||
if ($this->hasConfigurableActions) {
|
||||
$row['operations']['data'] = $this->buildOperations($entity);
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header = array(
|
||||
'type' => t('Action type'),
|
||||
'label' => t('Label'),
|
||||
'operations' => t('Operations'),
|
||||
);
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOperations(EntityInterface $entity) {
|
||||
$operations = array();
|
||||
if ($entity->isConfigurable()) {
|
||||
$uri = $entity->uri();
|
||||
$operations['edit'] = array(
|
||||
'title' => t('Configure'),
|
||||
'href' => $uri['path'],
|
||||
'options' => $uri['options'],
|
||||
'weight' => 10,
|
||||
);
|
||||
$operations['delete'] = array(
|
||||
'title' => t('Delete'),
|
||||
'href' => $uri['path'] . '/delete',
|
||||
'options' => $uri['options'],
|
||||
'weight' => 100,
|
||||
);
|
||||
}
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
$build['action_header']['#markup'] = '<h3>' . t('Available actions:') . '</h3>';
|
||||
$build['action_table'] = parent::render();
|
||||
if (!$this->hasConfigurableActions) {
|
||||
unset($build['action_table']['#header']['operations']);
|
||||
}
|
||||
$build['action_admin_manage_form'] = drupal_get_form(new ActionAdminManageForm($this->actionManager));
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\action\Controller\ActionController.
|
||||
*/
|
||||
|
||||
namespace Drupal\action\Controller;
|
||||
|
||||
use Drupal\action\Form\ActionAdminManageForm;
|
||||
use Drupal\Core\ControllerInterface;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
/**
|
||||
* Controller providing page callbacks for the action admin interface.
|
||||
*/
|
||||
class ActionController implements ControllerInterface {
|
||||
|
||||
/**
|
||||
* The database connection object for this controller.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* Constructs a new ActionController.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* The database connection object to be used by this controller.
|
||||
*/
|
||||
public function __construct(Connection $database) {
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\ControllerInterface::create().
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('database'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an overview of available and configured actions.
|
||||
*
|
||||
* @return
|
||||
* A render array containing a table of existing actions and the advanced
|
||||
* action creation form.
|
||||
*/
|
||||
public function adminManage() {
|
||||
action_synchronize();
|
||||
$actions = action_list();
|
||||
$actions_map = action_actions_map($actions);
|
||||
$options = array();
|
||||
$unconfigurable = array();
|
||||
|
||||
foreach ($actions_map as $key => $array) {
|
||||
if ($array['configurable']) {
|
||||
$options[$key] = $array['label'] . '...';
|
||||
}
|
||||
else {
|
||||
$unconfigurable[] = $array;
|
||||
}
|
||||
}
|
||||
|
||||
$row = array();
|
||||
$instances_present = $this->database->query("SELECT aid FROM {actions} WHERE parameters <> ''")->fetchField();
|
||||
$header = array(
|
||||
array('data' => t('Action type'), 'field' => 'type'),
|
||||
array('data' => t('Label'), 'field' => 'label'),
|
||||
$instances_present ? t('Operations') : '',
|
||||
);
|
||||
$query = $this->database->select('actions')
|
||||
->extend('Drupal\Core\Database\Query\PagerSelectExtender')
|
||||
->extend('Drupal\Core\Database\Query\TableSortExtender');
|
||||
$result = $query
|
||||
->fields('actions')
|
||||
->limit(50)
|
||||
->orderByHeader($header)
|
||||
->execute();
|
||||
|
||||
foreach ($result as $action) {
|
||||
$row = array();
|
||||
$row[] = $action->type;
|
||||
$row[] = check_plain($action->label);
|
||||
$links = array();
|
||||
if ($action->parameters) {
|
||||
$links['configure'] = array(
|
||||
'title' => t('configure'),
|
||||
'href' => "admin/config/system/actions/configure/$action->aid",
|
||||
);
|
||||
$links['delete'] = array(
|
||||
'title' => t('delete'),
|
||||
'href' => "admin/config/system/actions/delete/$action->aid",
|
||||
);
|
||||
}
|
||||
$row[] = array(
|
||||
'data' => array(
|
||||
'#type' => 'operations',
|
||||
'#links' => $links,
|
||||
),
|
||||
);
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
if ($rows) {
|
||||
$pager = theme('pager');
|
||||
if (!empty($pager)) {
|
||||
$rows[] = array(array('data' => $pager, 'colspan' => '3'));
|
||||
}
|
||||
$build['action_header'] = array(
|
||||
'#markup' => '<h3>' . t('Available actions:') . '</h3>'
|
||||
);
|
||||
$build['action_table'] = array(
|
||||
'#theme' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
);
|
||||
}
|
||||
|
||||
if ($actions_map) {
|
||||
$build['action_admin_manage_form'] = drupal_get_form(new ActionAdminManageForm(), $options);
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes actions that are in the database but not supported by any enabled module.
|
||||
*/
|
||||
public function adminRemoveOrphans() {
|
||||
action_synchronize(TRUE);
|
||||
return new RedirectResponse(url('admin/config/system/actions', array('absolute' => TRUE)));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\action\Form\ActionAdminConfigureForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\action\Form;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
|
||||
/**
|
||||
* Provides a form for configuring an action.
|
||||
*/
|
||||
class ActionAdminConfigureForm implements FormInterface {
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::getFormID().
|
||||
*/
|
||||
public function getFormID() {
|
||||
return 'action_admin_configure';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::buildForm().
|
||||
*/
|
||||
public function buildForm(array $form, array &$form_state, $action = NULL) {
|
||||
if ($action === NULL) {
|
||||
drupal_goto('admin/config/system/actions');
|
||||
}
|
||||
|
||||
$actions_map = action_actions_map(action_list());
|
||||
$edit = array();
|
||||
|
||||
// Numeric action denotes saved instance of a configurable action.
|
||||
if (is_numeric($action)) {
|
||||
$aid = $action;
|
||||
// Load stored parameter values from database.
|
||||
$data = db_query("SELECT * FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetch();
|
||||
$edit['action_label'] = $data->label;
|
||||
$edit['action_type'] = $data->type;
|
||||
$function = $data->callback;
|
||||
$action = Crypt::hashBase64($data->callback);
|
||||
$params = unserialize($data->parameters);
|
||||
if ($params) {
|
||||
foreach ($params as $name => $val) {
|
||||
$edit[$name] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise, we are creating a new action instance.
|
||||
else {
|
||||
$function = $actions_map[$action]['callback'];
|
||||
$edit['action_label'] = $actions_map[$action]['label'];
|
||||
$edit['action_type'] = $actions_map[$action]['type'];
|
||||
}
|
||||
|
||||
$form['action_label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Label'),
|
||||
'#default_value' => $edit['action_label'],
|
||||
'#maxlength' => '255',
|
||||
'#description' => t('A unique label for this advanced action. This label will be displayed in the interface of modules that integrate with actions.'),
|
||||
'#weight' => -10,
|
||||
);
|
||||
$action_form = $function . '_form';
|
||||
$form = array_merge($form, $action_form($edit));
|
||||
$form['action_type'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $edit['action_type'],
|
||||
);
|
||||
$form['action_action'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => $action,
|
||||
);
|
||||
// $aid is set when configuring an existing action instance.
|
||||
if (isset($aid)) {
|
||||
$form['action_aid'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => $aid,
|
||||
);
|
||||
}
|
||||
$form['action_configured'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => '1',
|
||||
);
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save'),
|
||||
'#weight' => 13,
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::validateForm().
|
||||
*/
|
||||
public function validateForm(array &$form, array &$form_state) {
|
||||
$function = action_function_lookup($form_state['values']['action_action']) . '_validate';
|
||||
// Hand off validation to the action.
|
||||
if (function_exists($function)) {
|
||||
$function($form, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::submitForm().
|
||||
*/
|
||||
public function submitForm(array &$form, array &$form_state) {
|
||||
$function = action_function_lookup($form_state['values']['action_action']);
|
||||
$submit_function = $function . '_submit';
|
||||
|
||||
// Action will return keyed array of values to store.
|
||||
$params = $submit_function($form, $form_state);
|
||||
$aid = isset($form_state['values']['action_aid']) ? $form_state['values']['action_aid'] : NULL;
|
||||
|
||||
action_save($function, $form_state['values']['action_type'], $params, $form_state['values']['action_label'], $aid);
|
||||
drupal_set_message(t('The action has been successfully saved.'));
|
||||
|
||||
$form_state['redirect'] = 'admin/config/system/actions';
|
||||
}
|
||||
|
||||
}
|
|
@ -7,27 +7,61 @@
|
|||
|
||||
namespace Drupal\action\Form;
|
||||
|
||||
use Drupal\Core\Controller\ControllerInterface;
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Action\ActionManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a configuration form for configurable actions.
|
||||
*/
|
||||
class ActionAdminManageForm implements FormInterface {
|
||||
class ActionAdminManageForm implements FormInterface, ControllerInterface {
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::getFormID().
|
||||
* The action plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Action\ActionManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Constructs a new ActionAdminManageForm.
|
||||
*
|
||||
* @param \Drupal\Core\Action\ActionManager $manager
|
||||
* The action plugin manager.
|
||||
*/
|
||||
public function __construct(ActionManager $manager) {
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.action')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormID() {
|
||||
return 'action_admin_manage';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::buildForm().
|
||||
*
|
||||
* @param array $options
|
||||
* An array of configurable actions.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, array &$form_state, array $options = array()) {
|
||||
public function buildForm(array $form, array &$form_state) {
|
||||
$actions = array();
|
||||
foreach ($this->manager->getDefinitions() as $id => $definition) {
|
||||
if (is_subclass_of($definition['class'], '\Drupal\Core\Action\ConfigurableActionInterface')) {
|
||||
$key = Crypt::hashBase64($id);
|
||||
$actions[$key] = $definition['label'] . '...';
|
||||
}
|
||||
}
|
||||
$form['parent'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Create an advanced action'),
|
||||
|
@ -37,7 +71,7 @@ class ActionAdminManageForm implements FormInterface {
|
|||
'#type' => 'select',
|
||||
'#title' => t('Action'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $options,
|
||||
'#options' => $actions,
|
||||
'#empty_option' => t('Choose an advanced action'),
|
||||
);
|
||||
$form['parent']['actions'] = array(
|
||||
|
@ -51,17 +85,17 @@ class ActionAdminManageForm implements FormInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::validateForm().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, array &$form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::submitForm().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, array &$form_state) {
|
||||
if ($form_state['values']['action']) {
|
||||
$form_state['redirect'] = 'admin/config/system/actions/configure/' . $form_state['values']['action'];
|
||||
$form_state['redirect'] = 'admin/config/system/actions/add/' . $form_state['values']['action'];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\action\Form;
|
||||
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\system\ActionConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Builds a form to delete an action.
|
||||
|
@ -17,7 +18,7 @@ class DeleteForm extends ConfirmFormBase {
|
|||
/**
|
||||
* The action to be deleted.
|
||||
*
|
||||
* @var \stdClass
|
||||
* @var \Drupal\system\ActionConfigEntityInterface
|
||||
*/
|
||||
protected $action;
|
||||
|
||||
|
@ -25,7 +26,7 @@ class DeleteForm extends ConfirmFormBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getQuestion() {
|
||||
return t('Are you sure you want to delete the action %action?', array('%action' => $this->action->label));
|
||||
return t('Are you sure you want to delete the action %action?', array('%action' => $this->action->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -40,7 +41,7 @@ class DeleteForm extends ConfirmFormBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCancelPath() {
|
||||
return 'admin/config/system/actions/manage';
|
||||
return 'admin/config/system/actions';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,9 +54,8 @@ class DeleteForm extends ConfirmFormBase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, array &$form_state, $action = NULL) {
|
||||
|
||||
$this->action = action_load($action);
|
||||
public function buildForm(array $form, array &$form_state, ActionConfigEntityInterface $action = NULL) {
|
||||
$this->action = $action;
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
@ -64,13 +64,12 @@ class DeleteForm extends ConfirmFormBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, array &$form_state) {
|
||||
$this->action->delete();
|
||||
|
||||
action_delete($this->action->aid);
|
||||
watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $this->action->id(), '%action' => $this->action->label()));
|
||||
drupal_set_message(t('Action %action was deleted', array('%action' => $this->action->label())));
|
||||
|
||||
watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $this->action->aid, '%action' => $this->action->label));
|
||||
drupal_set_message(t('Action %action was deleted', array('%action' => $this->action->label)));
|
||||
|
||||
$form_state['redirect'] = 'admin/config/system/actions/manage';
|
||||
$form_state['redirect'] = 'admin/config/system/actions';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\action\Plugin\Action\EmailAction.
|
||||
*/
|
||||
|
||||
namespace Drupal\action\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ConfigurableActionBase;
|
||||
use Drupal\Core\Entity\EntityManager;
|
||||
use Drupal\Core\Utility\Token;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Sends an e-mail message.
|
||||
*
|
||||
* @Action(
|
||||
* id = "action_send_email_action",
|
||||
* label = @Translation("Send e-mail"),
|
||||
* type = "system"
|
||||
* )
|
||||
*/
|
||||
class EmailAction extends ConfigurableActionBase {
|
||||
|
||||
/**
|
||||
* The token service.
|
||||
*
|
||||
* @var \Drupal\Core\Utility\Token
|
||||
*/
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* The user storage controller.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
|
||||
*/
|
||||
protected $storageController;
|
||||
|
||||
/**
|
||||
* Constructs a EmailAction object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param array $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Utility\Token $token
|
||||
* The token service.
|
||||
* @param \Drupal\Core\Entity\EntityManager $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, array $plugin_definition, Token $token, EntityManager $entity_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->token = $token;
|
||||
$this->storageController = $entity_manager->getStorageController('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
|
||||
return new static($configuration, $plugin_id, $plugin_definition,
|
||||
$container->get('token'),
|
||||
$container->get('plugin.manager.entity')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
if (empty($this->configuration['node'])) {
|
||||
$this->configuration['node'] = $entity;
|
||||
}
|
||||
|
||||
$recipient = $this->token->replace($this->configuration['recipient'], $this->configuration);
|
||||
|
||||
// If the recipient is a registered user with a language preference, use
|
||||
// the recipient's preferred language. Otherwise, use the system default
|
||||
// language.
|
||||
$recipient_accounts = $this->storageController->loadByProperties(array('mail' => $recipient));
|
||||
$recipient_account = reset($recipient_accounts);
|
||||
if ($recipient_account) {
|
||||
$langcode = user_preferred_langcode($recipient_account);
|
||||
}
|
||||
else {
|
||||
$langcode = language_default()->langcode;
|
||||
}
|
||||
$params = array('context' => $this->configuration);
|
||||
|
||||
if (drupal_mail('system', 'action_send_email', $recipient, $langcode, $params)) {
|
||||
watchdog('action', 'Sent email to %recipient', array('%recipient' => $recipient));
|
||||
}
|
||||
else {
|
||||
watchdog('error', 'Unable to send email to %recipient', array('%recipient' => $recipient));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultConfiguration() {
|
||||
return array(
|
||||
'recipient' => '',
|
||||
'subject' => '',
|
||||
'message' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, array &$form_state) {
|
||||
$form['recipient'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Recipient'),
|
||||
'#default_value' => $this->configuration['recipient'],
|
||||
'#maxlength' => '254',
|
||||
'#description' => t('The e-mail address to which the message should be sent OR enter [node:author:mail], [comment:author:mail], etc. if you would like to send an e-mail to the author of the original post.'),
|
||||
);
|
||||
$form['subject'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Subject'),
|
||||
'#default_value' => $this->configuration['subject'],
|
||||
'#maxlength' => '254',
|
||||
'#description' => t('The subject of the message.'),
|
||||
);
|
||||
$form['message'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Message'),
|
||||
'#default_value' => $this->configuration['message'],
|
||||
'#cols' => '80',
|
||||
'#rows' => '20',
|
||||
'#description' => t('The message that should be sent. You may include placeholders like [node:title], [user:name], and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate(array &$form, array &$form_state) {
|
||||
if (!valid_email_address($form_state['values']['recipient']) && strpos($form_state['values']['recipient'], ':mail') === FALSE) {
|
||||
// We want the literal %author placeholder to be emphasized in the error message.
|
||||
form_set_error('recipient', t('Enter a valid email address or use a token e-mail address such as %author.', array('%author' => '[node:author:mail]')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submit(array &$form, array &$form_state) {
|
||||
$this->configuration['recipient'] = $form_state['values']['recipient'];
|
||||
$this->configuration['subject'] = $form_state['values']['subject'];
|
||||
$this->configuration['message'] = $form_state['values']['message'];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\action\Plugin\Action\GotoAction.
|
||||
*/
|
||||
|
||||
namespace Drupal\action\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ConfigurableActionBase;
|
||||
|
||||
/**
|
||||
* Redirects to a different URL.
|
||||
*
|
||||
* @Action(
|
||||
* id = "action_goto_action",
|
||||
* label = @Translation("Redirect to URL"),
|
||||
* type = "system"
|
||||
* )
|
||||
*/
|
||||
class GotoAction extends ConfigurableActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($object = NULL) {
|
||||
drupal_goto($this->configuration['url']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultConfiguration() {
|
||||
return array(
|
||||
'url' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, array &$form_state) {
|
||||
$form['url'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('URL'),
|
||||
'#description' => t('The URL to which the user should be redirected. This can be an internal URL like node/1234 or an external URL like @url.', array('@url' => 'http://drupal.org')),
|
||||
'#default_value' => $this->configuration['url'],
|
||||
'#required' => TRUE,
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submit(array &$form, array &$form_state) {
|
||||
$this->configuration['url'] = $form_state['values']['url'];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\action\Plugin\Action\MessageAction.
|
||||
*/
|
||||
|
||||
namespace Drupal\action\Plugin\Action;
|
||||
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ConfigurableActionBase;
|
||||
use Drupal\Core\Utility\Token;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Sends a message to the current user's screen.
|
||||
*
|
||||
* @Action(
|
||||
* id = "action_message_action",
|
||||
* label = @Translation("Display a message to the user"),
|
||||
* type = "system"
|
||||
* )
|
||||
*/
|
||||
class MessageAction extends ConfigurableActionBase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Utility\Token
|
||||
*/
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* Constructs a MessageAction object.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, array $plugin_definition, Token $token) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
|
||||
return new static($configuration, $plugin_id, $plugin_definition, $container->get('token'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
if (empty($this->configuration['node'])) {
|
||||
$this->configuration['node'] = $entity;
|
||||
}
|
||||
$message = $this->token->replace(Xss::filterAdmin($this->configuration['message']), $this->configuration);
|
||||
drupal_set_message($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultConfiguration() {
|
||||
return array(
|
||||
'message' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, array &$form_state) {
|
||||
$form['message'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Message'),
|
||||
'#default_value' => $this->configuration['message'],
|
||||
'#required' => TRUE,
|
||||
'#rows' => '8',
|
||||
'#description' => t('The message to be displayed to the current user. You may include placeholders like [node:title], [user:name], and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submit(array &$form, array &$form_state) {
|
||||
$this->configuration['message'] = $form_state['values']['message'];
|
||||
unset($this->configuration['node']);
|
||||
}
|
||||
|
||||
}
|
|
@ -71,11 +71,10 @@ class BulkForm extends BulkFormBase {
|
|||
*/
|
||||
protected function getBulkOptions($filtered = TRUE) {
|
||||
// Get all available actions.
|
||||
$actions = action_get_all_actions();
|
||||
$entity_type = $this->getEntityType();
|
||||
$options = array();
|
||||
// Filter the action list.
|
||||
foreach ($actions as $id => $action) {
|
||||
foreach ($this->actions as $id => $action) {
|
||||
if ($filtered) {
|
||||
$in_selected = in_array($id, $this->options['selected_actions']);
|
||||
// If the field is configured to include only the selected actions,
|
||||
|
@ -90,8 +89,8 @@ class BulkForm extends BulkFormBase {
|
|||
}
|
||||
}
|
||||
// Only allow actions that are valid for this entity type.
|
||||
if (($action['type'] == $entity_type) && empty($action['configurable'])) {
|
||||
$options[$id] = $action['label'];
|
||||
if (($action->getType() == $entity_type)) {
|
||||
$options[$id] = $action->label();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,26 +101,13 @@ class BulkForm extends BulkFormBase {
|
|||
* Implements \Drupal\system\Plugin\views\field\BulkFormBase::views_form_submit().
|
||||
*/
|
||||
public function views_form_submit(&$form, &$form_state) {
|
||||
parent::views_form_submit($form, $form_state);
|
||||
if ($form_state['step'] == 'views_form_views_form') {
|
||||
$action = $form_state['values']['action'];
|
||||
$action = action_load($action);
|
||||
$count = 0;
|
||||
|
||||
// Filter only selected checkboxes.
|
||||
$selected = array_filter($form_state['values'][$this->options['id']]);
|
||||
|
||||
if (!empty($selected)) {
|
||||
foreach (array_keys($selected) as $row_index) {
|
||||
$entity = $this->get_entity($this->view->result[$row_index]);
|
||||
actions_do($action->aid, $entity);
|
||||
$entity->save();
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$count = count(array_filter($form_state['values'][$this->options['id']]));
|
||||
$action = $this->actions[$form_state['values']['action']];
|
||||
if ($count) {
|
||||
drupal_set_message(format_plural($count, '%action was applied to @count item.', '%action was applied to @count items.', array(
|
||||
'%action' => $action->label,
|
||||
'%action' => $action->label(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,43 +42,56 @@ class ConfigurationTest extends WebTestBase {
|
|||
$edit = array();
|
||||
$edit['action'] = Crypt::hashBase64('action_goto_action');
|
||||
$this->drupalPost('admin/config/system/actions', $edit, t('Create'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Make a POST request to the individual action configuration page.
|
||||
$edit = array();
|
||||
$action_label = $this->randomName();
|
||||
$edit['action_label'] = $action_label;
|
||||
$edit['label'] = $action_label;
|
||||
$edit['id'] = strtolower($action_label);
|
||||
$edit['url'] = 'admin';
|
||||
$this->drupalPost('admin/config/system/actions/configure/' . Crypt::hashBase64('action_goto_action'), $edit, t('Save'));
|
||||
$this->drupalPost('admin/config/system/actions/add/' . Crypt::hashBase64('action_goto_action'), $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Make sure that the new complex action was saved properly.
|
||||
$this->assertText(t('The action has been successfully saved.'), "Make sure we get a confirmation that we've successfully saved the complex action.");
|
||||
$this->assertText($action_label, "Make sure the action label appears on the configuration page after we've saved the complex action.");
|
||||
|
||||
// Make another POST request to the action edit page.
|
||||
$this->clickLink(t('configure'));
|
||||
preg_match('|admin/config/system/actions/configure/(\d+)|', $this->getUrl(), $matches);
|
||||
$this->clickLink(t('Configure'));
|
||||
preg_match('|admin/config/system/actions/configure/(.+)|', $this->getUrl(), $matches);
|
||||
$aid = $matches[1];
|
||||
$edit = array();
|
||||
$new_action_label = $this->randomName();
|
||||
$edit['action_label'] = $new_action_label;
|
||||
$edit['label'] = $new_action_label;
|
||||
$edit['url'] = 'admin';
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Make sure that the action updated properly.
|
||||
$this->assertText(t('The action has been successfully saved.'), "Make sure we get a confirmation that we've successfully updated the complex action.");
|
||||
$this->assertNoText($action_label, "Make sure the old action label does NOT appear on the configuration page after we've updated the complex action.");
|
||||
$this->assertText($new_action_label, "Make sure the action label appears on the configuration page after we've updated the complex action.");
|
||||
|
||||
$this->clickLink(t('Configure'));
|
||||
$element = $this->xpath('//input[@type="text" and @value="admin"]');
|
||||
$this->assertTrue(!empty($element), 'Make sure the URL appears when re-editing the action.');
|
||||
|
||||
// Make sure that deletions work properly.
|
||||
$this->clickLink(t('delete'));
|
||||
$this->drupalGet('admin/config/system/actions');
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertResponse(200);
|
||||
$edit = array();
|
||||
$this->drupalPost("admin/config/system/actions/delete/$aid", $edit, t('Delete'));
|
||||
$this->drupalPost("admin/config/system/actions/configure/$aid/delete", $edit, t('Delete'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Make sure that the action was actually deleted.
|
||||
$this->assertRaw(t('Action %action was deleted', array('%action' => $new_action_label)), 'Make sure that we get a delete confirmation message.');
|
||||
$this->drupalGet('admin/config/system/actions');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoText($new_action_label, "Make sure the action label does not appear on the overview page after we've deleted the action.");
|
||||
$exists = db_query('SELECT aid FROM {actions} WHERE callback = :callback', array(':callback' => 'drupal_goto_action'))->fetchField();
|
||||
$this->assertFalse($exists, 'Make sure the action is gone from the database after being deleted.');
|
||||
|
||||
$action = entity_load('action', $aid);
|
||||
$this->assertFalse($action, 'Make sure the action is gone after being deleted.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\action\Tests\LoopTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\action\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests aborting of actions executing in a potential loop.
|
||||
*/
|
||||
class LoopTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('dblog', 'action_loop_test');
|
||||
|
||||
protected $aid;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Actions executing in a potentially infinite loop',
|
||||
'description' => 'Tests actions executing in a loop, and makes sure they abort properly.',
|
||||
'group' => 'Action',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a loop with 3 - 12 recursions, and sees if it aborts properly.
|
||||
*/
|
||||
function testActionLoop() {
|
||||
$user = $this->drupalCreateUser(array('administer actions'));
|
||||
$this->drupalLogin($user);
|
||||
|
||||
$info = action_loop_test_action_info();
|
||||
$this->aid = action_save('action_loop_test_log', $info['action_loop_test_log']['type'], array(), $info['action_loop_test_log']['label']);
|
||||
|
||||
// Delete any existing watchdog messages to clear the plethora of
|
||||
// "Action added" messages from when Drupal was installed.
|
||||
db_delete('watchdog')->execute();
|
||||
// To prevent this test from failing when xdebug is enabled, the maximum
|
||||
// recursion level should be kept low enough to prevent the xdebug
|
||||
// infinite recursion protection mechanism from aborting the request.
|
||||
// See http://drupal.org/node/587634.
|
||||
config('action.settings')
|
||||
->set('recursion_limit', 7)
|
||||
->save();
|
||||
$this->triggerActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops watchdog messages up to actions_max_stack times.
|
||||
*
|
||||
* Creates an infinite loop by causing a watchdog message to be set,
|
||||
* which causes the actions to be triggered again, up to action_max_stack
|
||||
* times.
|
||||
*/
|
||||
protected function triggerActions() {
|
||||
$this->drupalGet('<front>', array('query' => array('trigger_action_on_watchdog' => $this->aid)));
|
||||
$expected = array();
|
||||
$expected[] = 'Triggering action loop';
|
||||
$recursion_limit = config('action.settings')->get('recursion_limit');
|
||||
for ($i = 1; $i <= $recursion_limit; $i++) {
|
||||
$expected[] = "Test log #$i";
|
||||
}
|
||||
$expected[] = 'Stack overflow: recursion limit for actions_do() has been reached. Stack is limited by %limit calls.';
|
||||
|
||||
$result = db_query("SELECT message FROM {watchdog} WHERE type = 'action_loop_test' OR type = 'action' ORDER BY wid");
|
||||
$loop_started = FALSE;
|
||||
foreach ($result as $row) {
|
||||
$expected_message = array_shift($expected);
|
||||
$this->assertEqual($row->message, $expected_message, format_string('Expected message %expected, got %message.', array('%expected' => $expected_message, '%message' => $row->message)));
|
||||
}
|
||||
$this->assertTrue(empty($expected), 'All expected messages found.');
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
name: 'Action loop test'
|
||||
type: module
|
||||
description: 'Support module for action loop testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
hidden: true
|
||||
dependencies:
|
||||
- action
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function action_loop_test_install() {
|
||||
module_set_weight('action_loop_test', 1);
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_watchdog().
|
||||
*/
|
||||
function action_loop_test_watchdog(array $log_entry) {
|
||||
// If the triggering actions are not explicitly enabled, abort.
|
||||
if (empty($_GET['trigger_action_on_watchdog'])) {
|
||||
return;
|
||||
}
|
||||
// We can pass in any applicable information in $context. There isn't much in
|
||||
// this case, but we'll pass in the hook name as the bare minimum.
|
||||
$context = array(
|
||||
'hook' => 'watchdog',
|
||||
);
|
||||
// Fire the actions on the associated object ($log_entry) and the context
|
||||
// variable.
|
||||
$aids = (array) $_GET['trigger_action_on_watchdog'];
|
||||
actions_do($aids, $log_entry, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_custom_theme().
|
||||
*
|
||||
* We need to check wheter a loop should be triggered and we do this as early
|
||||
* possible, in the first hook that fires.
|
||||
*/
|
||||
function action_loop_test_custom_theme() {
|
||||
if (!empty($_GET['trigger_action_on_watchdog'])) {
|
||||
watchdog_skip_semaphore('action_loop_test', 'Triggering action loop');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_action_info().
|
||||
*/
|
||||
function action_loop_test_action_info() {
|
||||
return array(
|
||||
'action_loop_test_log' => array(
|
||||
'label' => t('Write a message to the log.'),
|
||||
'type' => 'system',
|
||||
'configurable' => FALSE,
|
||||
'triggers' => array('any'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a message to the log.
|
||||
*/
|
||||
function action_loop_test_log() {
|
||||
$count = &drupal_static(__FUNCTION__, 0);
|
||||
$count++;
|
||||
watchdog_skip_semaphore('action_loop_test', "Test log #$count");
|
||||
}
|
||||
|
||||
/**
|
||||
* Replacement of the watchdog() function that eliminates the use of semaphores
|
||||
* so that we can test the abortion of an action loop.
|
||||
*/
|
||||
function watchdog_skip_semaphore($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
|
||||
global $user, $base_root;
|
||||
|
||||
// Prepare the fields to be logged
|
||||
$log_entry = array(
|
||||
'type' => $type,
|
||||
'message' => $message,
|
||||
'variables' => $variables,
|
||||
'severity' => $severity,
|
||||
'link' => $link,
|
||||
'user' => $user,
|
||||
'uid' => isset($user->uid) ? $user->uid : 0,
|
||||
'request_uri' => $base_root . request_uri(),
|
||||
'referer' => $_SERVER['HTTP_REFERER'],
|
||||
'ip' => Drupal::request()->getClientIP(),
|
||||
'timestamp' => REQUEST_TIME,
|
||||
);
|
||||
|
||||
// Call the logging hooks to log/process the message
|
||||
foreach (module_implements('watchdog') as $module) {
|
||||
module_invoke($module, 'watchdog', $log_entry);
|
||||
}
|
||||
}
|
|
@ -59,7 +59,7 @@ function comment_admin_overview($form, &$form_state, $arg) {
|
|||
|
||||
$form['options']['operation'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Operation'),
|
||||
'#title' => t('Action'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $options,
|
||||
'#default_value' => 'publish',
|
||||
|
|
|
@ -1805,161 +1805,6 @@ function comment_alphadecimal_to_int($c = '00') {
|
|||
return base_convert(substr($c, 1), 36, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_action_info().
|
||||
*/
|
||||
function comment_action_info() {
|
||||
return array(
|
||||
'comment_publish_action' => array(
|
||||
'label' => t('Publish comment'),
|
||||
'type' => 'comment',
|
||||
'configurable' => FALSE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('comment_presave', 'comment_insert', 'comment_update'),
|
||||
),
|
||||
'comment_unpublish_action' => array(
|
||||
'label' => t('Unpublish comment'),
|
||||
'type' => 'comment',
|
||||
'configurable' => FALSE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('comment_presave', 'comment_insert', 'comment_update'),
|
||||
),
|
||||
'comment_unpublish_by_keyword_action' => array(
|
||||
'label' => t('Unpublish comment containing keyword(s)'),
|
||||
'type' => 'comment',
|
||||
'configurable' => TRUE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('comment_presave', 'comment_insert', 'comment_update'),
|
||||
),
|
||||
'comment_save_action' => array(
|
||||
'label' => t('Save comment'),
|
||||
'type' => 'comment',
|
||||
'configurable' => FALSE,
|
||||
'triggers' => array('comment_insert', 'comment_update'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes a comment.
|
||||
*
|
||||
* @param Drupal\comment\Comment $comment
|
||||
* (optional) A comment object to publish.
|
||||
* @param array $context
|
||||
* Array with components:
|
||||
* - 'cid': Comment ID. Required if $comment is not given.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function comment_publish_action(Comment $comment = NULL, $context = array()) {
|
||||
if (isset($comment->subject->value)) {
|
||||
$subject = $comment->subject->value;
|
||||
$comment->status->value = COMMENT_PUBLISHED;
|
||||
}
|
||||
else {
|
||||
$cid = $context['cid'];
|
||||
$subject = db_query('SELECT subject FROM {comment} WHERE cid = :cid', array(':cid' => $cid))->fetchField();
|
||||
db_update('comment')
|
||||
->fields(array('status' => COMMENT_PUBLISHED))
|
||||
->condition('cid', $cid)
|
||||
->execute();
|
||||
}
|
||||
watchdog('action', 'Published comment %subject.', array('%subject' => $subject));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpublishes a comment.
|
||||
*
|
||||
* @param Drupal\comment\Comment|null $comment
|
||||
* (optional) A comment object to unpublish.
|
||||
* @param array $context
|
||||
* Array with components:
|
||||
* - 'cid': Comment ID. Required if $comment is not given.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function comment_unpublish_action(Comment $comment = NULL, $context = array()) {
|
||||
if (isset($comment->subject->value)) {
|
||||
$subject = $comment->subject->value;
|
||||
$comment->status->value = COMMENT_NOT_PUBLISHED;
|
||||
}
|
||||
else {
|
||||
$cid = $context['cid'];
|
||||
$subject = db_query('SELECT subject FROM {comment} WHERE cid = :cid', array(':cid' => $cid))->fetchField();
|
||||
db_update('comment')
|
||||
->fields(array('status' => COMMENT_NOT_PUBLISHED))
|
||||
->condition('cid', $cid)
|
||||
->execute();
|
||||
}
|
||||
watchdog('action', 'Unpublished comment %subject.', array('%subject' => $subject));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpublishes a comment if it contains certain keywords.
|
||||
*
|
||||
* @param Drupal\comment\Comment $comment
|
||||
* Comment object to modify.
|
||||
* @param array $context
|
||||
* Array with components:
|
||||
* - 'keywords': Keywords to look for. If the comment contains at least one
|
||||
* of the keywords, it is unpublished.
|
||||
*
|
||||
* @ingroup actions
|
||||
* @see comment_unpublish_by_keyword_action_form()
|
||||
* @see comment_unpublish_by_keyword_action_submit()
|
||||
*/
|
||||
function comment_unpublish_by_keyword_action(Comment $comment, $context) {
|
||||
$build = comment_view($comment);
|
||||
$text = drupal_render($build);
|
||||
foreach ($context['keywords'] as $keyword) {
|
||||
if (strpos($text, $keyword) !== FALSE) {
|
||||
$comment->status->value = COMMENT_NOT_PUBLISHED;
|
||||
watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject->value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form constructor for the blacklisted keywords form.
|
||||
*
|
||||
* @ingroup forms
|
||||
* @see comment_unpublish_by_keyword_action()
|
||||
* @see comment_unpublish_by_keyword_action_submit()
|
||||
*/
|
||||
function comment_unpublish_by_keyword_action_form($context) {
|
||||
$form['keywords'] = array(
|
||||
'#title' => t('Keywords'),
|
||||
'#type' => 'textarea',
|
||||
'#description' => t('The comment will be unpublished if it contains any of the phrases above. Use a case-sensitive, comma-separated list of phrases. Example: funny, bungee jumping, "Company, Inc."'),
|
||||
'#default_value' => isset($context['keywords']) ? drupal_implode_tags($context['keywords']) : '',
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for comment_unpublish_by_keyword_action_form().
|
||||
*
|
||||
* @see comment_unpublish_by_keyword_action()
|
||||
*/
|
||||
function comment_unpublish_by_keyword_action_submit($form, $form_state) {
|
||||
return array('keywords' => drupal_explode_tags($form_state['values']['keywords']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a comment.
|
||||
*
|
||||
* @param Drupal\comment\Comment $comment
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function comment_save_action(Comment $comment) {
|
||||
comment_save($comment);
|
||||
cache_invalidate_tags(array('content' => TRUE));
|
||||
watchdog('action', 'Saved comment %title', array('%title' => $comment->subject->value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ranking().
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
id: comment_publish_action
|
||||
label: 'Publish comment'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: comment
|
||||
plugin: comment_publish_action
|
|
@ -0,0 +1,6 @@
|
|||
id: comment_save_action
|
||||
label: 'Save comment'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: comment
|
||||
plugin: comment_save_action
|
|
@ -0,0 +1,6 @@
|
|||
id: comment_unpublish_action
|
||||
label: 'Unpublish comment'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: comment
|
||||
plugin: comment_unpublish_action
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\comment\Plugin\Action\PublishComment.
|
||||
*/
|
||||
|
||||
namespace Drupal\comment\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Publishes a comment.
|
||||
*
|
||||
* @Action(
|
||||
* id = "comment_publish_action",
|
||||
* label = @Translation("Publish comment"),
|
||||
* type = "comment"
|
||||
* )
|
||||
*/
|
||||
class PublishComment extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($comment = NULL) {
|
||||
$comment->status->value = COMMENT_PUBLISHED;
|
||||
$comment->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\comment\Plugin\Action\SaveComment.
|
||||
*/
|
||||
|
||||
namespace Drupal\comment\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Saves a comment.
|
||||
*
|
||||
* @Action(
|
||||
* id = "comment_save_action",
|
||||
* label = @Translation("Save comment"),
|
||||
* type = "comment"
|
||||
* )
|
||||
*/
|
||||
class SaveComment extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($comment = NULL) {
|
||||
$comment->save();
|
||||
Cache::invalidateTags(array('content' => TRUE));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\comment\Plugin\Action\UnpublishByKeywordComment.
|
||||
*/
|
||||
|
||||
namespace Drupal\comment\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ConfigurableActionBase;
|
||||
|
||||
/**
|
||||
* Unpublishes a comment containing certain keywords.
|
||||
*
|
||||
* @Action(
|
||||
* id = "comment_unpublish_by_keyword_action",
|
||||
* label = @Translation("Unpublish comment containing keyword(s)"),
|
||||
* type = "comment"
|
||||
* )
|
||||
*/
|
||||
class UnpublishByKeywordComment extends ConfigurableActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($comment = NULL) {
|
||||
$build = comment_view($comment);
|
||||
$text = drupal_render($build);
|
||||
foreach ($this->configuration['keywords'] as $keyword) {
|
||||
if (strpos($text, $keyword) !== FALSE) {
|
||||
$comment->status->value = COMMENT_NOT_PUBLISHED;
|
||||
$comment->save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultConfiguration() {
|
||||
return array(
|
||||
'keywords' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, array &$form_state) {
|
||||
$form['keywords'] = array(
|
||||
'#title' => t('Keywords'),
|
||||
'#type' => 'textarea',
|
||||
'#description' => t('The comment will be unpublished if it contains any of the phrases above. Use a case-sensitive, comma-separated list of phrases. Example: funny, bungee jumping, "Company, Inc."'),
|
||||
'#default_value' => drupal_implode_tags($this->configuration['keywords']),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submit(array &$form, array &$form_state) {
|
||||
$this->configuration['keywords'] = drupal_explode_tags($form_state['values']['keywords']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\comment\Plugin\Action\UnpublishComment.
|
||||
*/
|
||||
|
||||
namespace Drupal\comment\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Unpublishes a comment.
|
||||
*
|
||||
* @Action(
|
||||
* id = "comment_unpublish_action",
|
||||
* label = @Translation("Unpublish comment"),
|
||||
* type = "comment"
|
||||
* )
|
||||
*/
|
||||
class UnpublishComment extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($comment = NULL) {
|
||||
$comment->status->value = COMMENT_NOT_PUBLISHED;
|
||||
$comment->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -36,28 +36,15 @@ class CommentActionsTest extends CommentTestBase {
|
|||
$subject = $this->randomName();
|
||||
$comment = $this->postComment($this->node, $comment_text, $subject);
|
||||
|
||||
// Unpublish a comment (direct form: doesn't actually save the comment).
|
||||
comment_unpublish_action($comment);
|
||||
// Unpublish a comment.
|
||||
$action = entity_load('action', 'comment_unpublish_action');
|
||||
$action->execute(array($comment));
|
||||
$this->assertEqual($comment->status->value, COMMENT_NOT_PUBLISHED, 'Comment was unpublished');
|
||||
$this->assertWatchdogMessage('Unpublished comment %subject.', array('%subject' => $subject), 'Found watchdog message');
|
||||
$this->clearWatchdog();
|
||||
|
||||
// Unpublish a comment (indirect form: modify the comment in the database).
|
||||
comment_unpublish_action(NULL, array('cid' => $comment->id()));
|
||||
$this->assertEqual(comment_load($comment->id())->status->value, COMMENT_NOT_PUBLISHED, 'Comment was unpublished');
|
||||
$this->assertWatchdogMessage('Unpublished comment %subject.', array('%subject' => $subject), 'Found watchdog message');
|
||||
|
||||
// Publish a comment (direct form: doesn't actually save the comment).
|
||||
comment_publish_action($comment);
|
||||
// Publish a comment.
|
||||
$action = entity_load('action', 'comment_publish_action');
|
||||
$action->execute(array($comment));
|
||||
$this->assertEqual($comment->status->value, COMMENT_PUBLISHED, 'Comment was published');
|
||||
$this->assertWatchdogMessage('Published comment %subject.', array('%subject' => $subject), 'Found watchdog message');
|
||||
$this->clearWatchdog();
|
||||
|
||||
// Publish a comment (indirect form: modify the comment in the database).
|
||||
comment_publish_action(NULL, array('cid' => $comment->id()));
|
||||
$this->assertEqual(comment_load($comment->id())->status->value, COMMENT_PUBLISHED, 'Comment was published');
|
||||
$this->assertWatchdogMessage('Published comment %subject.', array('%subject' => $subject), 'Found watchdog message');
|
||||
$this->clearWatchdog();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,9 +54,16 @@ class CommentActionsTest extends CommentTestBase {
|
|||
$this->drupalLogin($this->admin_user);
|
||||
$keyword_1 = $this->randomName();
|
||||
$keyword_2 = $this->randomName();
|
||||
$aid = action_save('comment_unpublish_by_keyword_action', 'comment', array('keywords' => array($keyword_1, $keyword_2)), $this->randomName());
|
||||
|
||||
$this->assertTrue(action_load($aid), 'The action could be loaded.');
|
||||
$action = entity_create('action', array(
|
||||
'id' => 'comment_unpublish_by_keyword_action',
|
||||
'label' => $this->randomName(),
|
||||
'type' => 'comment',
|
||||
'configuration' => array(
|
||||
'keywords' => array($keyword_1, $keyword_2),
|
||||
),
|
||||
'plugin' => 'comment_unpublish_by_keyword_action',
|
||||
));
|
||||
$action->save();
|
||||
|
||||
$comment = $this->postComment($this->node, $keyword_2, $this->randomName());
|
||||
|
||||
|
@ -78,29 +72,8 @@ class CommentActionsTest extends CommentTestBase {
|
|||
|
||||
$this->assertTrue($comment->status->value == COMMENT_PUBLISHED, 'The comment status was set to published.');
|
||||
|
||||
actions_do($aid, $comment, array());
|
||||
$action->execute(array($comment));
|
||||
$this->assertTrue($comment->status->value == COMMENT_NOT_PUBLISHED, 'The comment status was set to not published.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a watchdog message has been entered.
|
||||
*
|
||||
* @param $watchdog_message
|
||||
* The watchdog message.
|
||||
* @param $variables
|
||||
* The array of variables passed to watchdog().
|
||||
* @param $message
|
||||
* The assertion message.
|
||||
*/
|
||||
function assertWatchdogMessage($watchdog_message, $variables, $message) {
|
||||
$status = (bool) db_query_range("SELECT 1 FROM {watchdog} WHERE message = :message AND variables = :variables", 0, 1, array(':message' => $watchdog_message, ':variables' => serialize($variables)))->fetchField();
|
||||
return $this->assert($status, format_string('@message', array('@message'=> $message)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears watchdog.
|
||||
*/
|
||||
function clearWatchdog() {
|
||||
db_truncate('watchdog')->execute();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
id: node_delete_action
|
||||
label: 'Delete selected content'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_delete_action
|
|
@ -0,0 +1,6 @@
|
|||
id: node_make_sticky_action
|
||||
label: 'Make content sticky'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_make_sticky_action
|
|
@ -0,0 +1,6 @@
|
|||
id: node_make_unsticky_action
|
||||
label: 'Make content unsticky'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_make_unsticky_action
|
|
@ -0,0 +1,6 @@
|
|||
id: node_promote_action
|
||||
label: 'Promote content to front page'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_promote_action
|
|
@ -0,0 +1,6 @@
|
|||
id: node_publish_action
|
||||
label: 'Publish content'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_publish_action
|
|
@ -0,0 +1,6 @@
|
|||
id: node_save_action
|
||||
label: 'Save content'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_save_action
|
|
@ -0,0 +1,6 @@
|
|||
id: node_unpromote_action
|
||||
label: 'Remove content from front page'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_unpromote_action
|
|
@ -0,0 +1,6 @@
|
|||
id: node_unpublish_action
|
||||
label: 'Unpublish content'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_unpublish_action
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Form\DeleteMultiple.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Form;
|
||||
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\ControllerInterface;
|
||||
use Drupal\Core\Entity\EntityManager;
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\user\TempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a node deletion confirmation form.
|
||||
*/
|
||||
class DeleteMultiple extends ConfirmFormBase implements ControllerInterface {
|
||||
|
||||
/**
|
||||
* The array of nodes to delete.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nodes = array();
|
||||
|
||||
/**
|
||||
* The tempstore factory.
|
||||
*
|
||||
* @var \Drupal\user\TempStoreFactory
|
||||
*/
|
||||
protected $tempStoreFactory;
|
||||
|
||||
/**
|
||||
* The node storage controller.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Constructs a DeleteMultiple form object.
|
||||
*
|
||||
* @param \Drupal\user\TempStoreFactory $temp_store_factory
|
||||
* The tempstore factory.
|
||||
* @param \Drupal\Core\Entity\EntityManager $manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(TempStoreFactory $temp_store_factory, EntityManager $manager) {
|
||||
$this->tempStoreFactory = $temp_store_factory;
|
||||
$this->storageController = $manager->getStorageController('node');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('user.tempstore'),
|
||||
$container->get('plugin.manager.entity')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormID() {
|
||||
return 'node_multiple_delete_confirm';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getQuestion() {
|
||||
return format_plural(count($this->nodes), 'Are you sure you want to delete this item?', 'Are you sure you want to delete these items?');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCancelPath() {
|
||||
return 'admin/content';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getConfirmText() {
|
||||
return t('Delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, array &$form_state) {
|
||||
$this->nodes = $this->tempStoreFactory->get('node_multiple_delete_confirm')->get($GLOBALS['user']->uid);
|
||||
if (empty($this->nodes)) {
|
||||
drupal_goto($this->getCancelPath());
|
||||
}
|
||||
|
||||
$form['nodes'] = array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => array_map(function ($node) {
|
||||
return String::checkPlain($node->label());
|
||||
}, $this->nodes),
|
||||
);
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, array &$form_state) {
|
||||
if ($form_state['values']['confirm'] && !empty($this->nodes)) {
|
||||
$this->storageController->delete($this->nodes);
|
||||
$this->tempStoreFactory->get('node_multiple_delete_confirm')->delete($GLOBALS['user']->uid);
|
||||
$count = count($this->nodes);
|
||||
watchdog('content', 'Deleted @count posts.', array('@count' => $count));
|
||||
drupal_set_message(format_plural($count, 'Deleted 1 post.', 'Deleted @count posts.'));
|
||||
}
|
||||
$form_state['redirect'] = 'admin/content';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\AssignOwnerNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ConfigurableActionBase;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Assigns ownership of a node to a user.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_assign_owner_action",
|
||||
* label = @Translation("Change the author of content"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class AssignOwnerNode extends ConfigurableActionBase {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Constructs a new AssignOwnerNode action.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param array $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, array $plugin_definition, Connection $connection) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
|
||||
return new static($configuration, $plugin_id, $plugin_definition,
|
||||
$container->get('database')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->uid = $this->configuration['owner_uid'];
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultConfiguration() {
|
||||
return array(
|
||||
'owner_uid' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, array &$form_state) {
|
||||
$description = t('The username of the user to which you would like to assign ownership.');
|
||||
$count = $this->connection->query("SELECT COUNT(*) FROM {users}")->fetchField();
|
||||
$owner_name = '';
|
||||
if (is_numeric($this->configuration['owner_uid'])) {
|
||||
$owner_name = $this->connection->query("SELECT name FROM {users} WHERE uid = :uid", array(':uid' => $this->configuration['owner_uid']))->fetchField();
|
||||
}
|
||||
|
||||
// Use dropdown for fewer than 200 users; textbox for more than that.
|
||||
if (intval($count) < 200) {
|
||||
$options = array();
|
||||
$result = $this->connection->query("SELECT uid, name FROM {users} WHERE uid > 0 ORDER BY name");
|
||||
foreach ($result as $data) {
|
||||
$options[$data->name] = $data->name;
|
||||
}
|
||||
$form['owner_name'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Username'),
|
||||
'#default_value' => $owner_name,
|
||||
'#options' => $options,
|
||||
'#description' => $description,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['owner_name'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Username'),
|
||||
'#default_value' => $owner_name,
|
||||
'#autocomplete_path' => 'user/autocomplete',
|
||||
'#size' => '6',
|
||||
'#maxlength' => '60',
|
||||
'#description' => $description,
|
||||
);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate(array &$form, array &$form_state) {
|
||||
$exists = (bool) $this->connection->queryRange('SELECT 1 FROM {users} WHERE name = :name', 0, 1, array(':name' => $form_state['values']['owner_name']))->fetchField();
|
||||
if (!$exists) {
|
||||
form_set_error('owner_name', t('Enter a valid username.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submit(array &$form, array &$form_state) {
|
||||
$this->configuration['owner_uid'] = $this->connection->query('SELECT uid from {users} WHERE name = :name', array(':name' => $form_state['values']['owner_name']))->fetchField();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\DeleteNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\user\TempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Redirects to a node deletion form.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_delete_action",
|
||||
* label = @Translation("Delete selected content"),
|
||||
* type = "node",
|
||||
* confirm_form_path = "admin/content/node/delete"
|
||||
* )
|
||||
*/
|
||||
class DeleteNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* The tempstore object.
|
||||
*
|
||||
* @var \Drupal\user\TempStore
|
||||
*/
|
||||
protected $tempStore;
|
||||
|
||||
/**
|
||||
* Constructs a new DeleteNode object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param array $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\user\TempStoreFactory $temp_store_factory
|
||||
* The tempstore factory.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, array $plugin_definition, TempStoreFactory $temp_store_factory) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->tempStore = $temp_store_factory->get('node_multiple_delete_confirm');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
|
||||
return new static($configuration, $plugin_id, $plugin_definition, $container->get('user.tempstore'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function executeMultiple(array $entities) {
|
||||
$this->tempStore->set($GLOBALS['user']->uid, $entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($object = NULL) {
|
||||
$this->executeMultiple(array($object));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\DemoteNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Demotes a node.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_unpromote_action",
|
||||
* label = @Translation("Demote selected content from front page"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class DemoteNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->promote = NODE_NOT_PROMOTED;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\PromoteNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Promotes a node.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_promote_action",
|
||||
* label = @Translation("Promote selected content to front page"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class PromoteNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->status = NODE_PUBLISHED;
|
||||
$entity->promote = NODE_PROMOTED;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\PublishNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Publishes a node.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_publish_action",
|
||||
* label = @Translation("Publish selected content"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class PublishNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->status = NODE_PUBLISHED;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\SaveNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Provides an action that can save any entity.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_save_action",
|
||||
* label = @Translation("Save content"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class SaveNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\StickyNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Makes a node sticky.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_make_sticky_action",
|
||||
* label = @Translation("Make selected content sticky"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class StickyNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->status = NODE_PUBLISHED;
|
||||
$entity->sticky = NODE_STICKY;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\UnpublishByKeywordNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ConfigurableActionBase;
|
||||
|
||||
/**
|
||||
* Unpublishes a node containing certain keywords.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_unpublish_by_keyword_action",
|
||||
* label = @Translation("Unpublish content containing keyword(s)"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class UnpublishByKeywordNode extends ConfigurableActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($node = NULL) {
|
||||
foreach ($this->configuration['keywords'] as $keyword) {
|
||||
$elements = node_view(clone $node);
|
||||
if (strpos(drupal_render($elements), $keyword) !== FALSE || strpos($node->label(), $keyword) !== FALSE) {
|
||||
$node->status = NODE_NOT_PUBLISHED;
|
||||
$node->save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultConfiguration() {
|
||||
return array(
|
||||
'keywords' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, array &$form_state) {
|
||||
$form['keywords'] = array(
|
||||
'#title' => t('Keywords'),
|
||||
'#type' => 'textarea',
|
||||
'#description' => t('The content will be unpublished if it contains any of the phrases above. Use a case-sensitive, comma-separated list of phrases. Example: funny, bungee jumping, "Company, Inc."'),
|
||||
'#default_value' => drupal_implode_tags($this->configuration['keywords']),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submit(array &$form, array &$form_state) {
|
||||
$this->configuration['keywords'] = drupal_explode_tags($form_state['values']['keywords']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\UnpublishNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Unpublishes a node.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_unpublish_action",
|
||||
* label = @Translation("Unpublish selected content"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class UnpublishNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->status = NODE_NOT_PUBLISHED;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\UnstickyNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Makes a node not sticky.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_make_unsticky_action",
|
||||
* label = @Translation("Make selected content not sticky"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class UnstickyNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->sticky = NODE_NOT_STICKY;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -31,49 +31,6 @@ function node_configure_rebuild_confirm_submit($form, &$form_state) {
|
|||
$form_state['redirect'] = 'admin/reports/status';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_operations().
|
||||
*/
|
||||
function node_node_operations() {
|
||||
$operations = array(
|
||||
'publish' => array(
|
||||
'label' => t('Publish selected content'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED)),
|
||||
),
|
||||
'unpublish' => array(
|
||||
'label' => t('Unpublish selected content'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('status' => NODE_NOT_PUBLISHED)),
|
||||
),
|
||||
'promote' => array(
|
||||
'label' => t('Promote selected content to front page'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED, 'promote' => NODE_PROMOTED)),
|
||||
),
|
||||
'demote' => array(
|
||||
'label' => t('Demote selected content from front page'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('promote' => NODE_NOT_PROMOTED)),
|
||||
),
|
||||
'sticky' => array(
|
||||
'label' => t('Make selected content sticky'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED, 'sticky' => NODE_STICKY)),
|
||||
),
|
||||
'unsticky' => array(
|
||||
'label' => t('Make selected content not sticky'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('sticky' => NODE_NOT_STICKY)),
|
||||
),
|
||||
'delete' => array(
|
||||
'label' => t('Delete selected content'),
|
||||
'callback' => NULL,
|
||||
),
|
||||
);
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists node administration filters that can be applied.
|
||||
*
|
||||
|
@ -446,12 +403,13 @@ function node_admin_nodes() {
|
|||
'#access' => $admin_access,
|
||||
);
|
||||
$options = array();
|
||||
foreach (module_invoke_all('node_operations') as $operation => $array) {
|
||||
$options[$operation] = $array['label'];
|
||||
$actions = entity_load_multiple_by_properties('action', array('type' => 'node'));
|
||||
foreach ($actions as $id => $action) {
|
||||
$options[$id] = $action->label();
|
||||
}
|
||||
$form['options']['operation'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Operation'),
|
||||
'#title' => t('Action'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $options,
|
||||
'#default_value' => 'approve',
|
||||
|
@ -630,20 +588,13 @@ function node_admin_nodes() {
|
|||
* @see node_multiple_delete_confirm_submit()
|
||||
*/
|
||||
function node_admin_nodes_submit($form, &$form_state) {
|
||||
$operations = module_invoke_all('node_operations');
|
||||
$operation = $operations[$form_state['values']['operation']];
|
||||
// Filter out unchecked nodes
|
||||
$nodes = array_filter($form_state['values']['nodes']);
|
||||
if ($function = $operation['callback']) {
|
||||
// Add in callback arguments if present.
|
||||
if (isset($operation['callback arguments'])) {
|
||||
$args = array_merge(array($nodes), $operation['callback arguments']);
|
||||
if ($action = entity_load('action', $form_state['values']['operation'])) {
|
||||
$nodes = entity_load_multiple('node', array_filter($form_state['values']['nodes']));
|
||||
$action->execute($nodes);
|
||||
$operation_definition = $action->getPluginDefinition();
|
||||
if (!empty($operation_definition['confirm_form_path'])) {
|
||||
$form_state['redirect'] = $operation_definition['confirm_form_path'];
|
||||
}
|
||||
else {
|
||||
$args = array($nodes);
|
||||
}
|
||||
call_user_func_array($function, $args);
|
||||
|
||||
cache_invalidate_tags(array('content' => TRUE));
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -401,64 +401,6 @@ function hook_node_grants_alter(&$grants, $account, $op) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add mass node operations.
|
||||
*
|
||||
* This hook enables modules to inject custom operations into the mass
|
||||
* operations dropdown found at admin/content, by associating a callback
|
||||
* function with the operation, which is called when the form is submitted. The
|
||||
* callback function receives one initial argument, which is an array of the
|
||||
* checked nodes.
|
||||
*
|
||||
* @return
|
||||
* An array of operations. Each operation is an associative array that may
|
||||
* contain the following key-value pairs:
|
||||
* - label: (required) The label for the operation, displayed in the dropdown
|
||||
* menu.
|
||||
* - callback: (required) The function to call for the operation.
|
||||
* - callback arguments: (optional) An array of additional arguments to pass
|
||||
* to the callback function.
|
||||
*/
|
||||
function hook_node_operations() {
|
||||
$operations = array(
|
||||
'publish' => array(
|
||||
'label' => t('Publish selected content'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED)),
|
||||
),
|
||||
'unpublish' => array(
|
||||
'label' => t('Unpublish selected content'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('status' => NODE_NOT_PUBLISHED)),
|
||||
),
|
||||
'promote' => array(
|
||||
'label' => t('Promote selected content to front page'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED, 'promote' => NODE_PROMOTED)),
|
||||
),
|
||||
'demote' => array(
|
||||
'label' => t('Demote selected content from front page'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('promote' => NODE_NOT_PROMOTED)),
|
||||
),
|
||||
'sticky' => array(
|
||||
'label' => t('Make selected content sticky'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED, 'sticky' => NODE_STICKY)),
|
||||
),
|
||||
'unsticky' => array(
|
||||
'label' => t('Make selected content not sticky'),
|
||||
'callback' => 'node_mass_update',
|
||||
'callback arguments' => array('updates' => array('sticky' => NODE_NOT_STICKY)),
|
||||
),
|
||||
'delete' => array(
|
||||
'label' => t('Delete selected content'),
|
||||
'callback' => NULL,
|
||||
),
|
||||
);
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Act before node deletion.
|
||||
*
|
||||
|
|
|
@ -3111,332 +3111,6 @@ function node_content_form(EntityInterface $node, $form_state) {
|
|||
* @} End of "defgroup node_content".
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_action_info().
|
||||
*/
|
||||
function node_action_info() {
|
||||
return array(
|
||||
'node_publish_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Publish content'),
|
||||
'configurable' => FALSE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('node_presave', 'comment_insert', 'comment_update', 'comment_delete'),
|
||||
),
|
||||
'node_unpublish_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Unpublish content'),
|
||||
'configurable' => FALSE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('node_presave', 'comment_insert', 'comment_update', 'comment_delete'),
|
||||
),
|
||||
'node_make_sticky_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Make content sticky'),
|
||||
'configurable' => FALSE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('node_presave', 'comment_insert', 'comment_update', 'comment_delete'),
|
||||
),
|
||||
'node_make_unsticky_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Make content unsticky'),
|
||||
'configurable' => FALSE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('node_presave', 'comment_insert', 'comment_update', 'comment_delete'),
|
||||
),
|
||||
'node_promote_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Promote content to front page'),
|
||||
'configurable' => FALSE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('node_presave', 'comment_insert', 'comment_update', 'comment_delete'),
|
||||
),
|
||||
'node_unpromote_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Remove content from front page'),
|
||||
'configurable' => FALSE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('node_presave', 'comment_insert', 'comment_update', 'comment_delete'),
|
||||
),
|
||||
'node_assign_owner_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Change the author of content'),
|
||||
'configurable' => TRUE,
|
||||
'behavior' => array('changes_property'),
|
||||
'triggers' => array('node_presave', 'comment_insert', 'comment_update', 'comment_delete'),
|
||||
),
|
||||
'node_save_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Save content'),
|
||||
'configurable' => FALSE,
|
||||
'triggers' => array('comment_insert', 'comment_update', 'comment_delete'),
|
||||
),
|
||||
'node_unpublish_by_keyword_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Unpublish content containing keyword(s)'),
|
||||
'configurable' => TRUE,
|
||||
'triggers' => array('node_presave', 'node_insert', 'node_update'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the status of a node to 1 (published).
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* A node entity.
|
||||
* @param $context
|
||||
* (optional) Array of additional information about what triggered the action.
|
||||
* Not used for this action.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function node_publish_action(EntityInterface $node, $context = array()) {
|
||||
$node->status = NODE_PUBLISHED;
|
||||
watchdog('action', 'Set @type %title to published.', array('@type' => node_get_type_label($node), '%title' => $node->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the status of a node to 0 (unpublished).
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* A node entity.
|
||||
* @param $context
|
||||
* (optional) Array of additional information about what triggered the action.
|
||||
* Not used for this action.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function node_unpublish_action(EntityInterface $node, $context = array()) {
|
||||
$node->status = NODE_NOT_PUBLISHED;
|
||||
watchdog('action', 'Set @type %title to unpublished.', array('@type' => node_get_type_label($node), '%title' => $node->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sticky-at-top-of-list property of a node to 1.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* A node entity.
|
||||
* @param $context
|
||||
* (optional) Array of additional information about what triggered the action.
|
||||
* Not used for this action.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function node_make_sticky_action(EntityInterface $node, $context = array()) {
|
||||
$node->sticky = NODE_STICKY;
|
||||
watchdog('action', 'Set @type %title to sticky.', array('@type' => node_get_type_label($node), '%title' => $node->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sticky-at-top-of-list property of a node to 0.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* A node entity.
|
||||
* @param $context
|
||||
* (optional) Array of additional information about what triggered the action.
|
||||
* Not used for this action.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function node_make_unsticky_action(EntityInterface $node, $context = array()) {
|
||||
$node->sticky = NODE_NOT_STICKY;
|
||||
watchdog('action', 'Set @type %title to unsticky.', array('@type' => node_get_type_label($node), '%title' => $node->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the promote property of a node to 1.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* A node entity.
|
||||
* @param $context
|
||||
* (optional) Array of additional information about what triggered the action.
|
||||
* Not used for this action.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function node_promote_action(EntityInterface $node, $context = array()) {
|
||||
$node->promote = NODE_PROMOTED;
|
||||
watchdog('action', 'Promoted @type %title to front page.', array('@type' => node_get_type_label($node), '%title' => $node->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the promote property of a node to 0.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* A node entity.
|
||||
* @param $context
|
||||
* (optional) Array of additional information about what triggered the action.
|
||||
* Not used for this action.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function node_unpromote_action(EntityInterface $node, $context = array()) {
|
||||
$node->promote = NODE_NOT_PROMOTED;
|
||||
watchdog('action', 'Removed @type %title from front page.', array('@type' => node_get_type_label($node), '%title' => $node->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a node.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* The node to be saved.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function node_save_action(EntityInterface $node) {
|
||||
$node->save();
|
||||
watchdog('action', 'Saved @type %title', array('@type' => node_get_type_label($node), '%title' => $node->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns ownership of a node to a user.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* A node entity to modify.
|
||||
* @param $context
|
||||
* Array of additional information about what triggered the action. Includes
|
||||
* the following elements:
|
||||
* - owner_uid: User ID to assign to the node.
|
||||
*
|
||||
* @see node_assign_owner_action_form()
|
||||
* @see node_assign_owner_action_validate()
|
||||
* @see node_assign_owner_action_submit()
|
||||
* @ingroup actions
|
||||
*/
|
||||
function node_assign_owner_action(EntityInterface $node, $context) {
|
||||
$node->uid = $context['owner_uid'];
|
||||
$owner_name = db_query("SELECT name FROM {users} WHERE uid = :uid", array(':uid' => $context['owner_uid']))->fetchField();
|
||||
watchdog('action', 'Changed owner of @type %title to uid %name.', array('@type' => node_get_type_label($node), '%title' => $node->label(), '%name' => $owner_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form constructor for the settings form for node_assign_owner_action().
|
||||
*
|
||||
* @param $context
|
||||
* Array of additional information about what triggered the action. Includes
|
||||
* the following elements:
|
||||
* - owner_uid: User ID to assign to the node.
|
||||
*
|
||||
* @see node_assign_owner_action_submit()
|
||||
* @see node_assign_owner_action_validate()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function node_assign_owner_action_form($context) {
|
||||
$description = t('The username of the user to which you would like to assign ownership.');
|
||||
$count = db_query("SELECT COUNT(*) FROM {users}")->fetchField();
|
||||
$owner_name = '';
|
||||
if (isset($context['owner_uid'])) {
|
||||
$owner_name = db_query("SELECT name FROM {users} WHERE uid = :uid", array(':uid' => $context['owner_uid']))->fetchField();
|
||||
}
|
||||
|
||||
// Use dropdown for fewer than 200 users; textbox for more than that.
|
||||
if (intval($count) < 200) {
|
||||
$options = array();
|
||||
$result = db_query("SELECT uid, name FROM {users} WHERE uid > 0 ORDER BY name");
|
||||
foreach ($result as $data) {
|
||||
$options[$data->name] = $data->name;
|
||||
}
|
||||
$form['owner_name'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Username'),
|
||||
'#default_value' => $owner_name,
|
||||
'#options' => $options,
|
||||
'#description' => $description,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['owner_name'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Username'),
|
||||
'#default_value' => $owner_name,
|
||||
'#autocomplete_path' => 'user/autocomplete',
|
||||
'#size' => '6',
|
||||
'#maxlength' => '60',
|
||||
'#description' => $description,
|
||||
);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form validation handler for node_assign_owner_action_form().
|
||||
*
|
||||
* @see node_assign_owner_action_submit()
|
||||
*/
|
||||
function node_assign_owner_action_validate($form, $form_state) {
|
||||
$exists = (bool) db_query_range('SELECT 1 FROM {users} WHERE name = :name', 0, 1, array(':name' => $form_state['values']['owner_name']))->fetchField();
|
||||
if (!$exists) {
|
||||
form_set_error('owner_name', t('Enter a valid username.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for node_assign_owner_action_form().
|
||||
*
|
||||
* @see node_assign_owner_action_validate()
|
||||
*/
|
||||
function node_assign_owner_action_submit($form, $form_state) {
|
||||
// Username can change, so we need to store the ID, not the username.
|
||||
$uid = db_query('SELECT uid from {users} WHERE name = :name', array(':name' => $form_state['values']['owner_name']))->fetchField();
|
||||
return array('owner_uid' => $uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates settings form for node_unpublish_by_keyword_action().
|
||||
*
|
||||
* @param array $context
|
||||
* Array of additional information about what triggered this action.
|
||||
*
|
||||
* @return array
|
||||
* A form array.
|
||||
*
|
||||
* @see node_unpublish_by_keyword_action_submit()
|
||||
*/
|
||||
function node_unpublish_by_keyword_action_form($context) {
|
||||
$form['keywords'] = array(
|
||||
'#title' => t('Keywords'),
|
||||
'#type' => 'textarea',
|
||||
'#description' => t('The content will be unpublished if it contains any of the phrases above. Use a case-sensitive, comma-separated list of phrases. Example: funny, bungee jumping, "Company, Inc."'),
|
||||
'#default_value' => isset($context['keywords']) ? drupal_implode_tags($context['keywords']) : '',
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for node_unpublish_by_keyword_action().
|
||||
*/
|
||||
function node_unpublish_by_keyword_action_submit($form, $form_state) {
|
||||
return array('keywords' => drupal_explode_tags($form_state['values']['keywords']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpublishes a node containing certain keywords.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* A node entity to modify.
|
||||
* @param $context
|
||||
* Array of additional information about what triggered the action. Includes
|
||||
* the following elements:
|
||||
* - keywords: Array of keywords. If any keyword is present in the rendered
|
||||
* node, the node's status flag is set to unpublished.
|
||||
*
|
||||
* @see node_unpublish_by_keyword_action_form()
|
||||
* @see node_unpublish_by_keyword_action_submit()
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function node_unpublish_by_keyword_action(EntityInterface $node, $context) {
|
||||
foreach ($context['keywords'] as $keyword) {
|
||||
$elements = node_view(clone $node);
|
||||
if (strpos(drupal_render($elements), $keyword) !== FALSE || strpos($node->label(), $keyword) !== FALSE) {
|
||||
$node->status = NODE_NOT_PUBLISHED;
|
||||
watchdog('action', 'Set @type %title to unpublished.', array('@type' => node_get_type_label($node), '%title' => $node->label()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
node_multiple_delete_confirm:
|
||||
pattern: '/admin/content/node/delete'
|
||||
defaults:
|
||||
_form: '\Drupal\node\Form\DeleteMultiple'
|
||||
requirements:
|
||||
_permission: 'administer nodes'
|
|
@ -54,7 +54,7 @@ class SimpletestTestForm implements FormInterface {
|
|||
}
|
||||
}
|
||||
|
||||
// Operation buttons.
|
||||
// Action buttons.
|
||||
$form['tests']['op'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Run tests'),
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\ActionConfigEntityInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\system;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a action entity.
|
||||
*/
|
||||
interface ActionConfigEntityInterface extends ConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* Returns whether or not this action is configurable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConfigurable();
|
||||
|
||||
/**
|
||||
* Returns the operation type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType();
|
||||
|
||||
/**
|
||||
* Returns the operation plugin.
|
||||
*
|
||||
* @return \Drupal\Core\Action\ActionInterface
|
||||
*/
|
||||
public function getPlugin();
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\ActionStorageController.
|
||||
*/
|
||||
|
||||
namespace Drupal\system;
|
||||
|
||||
use Drupal\Core\Action\ConfigurableActionInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigStorageController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Defines the storage controller class for Action entities.
|
||||
*/
|
||||
class ActionStorageController extends ConfigStorageController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) {
|
||||
parent::preSave($entity);
|
||||
|
||||
$plugin = $entity->getPlugin();
|
||||
// If this plugin has any configuration, ensure that it is set.
|
||||
if ($plugin instanceof ConfigurableActionInterface) {
|
||||
$entity->set('configuration', $plugin->getConfiguration());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Plugin\Core\Entity\Action.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Plugin\Core\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\system\ActionConfigEntityInterface;
|
||||
use Drupal\Core\Action\ActionBag;
|
||||
use Drupal\Core\Action\ConfigurableActionInterface;
|
||||
|
||||
/**
|
||||
* Defines the configured action entity.
|
||||
*
|
||||
* @EntityType(
|
||||
* id = "action",
|
||||
* label = @Translation("Action"),
|
||||
* module = "system",
|
||||
* controllers = {
|
||||
* "storage" = "Drupal\system\ActionStorageController"
|
||||
* },
|
||||
* config_prefix = "action.action",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label",
|
||||
* "uuid" = "uuid"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class Action extends ConfigEntityBase implements ActionConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* The name (plugin ID) of the action.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The label of the action.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* The UUID of the action.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $uuid;
|
||||
|
||||
/**
|
||||
* The action type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* The configuration of the action.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $configuration = array();
|
||||
|
||||
/**
|
||||
* The plugin ID of the action.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* The plugin bag that stores action plugins.
|
||||
*
|
||||
* @var \Drupal\Core\Action\ActionBag
|
||||
*/
|
||||
protected $pluginBag;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $values, $entity_type) {
|
||||
parent::__construct($values, $entity_type);
|
||||
|
||||
$this->pluginBag = new ActionBag(\Drupal::service('plugin.manager.action'), array($this->plugin), $this->configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPlugin() {
|
||||
return $this->pluginBag->get($this->plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setPlugin($plugin_id) {
|
||||
$this->plugin = $plugin_id;
|
||||
$this->pluginBag->addInstanceID($plugin_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPluginDefinition() {
|
||||
return $this->getPlugin()->getDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute(array $entities) {
|
||||
return $this->getPlugin()->executeMultiple($entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isConfigurable() {
|
||||
return $this->getPlugin() instanceof ConfigurableActionInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function uri() {
|
||||
return array(
|
||||
'path' => 'admin/config/system/actions/configure/' . $this->id(),
|
||||
'options' => array(
|
||||
'entity_type' => $this->entityType,
|
||||
'entity' => $this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function sort($a, $b) {
|
||||
$a_type = $a->getType();
|
||||
$b_type = $b->getType();
|
||||
if ($a_type != $b_type) {
|
||||
return strnatcasecmp($a_type, $b_type);
|
||||
}
|
||||
return parent::sort($a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExportProperties() {
|
||||
$properties = parent::getExportProperties();
|
||||
$names = array(
|
||||
'type',
|
||||
'plugin',
|
||||
'configuration',
|
||||
);
|
||||
foreach ($names as $name) {
|
||||
$properties[$name] = $this->get($name);
|
||||
}
|
||||
return $properties;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,15 +7,48 @@
|
|||
|
||||
namespace Drupal\system\Plugin\views\field;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
use Drupal\Core\Entity\EntityManager;
|
||||
use Drupal\views\Plugin\views\field\FieldPluginBase;
|
||||
use Drupal\views\Plugin\views\style\Table;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a generic bulk operation form element.
|
||||
*/
|
||||
abstract class BulkFormBase extends FieldPluginBase {
|
||||
|
||||
/**
|
||||
* An array of actions that can be executed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $actions = array();
|
||||
|
||||
/**
|
||||
* Constructs a new BulkForm object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param array $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Entity\EntityManager $manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManager $manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->actions = $manager->getStorageController('action')->load();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
|
||||
return new static($configuration, $plugin_id, $plugin_definition, $container->get('plugin.manager.entity'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\views\Plugin\views\Plugin\field\FieldPluginBase::render().
|
||||
*/
|
||||
|
@ -104,7 +137,25 @@ abstract class BulkFormBase extends FieldPluginBase {
|
|||
* @param array $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*/
|
||||
abstract public function views_form_submit(&$form, &$form_state);
|
||||
public function views_form_submit(&$form, &$form_state) {
|
||||
if ($form_state['step'] == 'views_form_views_form') {
|
||||
// Filter only selected checkboxes.
|
||||
$selected = array_filter($form_state['values'][$this->options['id']]);
|
||||
$entities = array();
|
||||
foreach (array_intersect_key($this->view->result, $selected) as $row) {
|
||||
$entity = $this->get_entity($row);
|
||||
$entities[$entity->id()] = $entity;
|
||||
}
|
||||
|
||||
$action = $this->actions[$form_state['values']['action']];
|
||||
$action->execute($entities);
|
||||
|
||||
$operation_definition = $action->getPluginDefinition();
|
||||
if (!empty($operation_definition['confirm_form_path'])) {
|
||||
$form_state['confirm_form_path'] = $operation_definition['confirm_form_path'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\views\Plugin\views\Plugin\field\FieldPluginBase::query().
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Action\ActionUnitTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Action;
|
||||
|
||||
use Drupal\simpletest\DrupalUnitTestBase;
|
||||
use Drupal\Core\Action\ActionInterface;
|
||||
|
||||
/**
|
||||
* Tests action plugins.
|
||||
*/
|
||||
class ActionUnitTest extends DrupalUnitTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('system', 'field', 'user', 'action_test');
|
||||
|
||||
/**
|
||||
* The action manager.
|
||||
*
|
||||
* @var \Drupal\Core\Action\ActionManager
|
||||
*/
|
||||
protected $actionManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Action Plugins',
|
||||
'description' => 'Tests Action plugins.',
|
||||
'group' => 'Action',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->actionManager = $this->container->get('plugin.manager.action');
|
||||
$this->installSchema('user', array('users', 'users_roles'));
|
||||
$this->installSchema('system', array('sequences'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the functionality of test actions.
|
||||
*/
|
||||
public function testOperations() {
|
||||
// Test that actions can be discovered.
|
||||
$definitions = $this->actionManager->getDefinitions();
|
||||
$this->assertTrue(count($definitions) > 1, 'Action definitions are found.');
|
||||
$this->assertTrue(!empty($definitions['action_test_no_type']), 'The test action is among the definitions found.');
|
||||
|
||||
$definition = $this->actionManager->getDefinition('action_test_no_type');
|
||||
$this->assertTrue(!empty($definition), 'The test action definition is found.');
|
||||
|
||||
$definitions = $this->actionManager->getDefinitionsByType('user');
|
||||
$this->assertTrue(empty($definitions['action_test_no_type']), 'An action with no type is not found.');
|
||||
|
||||
// Create an instance of the 'save entity' action.
|
||||
$action = $this->actionManager->createInstance('action_test_save_entity');
|
||||
$this->assertTrue($action instanceof ActionInterface, 'The action implements the correct interface.');
|
||||
|
||||
// Create a new unsaved user.
|
||||
$name = $this->randomName();
|
||||
$user_storage = $this->container->get('plugin.manager.entity')->getStorageController('user');
|
||||
$account = $user_storage->create(array('name' => $name, 'bundle' => 'user'));
|
||||
$loaded_accounts = $user_storage->load();
|
||||
$this->assertEqual(count($loaded_accounts), 0);
|
||||
|
||||
// Execute the 'save entity' action.
|
||||
$action->execute($account);
|
||||
$loaded_accounts = $user_storage->load();
|
||||
$this->assertEqual(count($loaded_accounts), 1);
|
||||
$account = reset($loaded_accounts);
|
||||
$this->assertEqual($name, $account->label());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Upgrade\ActionUpgradePathTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Upgrade;
|
||||
|
||||
/**
|
||||
* Tests the upgrade path of actions.
|
||||
*/
|
||||
class ActionUpgradePathTest extends UpgradePathTestBase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Action upgrade test',
|
||||
'description' => 'Upgrade tests with action data.',
|
||||
'group' => 'Upgrade path',
|
||||
);
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
// Path to the database dump files.
|
||||
$this->databaseDumpFiles = array(
|
||||
drupal_get_path('module', 'system') . '/tests/upgrade/drupal-7.bare.minimal.database.php.gz',
|
||||
);
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if actions were upgrade.
|
||||
*/
|
||||
public function testActionUpgrade() {
|
||||
$this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.');
|
||||
|
||||
$this->drupalGet('admin/people');
|
||||
$elements = $this->xpath('//select[@name="operation"]/option');
|
||||
$this->assertTrue(!empty($elements), 'The user actions were upgraded.');
|
||||
}
|
||||
|
||||
}
|
|
@ -2196,6 +2196,32 @@ function system_update_8056() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert actions to configuration.
|
||||
*
|
||||
* @ingroup config_upgrade
|
||||
*/
|
||||
function system_update_8057() {
|
||||
$actions = db_query("SELECT * FROM {actions}")->fetchAllAssoc('aid', PDO::FETCH_ASSOC);
|
||||
$action_plugins = Drupal::service('plugin.manager.action')->getDefinitions();
|
||||
foreach ($actions as $action) {
|
||||
if (isset($action_plugins[$action['callback']])) {
|
||||
if (is_numeric($action['aid'])) {
|
||||
$action['aid'] = $action['callback'] . '_' . $action['aid'];
|
||||
}
|
||||
$configuration = unserialize($action['parameters']) ?: array();
|
||||
config('action.action.' . $action['aid'])
|
||||
->set('id', $action['aid'])
|
||||
->set('label', $action['label'])
|
||||
->set('status', '1')
|
||||
->set('type', $action['type'])
|
||||
->set('plugin', $action['callback'])
|
||||
->set('configuration', $configuration)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "defgroup updates-7.x-to-8.x".
|
||||
* The next series of updates should start at 9000.
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
name: 'Action test'
|
||||
type: module
|
||||
description: 'Support module for action testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
hidden: true
|
|
@ -0,0 +1 @@
|
|||
<?php
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\action_test\Plugin\Action\NoType.
|
||||
*/
|
||||
|
||||
namespace Drupal\action_test\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Provides an operation with no type specified.
|
||||
*
|
||||
* @Action(
|
||||
* id = "action_test_no_type",
|
||||
* label = @Translation("An operation with no type specified")
|
||||
* )
|
||||
*/
|
||||
class NoType extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\action_test\Plugin\Action\SaveEntity.
|
||||
*/
|
||||
|
||||
namespace Drupal\action_test\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Provides an operation to save user entities.
|
||||
*
|
||||
* @Action(
|
||||
* id = "action_test_save_entity",
|
||||
* label = @Translation("Saves entities"),
|
||||
* type = "user"
|
||||
* )
|
||||
*/
|
||||
class SaveEntity extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -271,7 +271,7 @@ class TrackerTest extends WebTestBase {
|
|||
|
||||
// Unpublish the node and ensure that it's no longer displayed.
|
||||
$edit = array(
|
||||
'operation' => 'unpublish',
|
||||
'operation' => 'node_unpublish_action',
|
||||
'nodes[' . $node->nid . ']' => $node->nid,
|
||||
);
|
||||
$this->drupalPost('admin/content', $edit, t('Update'));
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
id: user_block_user_action
|
||||
label: 'Block the selected user(s)'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: user
|
||||
plugin: user_block_user_action
|
|
@ -0,0 +1,6 @@
|
|||
id: user_cancel_user_action
|
||||
label: 'Cancel the selected user account(s)'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: user
|
||||
plugin: user_cancel_user_action
|
|
@ -0,0 +1,6 @@
|
|||
id: user_unblock_user_action
|
||||
label: 'Unblock the selected user(s)'
|
||||
status: '1'
|
||||
langcode: en
|
||||
type: user
|
||||
plugin: user_unblock_user_action
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Plugin\Action\AddRoleUser.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\user\Plugin\Action\ChangeUserRoleBase;
|
||||
|
||||
/**
|
||||
* Adds a role to a user.
|
||||
*
|
||||
* @Action(
|
||||
* id = "user_add_role_action",
|
||||
* label = @Translation("Add a role to the selected users"),
|
||||
* type = "user"
|
||||
* )
|
||||
*/
|
||||
class AddRoleUser extends ChangeUserRoleBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($account = NULL) {
|
||||
$rid = $this->configuration['rid'];
|
||||
// Skip adding the role to the user if they already have it.
|
||||
if ($account !== FALSE && !isset($account->roles[$rid])) {
|
||||
$roles = $account->roles + array($rid => $rid);
|
||||
// For efficiency manually save the original account before applying
|
||||
// any changes.
|
||||
$account->original = clone $account;
|
||||
$account->roles = $roles;
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Plugin\Action\BlockUser.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Blocks a user.
|
||||
*
|
||||
* @Action(
|
||||
* id = "user_block_user_action",
|
||||
* label = @Translation("Block the selected users"),
|
||||
* type = "user"
|
||||
* )
|
||||
*/
|
||||
class BlockUser extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($account = NULL) {
|
||||
// Skip blocking user if they are already blocked.
|
||||
if ($account !== FALSE && $account->status->value == 1) {
|
||||
// For efficiency manually save the original account before applying any
|
||||
// changes.
|
||||
$account->original = clone $account;
|
||||
$account->status = 0;
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Plugin\Action\CancelUser.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\user\TempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Cancels a user account.
|
||||
*
|
||||
* @Action(
|
||||
* id = "user_cancel_user_action",
|
||||
* label = @Translation("Cancel the selected user accounts"),
|
||||
* type = "user",
|
||||
* confirm_form_path = "admin/people/cancel"
|
||||
* )
|
||||
*/
|
||||
class CancelUser extends ActionBase {
|
||||
|
||||
/**
|
||||
* The tempstore factory.
|
||||
*
|
||||
* @var \Drupal\user\TempStoreFactory
|
||||
*/
|
||||
protected $tempStoreFactory;
|
||||
|
||||
/**
|
||||
* Constructs a DeleteNode object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param array $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\user\TempStoreFactory $temp_store_factory
|
||||
* The tempstore factory.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, array $plugin_definition, TempStoreFactory $temp_store_factory) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->tempStoreFactory = $temp_store_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
|
||||
return new static($configuration, $plugin_id, $plugin_definition, $container->get('user.tempstore'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function executeMultiple(array $entities) {
|
||||
$this->tempStoreFactory->get('user_user_operations_cancel')->set($GLOBALS['user']->uid, $entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($object = NULL) {
|
||||
$this->executeMultiple(array($object));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Plugin\Action\ChangeUserRoleBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\ConfigurableActionBase;
|
||||
|
||||
/**
|
||||
* Provides a base class for operations to change a user's role.
|
||||
*/
|
||||
abstract class ChangeUserRoleBase extends ConfigurableActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultConfiguration() {
|
||||
return array(
|
||||
'rid' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, array &$form_state) {
|
||||
$roles = user_role_names(TRUE);
|
||||
unset($roles[DRUPAL_AUTHENTICATED_RID]);
|
||||
$form['rid'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Role'),
|
||||
'#options' => $roles,
|
||||
'#default_value' => $this->configuration['rid'],
|
||||
'#required' => TRUE,
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submit(array &$form, array &$form_state) {
|
||||
$this->configuration['rid'] = $form_state['values']['rid'];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Plugin\Action\RemoveRoleUser.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\user\Plugin\Action\ChangeUserRoleBase;
|
||||
|
||||
/**
|
||||
* Removes a role from a user.
|
||||
*
|
||||
* @Action(
|
||||
* id = "user_remove_role_action",
|
||||
* label = @Translation("Remove a role from the selected users"),
|
||||
* type = "user"
|
||||
* )
|
||||
*/
|
||||
class RemoveRoleUser extends ChangeUserRoleBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($account = NULL) {
|
||||
$rid = $this->configuration['rid'];
|
||||
// Skip removing the role from the user if they already don't have it.
|
||||
if ($account !== FALSE && isset($account->roles[$rid])) {
|
||||
$roles = array_diff($account->roles, array($rid => $rid));
|
||||
// For efficiency manually save the original account before applying
|
||||
// any changes.
|
||||
$account->original = clone $account;
|
||||
$account->roles = $roles;
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Plugin\Action\UnblockUser.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Annotation\Action;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
|
||||
/**
|
||||
* Unblocks a user.
|
||||
*
|
||||
* @Action(
|
||||
* id = "user_unblock_user_action",
|
||||
* label = @Translation("Unblock the selected users"),
|
||||
* type = "user"
|
||||
* )
|
||||
*/
|
||||
class UnblockUser extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($account = NULL) {
|
||||
// Skip unblocking user if they are already unblocked.
|
||||
if ($account !== FALSE && $account->status->value == 0) {
|
||||
$account->status = 1;
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -73,7 +73,7 @@ class UserAdminTest extends WebTestBase {
|
|||
$account = user_load($user_c->uid);
|
||||
$this->assertEqual($account->status, 1, 'User C not blocked');
|
||||
$edit = array();
|
||||
$edit['operation'] = 'block';
|
||||
$edit['operation'] = 'user_block_user_action';
|
||||
$edit['accounts[' . $account->uid . ']'] = TRUE;
|
||||
$this->drupalPost('admin/people', $edit, t('Update'));
|
||||
$account = user_load($user_c->uid, TRUE);
|
||||
|
@ -81,7 +81,7 @@ class UserAdminTest extends WebTestBase {
|
|||
|
||||
// Test unblocking of a user from /admin/people page and sending of activation mail
|
||||
$editunblock = array();
|
||||
$editunblock['operation'] = 'unblock';
|
||||
$editunblock['operation'] = 'user_unblock_user_action';
|
||||
$editunblock['accounts[' . $account->uid . ']'] = TRUE;
|
||||
$this->drupalPost('admin/people', $editunblock, t('Update'));
|
||||
$account = user_load($user_c->uid, TRUE);
|
||||
|
|
|
@ -88,7 +88,7 @@ class UserCancelTest extends WebTestBase {
|
|||
$this->admin_user = $this->drupalCreateUser(array('administer users'));
|
||||
$this->drupalLogin($this->admin_user);
|
||||
$edit = array(
|
||||
'operation' => 'cancel',
|
||||
'operation' => 'user_cancel_user_action',
|
||||
'accounts[1]' => TRUE,
|
||||
);
|
||||
$this->drupalPost('admin/people', $edit, t('Update'));
|
||||
|
@ -408,7 +408,7 @@ class UserCancelTest extends WebTestBase {
|
|||
|
||||
// Cancel user accounts, including own one.
|
||||
$edit = array();
|
||||
$edit['operation'] = 'cancel';
|
||||
$edit['operation'] = 'user_cancel_user_action';
|
||||
foreach ($users as $uid => $account) {
|
||||
$edit['accounts[' . $uid . ']'] = TRUE;
|
||||
}
|
||||
|
|
|
@ -190,12 +190,13 @@ function user_admin_account() {
|
|||
'#attributes' => array('class' => array('container-inline')),
|
||||
);
|
||||
$options = array();
|
||||
foreach (module_invoke_all('user_operations') as $operation => $array) {
|
||||
$options[$operation] = $array['label'];
|
||||
$actions = entity_load_multiple_by_properties('action', array('type' => 'user'));
|
||||
foreach ($actions as $id => $action) {
|
||||
$options[$id] = $action->label();
|
||||
}
|
||||
$form['options']['operation'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Operation'),
|
||||
'#title' => t('Action'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $options,
|
||||
'#default_value' => 'unblock',
|
||||
|
@ -263,20 +264,13 @@ function user_admin_account() {
|
|||
* Submit the user administration update form.
|
||||
*/
|
||||
function user_admin_account_submit($form, &$form_state) {
|
||||
$operations = module_invoke_all('user_operations', $form, $form_state);
|
||||
$operation = $operations[$form_state['values']['operation']];
|
||||
// Filter out unchecked accounts.
|
||||
$accounts = array_filter($form_state['values']['accounts']);
|
||||
if ($function = $operation['callback']) {
|
||||
// Add in callback arguments if present.
|
||||
if (isset($operation['callback arguments'])) {
|
||||
$args = array_merge(array($accounts), $operation['callback arguments']);
|
||||
if ($action = entity_load('action', $form_state['values']['operation'])) {
|
||||
$accounts = entity_load_multiple('user', array_filter($form_state['values']['accounts']));
|
||||
$action->execute($accounts);
|
||||
$operation_definition = $action->getPluginDefinition();
|
||||
if (!empty($operation_definition['confirm_form_path'])) {
|
||||
$form_state['redirect'] = $operation_definition['confirm_form_path'];
|
||||
}
|
||||
else {
|
||||
$args = array($accounts);
|
||||
}
|
||||
call_user_func_array($function, $args);
|
||||
|
||||
drupal_set_message(t('The update has been performed.'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -205,40 +205,6 @@ function hook_user_format_name_alter(&$name, $account) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add mass user operations.
|
||||
*
|
||||
* This hook enables modules to inject custom operations into the mass operations
|
||||
* dropdown found at admin/people, by associating a callback function with
|
||||
* the operation, which is called when the form is submitted. The callback function
|
||||
* receives one initial argument, which is an array of the checked users.
|
||||
*
|
||||
* @return
|
||||
* An array of operations. Each operation is an associative array that may
|
||||
* contain the following key-value pairs:
|
||||
* - "label": Required. The label for the operation, displayed in the dropdown menu.
|
||||
* - "callback": Required. The function to call for the operation.
|
||||
* - "callback arguments": Optional. An array of additional arguments to pass to
|
||||
* the callback function.
|
||||
*
|
||||
*/
|
||||
function hook_user_operations() {
|
||||
$operations = array(
|
||||
'unblock' => array(
|
||||
'label' => t('Unblock the selected users'),
|
||||
'callback' => 'user_user_operations_unblock',
|
||||
),
|
||||
'block' => array(
|
||||
'label' => t('Block the selected users'),
|
||||
'callback' => 'user_user_operations_block',
|
||||
),
|
||||
'cancel' => array(
|
||||
'label' => t('Cancel the selected user accounts'),
|
||||
),
|
||||
);
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Act on a user account being inserted or updated.
|
||||
*
|
||||
|
|
|
@ -9,6 +9,7 @@ use Drupal\file\Plugin\Core\Entity\File;
|
|||
use Drupal\user\Plugin\Core\Entity\User;
|
||||
use Drupal\user\UserInterface;
|
||||
use Drupal\user\UserRole;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Drupal\menu_link\Plugin\Core\Entity\MenuLink;
|
||||
|
@ -986,6 +987,13 @@ function user_menu() {
|
|||
'access arguments' => array('administer users'),
|
||||
'type' => MENU_LOCAL_ACTION,
|
||||
);
|
||||
$items['admin/people/cancel'] = array(
|
||||
'title' => 'Cancel user',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('user_multiple_cancel_confirm'),
|
||||
'access arguments' => array('administer users'),
|
||||
'file' => 'user.admin.inc',
|
||||
);
|
||||
|
||||
// Administration pages.
|
||||
$items['admin/config/people'] = array(
|
||||
|
@ -1832,6 +1840,55 @@ function user_role_names($membersonly = FALSE, $permission = NULL) {
|
|||
}, user_roles($membersonly, $permission));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_user_role_insert().
|
||||
*/
|
||||
function user_user_role_insert(RoleInterface $role) {
|
||||
// Ignore the authenticated and anonymous roles.
|
||||
if (in_array($role->id(), array(DRUPAL_AUTHENTICATED_RID, DRUPAL_ANONYMOUS_RID))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$action = entity_create('action', array(
|
||||
'id' => 'user_add_role_action.' . $role->id(),
|
||||
'type' => 'user',
|
||||
'label' => t('Add the @label role to the selected users', array('@label' => $role->label())),
|
||||
'configuration' => array(
|
||||
'rid' => $role->id(),
|
||||
),
|
||||
'plugin' => 'user_add_role_action',
|
||||
));
|
||||
$action->save();
|
||||
$action = entity_create('action', array(
|
||||
'id' => 'user_remove_role_action.' . $role->id(),
|
||||
'type' => 'user',
|
||||
'label' => t('Remove the @label role from the selected users', array('@label' => $role->label())),
|
||||
'configuration' => array(
|
||||
'rid' => $role->id(),
|
||||
),
|
||||
'plugin' => 'user_remove_role_action',
|
||||
));
|
||||
$action->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_user_role_delete().
|
||||
*/
|
||||
function user_user_role_delete(RoleInterface $role) {
|
||||
// Ignore the authenticated and anonymous roles.
|
||||
if (in_array($role->id(), array(DRUPAL_AUTHENTICATED_RID, DRUPAL_ANONYMOUS_RID))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$actions = entity_load_multiple('action', array(
|
||||
'user_add_role_action.' . $role->id(),
|
||||
'user_remove_role_action.' . $role->id(),
|
||||
));
|
||||
foreach ($actions as $action) {
|
||||
$action->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an array of roles matching specified conditions.
|
||||
*
|
||||
|
@ -2005,149 +2062,14 @@ function user_role_revoke_permissions($rid, array $permissions = array()) {
|
|||
drupal_static_reset('user_role_permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_user_operations().
|
||||
*/
|
||||
function user_user_operations($form = array(), $form_state = array()) {
|
||||
$operations = array(
|
||||
'unblock' => array(
|
||||
'label' => t('Unblock the selected users'),
|
||||
'callback' => 'user_user_operations_unblock',
|
||||
),
|
||||
'block' => array(
|
||||
'label' => t('Block the selected users'),
|
||||
'callback' => 'user_user_operations_block',
|
||||
),
|
||||
'cancel' => array(
|
||||
'label' => t('Cancel the selected user accounts'),
|
||||
),
|
||||
);
|
||||
|
||||
if (user_access('administer permissions')) {
|
||||
$roles = user_role_names(TRUE);
|
||||
unset($roles[DRUPAL_AUTHENTICATED_RID]); // Can't edit authenticated role.
|
||||
|
||||
$add_roles = array();
|
||||
foreach ($roles as $key => $value) {
|
||||
$add_roles['add_role-' . $key] = $value;
|
||||
}
|
||||
|
||||
$remove_roles = array();
|
||||
foreach ($roles as $key => $value) {
|
||||
$remove_roles['remove_role-' . $key] = $value;
|
||||
}
|
||||
|
||||
if (count($roles)) {
|
||||
$role_operations = array(
|
||||
t('Add a role to the selected users') => array(
|
||||
'label' => $add_roles,
|
||||
),
|
||||
t('Remove a role from the selected users') => array(
|
||||
'label' => $remove_roles,
|
||||
),
|
||||
);
|
||||
|
||||
$operations += $role_operations;
|
||||
}
|
||||
}
|
||||
|
||||
// If the form has been posted, we need to insert the proper data for
|
||||
// role editing if necessary.
|
||||
if (!empty($form_state['submitted'])) {
|
||||
$operation_rid = explode('-', $form_state['values']['operation']);
|
||||
$operation = $operation_rid[0];
|
||||
if ($operation == 'add_role' || $operation == 'remove_role') {
|
||||
$rid = $operation_rid[1];
|
||||
if (user_access('administer permissions')) {
|
||||
$operations[$form_state['values']['operation']] = array(
|
||||
'callback' => 'user_multiple_role_edit',
|
||||
'callback arguments' => array($operation, $rid),
|
||||
);
|
||||
}
|
||||
else {
|
||||
watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for admin mass unblocking users.
|
||||
*/
|
||||
function user_user_operations_unblock($accounts) {
|
||||
$accounts = user_load_multiple($accounts);
|
||||
foreach ($accounts as $account) {
|
||||
// Skip unblocking user if they are already unblocked.
|
||||
if ($account !== FALSE && $account->status == 0) {
|
||||
$account->status = 1;
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for admin mass blocking users.
|
||||
*/
|
||||
function user_user_operations_block($accounts) {
|
||||
$accounts = user_load_multiple($accounts);
|
||||
foreach ($accounts as $account) {
|
||||
// Skip blocking user if they are already blocked.
|
||||
if ($account !== FALSE && $account->status == 1) {
|
||||
// For efficiency manually save the original account before applying any
|
||||
// changes.
|
||||
$account->original = clone $account;
|
||||
$account->status = 0;
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for admin mass adding/deleting a user role.
|
||||
*/
|
||||
function user_multiple_role_edit($accounts, $operation, $rid) {
|
||||
$role_name = entity_load('user_role', $rid)->label();
|
||||
|
||||
switch ($operation) {
|
||||
case 'add_role':
|
||||
$accounts = user_load_multiple($accounts);
|
||||
foreach ($accounts as $account) {
|
||||
// Skip adding the role to the user if they already have it.
|
||||
if ($account !== FALSE && !in_array($rid, $account->roles)) {
|
||||
// For efficiency manually save the original account before applying
|
||||
// any changes.
|
||||
$account->original = clone $account;
|
||||
$account->roles[] = $rid;
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'remove_role':
|
||||
$accounts = user_load_multiple($accounts);
|
||||
foreach ($accounts as $account) {
|
||||
// Skip removing the role from the user if they already don't have it.
|
||||
if ($account !== FALSE && in_array($rid, $account->roles)) {
|
||||
$roles = array_diff($account->roles, array($rid));
|
||||
// For efficiency manually save the original account before applying
|
||||
// any changes.
|
||||
$account->original = clone $account;
|
||||
$account->roles = $roles;
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function user_multiple_cancel_confirm($form, &$form_state) {
|
||||
$edit = $form_state['input'];
|
||||
|
||||
// Retrieve the accounts to be canceled from the temp store.
|
||||
$accounts = Drupal::service('user.tempstore')->get('user_user_operations_cancel')->get($GLOBALS['user']->uid);
|
||||
$form['accounts'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
|
||||
$accounts = user_load_multiple(array_keys(array_filter($edit['accounts'])));
|
||||
foreach ($accounts as $uid => $account) {
|
||||
foreach ($accounts as $account) {
|
||||
$uid = $account->id();
|
||||
// Prevent user 1 from being canceled.
|
||||
if ($uid <= 1) {
|
||||
continue;
|
||||
|
@ -2156,14 +2078,14 @@ function user_multiple_cancel_confirm($form, &$form_state) {
|
|||
'#type' => 'hidden',
|
||||
'#value' => $uid,
|
||||
'#prefix' => '<li>',
|
||||
'#suffix' => check_plain($account->name) . "</li>\n",
|
||||
'#suffix' => check_plain($account->name->value) . "</li>\n",
|
||||
);
|
||||
}
|
||||
|
||||
// Output a notice that user 1 cannot be canceled.
|
||||
if (isset($accounts[1])) {
|
||||
$redirect = (count($accounts) == 1);
|
||||
$message = t('The user account %name cannot be cancelled.', array('%name' => $accounts[1]->name));
|
||||
$message = t('The user account %name cannot be cancelled.', array('%name' => $accounts[1]->name->value));
|
||||
drupal_set_message($message, $redirect ? 'error' : 'warning');
|
||||
// If only user 1 was selected, redirect to the overview.
|
||||
if ($redirect) {
|
||||
|
@ -2211,6 +2133,8 @@ function user_multiple_cancel_confirm($form, &$form_state) {
|
|||
function user_multiple_cancel_confirm_submit($form, &$form_state) {
|
||||
global $user;
|
||||
|
||||
// Clear out the accounts from the temp store.
|
||||
Drupal::service('user.tempstore')->get('user_user_operations_cancel')->delete($user->uid);
|
||||
if ($form_state['values']['confirm']) {
|
||||
foreach ($form_state['values']['accounts'] as $uid => $value) {
|
||||
// Prevent programmatic form submissions from cancelling user 1.
|
||||
|
@ -2509,44 +2433,6 @@ function user_node_load($nodes, $types) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_action_info().
|
||||
*/
|
||||
function user_action_info() {
|
||||
return array(
|
||||
'user_block_user_action' => array(
|
||||
'label' => t('Block current user'),
|
||||
'type' => 'user',
|
||||
'configurable' => FALSE,
|
||||
'triggers' => array('any'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks the current user.
|
||||
*
|
||||
* @ingroup actions
|
||||
*/
|
||||
function user_block_user_action(&$entity, $context = array()) {
|
||||
// First priority: If there is a $entity->uid, block that user.
|
||||
// This is most likely a user object or the author if a node or comment.
|
||||
if (isset($entity->uid)) {
|
||||
$uid = $entity->uid;
|
||||
}
|
||||
elseif (isset($context['uid'])) {
|
||||
$uid = $context['uid'];
|
||||
}
|
||||
// If neither of those are valid, then block the current user.
|
||||
else {
|
||||
$uid = $GLOBALS['user']->uid;
|
||||
}
|
||||
$account = user_load($uid);
|
||||
$account->status = 0;
|
||||
$account->save();
|
||||
watchdog('action', 'Blocked user %name.', array('%name' => $account->name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for 'field_ui_field_instance_edit_form'.
|
||||
*
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
namespace Drupal\views\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerBase;
|
||||
use Drupal\Component\Plugin\Factory\DefaultFactory;
|
||||
use Drupal\Core\Plugin\Factory\ContainerFactory;
|
||||
use Drupal\Core\Plugin\Discovery\CacheDecorator;
|
||||
use Drupal\views\Plugin\Discovery\ViewsHandlerDiscovery;
|
||||
|
||||
|
@ -30,7 +30,7 @@ class ViewsHandlerManager extends PluginManagerBase {
|
|||
$this->discovery = new ViewsHandlerDiscovery($type, $namespaces);
|
||||
$this->discovery = new CacheDecorator($this->discovery, "views:$type", 'views_info');
|
||||
|
||||
$this->factory = new DefaultFactory($this->discovery);
|
||||
$this->factory = new ContainerFactory($this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue