Skip to content

Commit c6737f1

Browse files
committed
Issue #1008166 by sun, catch, rootatwc, Rob Loach, Jody Lynn: Actions should be a module.
0 parents  commit c6737f1

File tree

10 files changed

+1410
-0
lines changed

10 files changed

+1410
-0
lines changed

action.admin.inc

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
<?php
2+
3+
/**
4+
* @file
5+
* Admin page callbacks for the Action module.
6+
*/
7+
8+
/**
9+
* Menu callback; Displays an overview of available and configured actions.
10+
*/
11+
function action_admin_manage() {
12+
action_synchronize();
13+
$actions = action_list();
14+
$actions_map = action_actions_map($actions);
15+
$options = array();
16+
$unconfigurable = array();
17+
18+
foreach ($actions_map as $key => $array) {
19+
if ($array['configurable']) {
20+
$options[$key] = $array['label'] . '...';
21+
}
22+
else {
23+
$unconfigurable[] = $array;
24+
}
25+
}
26+
27+
$row = array();
28+
$instances_present = db_query("SELECT aid FROM {actions} WHERE parameters <> ''")->fetchField();
29+
$header = array(
30+
array('data' => t('Action type'), 'field' => 'type'),
31+
array('data' => t('Label'), 'field' => 'label'),
32+
array('data' => $instances_present ? t('Operations') : '', 'colspan' => '2')
33+
);
34+
$query = db_select('actions')
35+
->extend('Drupal\Core\Database\Query\PagerSelectExtender')
36+
->extend('Drupal\Core\Database\Query\TableSortExtender');
37+
$result = $query
38+
->fields('actions')
39+
->limit(50)
40+
->orderByHeader($header)
41+
->execute();
42+
43+
foreach ($result as $action) {
44+
$row[] = array(
45+
array('data' => $action->type),
46+
array('data' => check_plain($action->label)),
47+
array('data' => $action->parameters ? l(t('configure'), "admin/config/system/actions/configure/$action->aid") : ''),
48+
array('data' => $action->parameters ? l(t('delete'), "admin/config/system/actions/delete/$action->aid") : '')
49+
);
50+
}
51+
52+
if ($row) {
53+
$pager = theme('pager');
54+
if (!empty($pager)) {
55+
$row[] = array(array('data' => $pager, 'colspan' => '3'));
56+
}
57+
$build['action_header'] = array('#markup' => '<h3>' . t('Available actions:') . '</h3>');
58+
$build['action_table'] = array('#markup' => theme('table', array('header' => $header, 'rows' => $row)));
59+
}
60+
61+
if ($actions_map) {
62+
$build['action_admin_manage_form'] = drupal_get_form('action_admin_manage_form', $options);
63+
}
64+
65+
return $build;
66+
}
67+
68+
/**
69+
* Define the form for the actions overview page.
70+
*
71+
* @param $form_state
72+
* An associative array containing the current state of the form; not used.
73+
* @param $options
74+
* An array of configurable actions.
75+
* @return
76+
* Form definition.
77+
*
78+
* @ingroup forms
79+
* @see action_admin_manage_form_submit()
80+
*/
81+
function action_admin_manage_form($form, &$form_state, $options = array()) {
82+
$form['parent'] = array(
83+
'#type' => 'fieldset',
84+
'#title' => t('Create an advanced action'),
85+
'#attributes' => array('class' => array('container-inline')),
86+
);
87+
$form['parent']['action'] = array(
88+
'#type' => 'select',
89+
'#title' => t('Action'),
90+
'#title_display' => 'invisible',
91+
'#options' => $options,
92+
'#empty_option' => t('Choose an advanced action'),
93+
);
94+
$form['parent']['actions'] = array('#type' => 'actions');
95+
$form['parent']['actions']['submit'] = array(
96+
'#type' => 'submit',
97+
'#value' => t('Create'),
98+
);
99+
return $form;
100+
}
101+
102+
/**
103+
* Form submission handler for action_admin_manage_form().
104+
*/
105+
function action_admin_manage_form_submit($form, &$form_state) {
106+
if ($form_state['values']['action']) {
107+
$form_state['redirect'] = 'admin/config/system/actions/configure/' . $form_state['values']['action'];
108+
}
109+
}
110+
111+
/**
112+
* Form constructor for the configuration of a single action.
113+
*
114+
* We provide the "Description" field. The rest of the form is provided by the
115+
* action. We then provide the Save button. Because we are combining unknown
116+
* form elements with the action configuration form, we use an 'action_' prefix
117+
* on our elements.
118+
*
119+
* @param $action
120+
* Hash of an action ID or an integer. If it is a hash, we are
121+
* creating a new instance. If it is an integer, we are editing an existing
122+
* instance.
123+
*
124+
* @see action_admin_configure_validate()
125+
* @see action_admin_configure_submit()
126+
* @ingroup forms
127+
*/
128+
function action_admin_configure($form, &$form_state, $action = NULL) {
129+
if ($action === NULL) {
130+
drupal_goto('admin/config/system/actions');
131+
}
132+
133+
$actions_map = action_actions_map(action_list());
134+
$edit = array();
135+
136+
// Numeric action denotes saved instance of a configurable action.
137+
if (is_numeric($action)) {
138+
$aid = $action;
139+
// Load stored parameter values from database.
140+
$data = db_query("SELECT * FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetch();
141+
$edit['action_label'] = $data->label;
142+
$edit['action_type'] = $data->type;
143+
$function = $data->callback;
144+
$action = drupal_hash_base64($data->callback);
145+
$params = unserialize($data->parameters);
146+
if ($params) {
147+
foreach ($params as $name => $val) {
148+
$edit[$name] = $val;
149+
}
150+
}
151+
}
152+
// Otherwise, we are creating a new action instance.
153+
else {
154+
$function = $actions_map[$action]['callback'];
155+
$edit['action_label'] = $actions_map[$action]['label'];
156+
$edit['action_type'] = $actions_map[$action]['type'];
157+
}
158+
159+
$form['action_label'] = array(
160+
'#type' => 'textfield',
161+
'#title' => t('Label'),
162+
'#default_value' => $edit['action_label'],
163+
'#maxlength' => '255',
164+
'#description' => t('A unique label for this advanced action. This label will be displayed in the interface of modules that integrate with actions.'),
165+
'#weight' => -10
166+
);
167+
$action_form = $function . '_form';
168+
$form = array_merge($form, $action_form($edit));
169+
$form['action_type'] = array(
170+
'#type' => 'value',
171+
'#value' => $edit['action_type'],
172+
);
173+
$form['action_action'] = array(
174+
'#type' => 'hidden',
175+
'#value' => $action,
176+
);
177+
// $aid is set when configuring an existing action instance.
178+
if (isset($aid)) {
179+
$form['action_aid'] = array(
180+
'#type' => 'hidden',
181+
'#value' => $aid,
182+
);
183+
}
184+
$form['action_configured'] = array(
185+
'#type' => 'hidden',
186+
'#value' => '1',
187+
);
188+
$form['actions'] = array('#type' => 'actions');
189+
$form['actions']['submit'] = array(
190+
'#type' => 'submit',
191+
'#value' => t('Save'),
192+
'#weight' => 13
193+
);
194+
195+
return $form;
196+
}
197+
198+
/**
199+
* Form validation handler for action_admin_configure().
200+
*
201+
* @see action_admin_configure_submit()
202+
*/
203+
function action_admin_configure_validate($form, &$form_state) {
204+
$function = action_function_lookup($form_state['values']['action_action']) . '_validate';
205+
// Hand off validation to the action.
206+
if (function_exists($function)) {
207+
$function($form, $form_state);
208+
}
209+
}
210+
211+
/**
212+
* Form submission handler for action_admin_configure().
213+
*
214+
* @see action_admin_configure_validate()
215+
*/
216+
function action_admin_configure_submit($form, &$form_state) {
217+
$function = action_function_lookup($form_state['values']['action_action']);
218+
$submit_function = $function . '_submit';
219+
220+
// Action will return keyed array of values to store.
221+
$params = $submit_function($form, $form_state);
222+
$aid = isset($form_state['values']['action_aid']) ? $form_state['values']['action_aid'] : NULL;
223+
224+
action_save($function, $form_state['values']['action_type'], $params, $form_state['values']['action_label'], $aid);
225+
drupal_set_message(t('The action has been successfully saved.'));
226+
227+
$form_state['redirect'] = 'admin/config/system/actions/manage';
228+
}
229+
230+
/**
231+
* Creates the form for confirmation of deleting an action.
232+
*
233+
* @see action_admin_delete_form_submit()
234+
* @ingroup forms
235+
*/
236+
function action_admin_delete_form($form, &$form_state, $action) {
237+
$form['aid'] = array(
238+
'#type' => 'hidden',
239+
'#value' => $action->aid,
240+
);
241+
return confirm_form($form,
242+
t('Are you sure you want to delete the action %action?', array('%action' => $action->label)),
243+
'admin/config/system/actions/manage',
244+
t('This cannot be undone.'),
245+
t('Delete'),
246+
t('Cancel')
247+
);
248+
}
249+
250+
/**
251+
* Form submission handler for action_admin_delete_form().
252+
*/
253+
function action_admin_delete_form_submit($form, &$form_state) {
254+
$aid = $form_state['values']['aid'];
255+
$action = action_load($aid);
256+
action_delete($aid);
257+
watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $aid, '%action' => $action->label));
258+
drupal_set_message(t('Action %action was deleted', array('%action' => $action->label)));
259+
$form_state['redirect'] = 'admin/config/system/actions/manage';
260+
}
261+
262+
/**
263+
* Post-deletion operations for deleting action orphans.
264+
*
265+
* @param $orphaned
266+
* An array of orphaned actions.
267+
*/
268+
function action_admin_delete_orphans_post($orphaned) {
269+
foreach ($orphaned as $callback) {
270+
drupal_set_message(t("Deleted orphaned action (%action).", array('%action' => $callback)));
271+
}
272+
}
273+
274+
/**
275+
* Removes actions that are in the database but not supported by any enabled module.
276+
*/
277+
function action_admin_remove_orphans() {
278+
action_synchronize(TRUE);
279+
drupal_goto('admin/config/system/actions/manage');
280+
}

action.api.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
/**
4+
* @file
5+
* Hooks provided by the Actions module.
6+
*/
7+
8+
/**
9+
* Declares information about actions.
10+
*
11+
* Any module can define actions, and then call actions_do() to make those
12+
* actions happen in response to events.
13+
*
14+
* An action consists of two or three parts:
15+
* - an action definition (returned by this hook)
16+
* - a function which performs the action (which by convention is named
17+
* MODULE_description-of-function_action)
18+
* - an optional form definition function that defines a configuration form
19+
* (which has the name of the action function with '_form' appended to it.)
20+
*
21+
* The action function takes two to four arguments, which come from the input
22+
* arguments to actions_do().
23+
*
24+
* @return
25+
* An associative array of action descriptions. The keys of the array
26+
* are the names of the action functions, and each corresponding value
27+
* is an associative array with the following key-value pairs:
28+
* - 'type': The type of object this action acts upon. Core actions have types
29+
* 'node', 'user', 'comment', and 'system'.
30+
* - 'label': The human-readable name of the action, which should be passed
31+
* through the t() function for translation.
32+
* - 'configurable': If FALSE, then the action doesn't require any extra
33+
* configuration. If TRUE, then your module must define a form function with
34+
* the same name as the action function with '_form' appended (e.g., the
35+
* form for 'node_assign_owner_action' is 'node_assign_owner_action_form'.)
36+
* This function takes $context as its only parameter, and is paired with
37+
* the usual _submit function, and possibly a _validate function.
38+
* - 'triggers': An array of the events (that is, hooks) that can trigger this
39+
* action. For example: array('node_insert', 'user_update'). You can also
40+
* declare support for any trigger by returning array('any') for this value.
41+
* - 'behavior': (optional) A machine-readable array of behaviors of this
42+
* action, used to signal additionally required actions that may need to be
43+
* triggered. Modules that are processing actions should take special care
44+
* for the "presave" hook, in which case a dependent "save" action should
45+
* NOT be invoked.
46+
*
47+
* @ingroup actions
48+
*/
49+
function hook_action_info() {
50+
return array(
51+
'comment_unpublish_action' => array(
52+
'type' => 'comment',
53+
'label' => t('Unpublish comment'),
54+
'configurable' => FALSE,
55+
'behavior' => array('changes_property'),
56+
'triggers' => array('comment_presave', 'comment_insert', 'comment_update'),
57+
),
58+
'comment_unpublish_by_keyword_action' => array(
59+
'type' => 'comment',
60+
'label' => t('Unpublish comment containing keyword(s)'),
61+
'configurable' => TRUE,
62+
'behavior' => array('changes_property'),
63+
'triggers' => array('comment_presave', 'comment_insert', 'comment_update'),
64+
),
65+
'comment_save_action' => array(
66+
'type' => 'comment',
67+
'label' => t('Save comment'),
68+
'configurable' => FALSE,
69+
'triggers' => array('comment_insert', 'comment_update'),
70+
),
71+
);
72+
}
73+
74+
/**
75+
* Alters the actions declared by another module.
76+
*
77+
* Called by action_list() to allow modules to alter the return values from
78+
* implementations of hook_action_info().
79+
*
80+
* @ingroup actions
81+
*/
82+
function hook_action_info_alter(&$actions) {
83+
$actions['node_unpublish_action']['label'] = t('Unpublish and remove from public view.');
84+
}
85+
86+
/**
87+
* Executes code after an action is deleted.
88+
*
89+
* @param $aid
90+
* The action ID.
91+
*
92+
* @ingroup actions
93+
*/
94+
function hook_action_delete($aid) {
95+
db_delete('actions_assignments')
96+
->condition('aid', $aid)
97+
->execute();
98+
}

action.info

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name = Actions
2+
description = Perform tasks on specific events triggered within the system.
3+
package = Core
4+
version = VERSION
5+
core = 8.x
6+
configure = admin/config/system/actions

0 commit comments

Comments
 (0)