-
Notifications
You must be signed in to change notification settings - Fork 2
/
conductor.module
359 lines (327 loc) · 10.2 KB
/
conductor.module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
<?php
/**
* @file
* conductor.module provides a workflow editor with a GUI (or it will).
*/
/**
* Implements hook_menu().
*/
function conductor_menu() {
$items = array();
$items['conductor/ajax'] = array(
'title' => 'Conductor',
'description' => 'Ajax callback for workflow manipulation.',
'page callback' => 'conductor_ajax',
'theme callback' => 'ajax_base_page_theme',
'delivery callback' => 'ajax_deliver',
'access arguments' => array('administer conductor workflows'),
'type' => MENU_CALLBACK,
'file' => 'includes/ajax.inc',
);
return $items;
}
/*
* Implements hook_permission().
*/
function conductor_permission() {
return array(
'administer conductor workflows' => array(
'title' => t('Administer workflows'),
'description' => t('Access the conductor workflow administration pages.'),
),
);
}
/**
* Implements hook_theme().
*/
function conductor_theme() {
$theme = array();
ctools_include('plugins');
$activity_plugins = ctools_get_plugins('conductor', 'activity');
foreach ($activity_plugins as $activity_name => $activity_plugin) {
/*
TODO: add the ability to setup custom rendering for each of your plugins.
$theme['conductor_activity_' . $activity_name] = array(
);
*/
}
return $theme;
}
/**
* Implements hook_ctools_plugin_api().
*/
function conductor_ctools_plugin_api($owner, $api) {
if ($owner == 'conductor' && ($api == 'plugins' || $api == 'workflow')) {
return array('version' => 1.0);
}
}
/**
* Implements hook_ctools_plugin_type().
*/
function conductor_ctools_plugin_type() {
return array(
'activity' => array(
'process' => 'conductor_activity_process',
'child plugins' => TRUE,
'classes' => array('handler'),
),
'storage' => array(
'child plugins' => TRUE,
'classes' => array('handler'),
),
);
}
/**
* Ensure an activity has a minimal set of data.
*/
function conductor_activity_process(&$plugin) {
$plugin += array(
'category' => t('Miscellaneous'),
'description' => '',
'conductor_ui_template' => 'conductor_activity',
'conductur_ui_js_object' => 'activity',
);
foreach (module_implements('conductor_activity_process') as $module) {
// Enable other modules to modify the plugin data.
$function = $module . '_conductor_activity_process';
$function($plugin);
}
drupal_alter('conductor_activity_process', $plugin);
}
/**
* Implements hook_ctools_plugin_directory().
*
* @param string $owner
* The system name of the module owning the plugin type for which a base
* directory location is being requested.
* @param string $plugin_type
* The name of the plugin type for which a base directory is being requested.
* @return string
* The path where CTools' plugin system should search for plugin files,
* relative to your module's root. Omit leading and trailing slashes.
*/
function conductor_ctools_plugin_directory($owner, $plugin_type) {
if ($owner == 'conductor') {
return "plugins/$plugin_type";
}
}
/**
* Load a conductor activity handler.
*
* @param $activity
* A string representing the name of the actiivty plugin.
*/
function conductor_get_activity_handler($name) {
ctools_include('plugins');
$activity_plugin = ctools_get_plugins('conductor', 'activity', $name);
$class_name = ctools_plugin_get_class($activity_plugin, 'handler');
$handler = FALSE;
if ($class_name != FALSE) {
// TODO: plugin_info should probably be handed in to the constructor as configuration...
$handler = new $class_name;
$handler->plugin_info = $activity_plugin;
}
return $handler;
}
/**
* Load a conductor state storage handler.
*
* @param $name
* A string representing the name of the storage plugin.
*/
function conductor_get_storage_handler($name) {
ctools_include('plugins');
$storage_plugin = ctools_get_plugins('conductor', 'storage', $name);
$class_name = ctools_plugin_get_class($storage_plugin, 'handler');
$handler = FALSE;
if ($class_name != FALSE) {
$handler = new $class_name;
$handler->plugin_info = $storage_plugin;
}
return $handler;
}
/**
* Menu callback to load a workflow via AJAX.
*/
function conductor_ajax() {
if (isset($_REQUEST['conductor_name'])) {
}
}
/**
* Get a workflow from the database or from default workflows.
*
* This function is just a static wrapper around workflows::load(). This function
* isn't called 'workflows_load()' primarily because it might get a workflow
* from the default workflows which aren't technically loaded from the database.
*
* @param $name
* The name of the workflow.
* @param $reset
* If TRUE, reset this entry in the load cache.
* @return workflow
* A reference to the $workflow object. Use $reset if you're sure you want
* a fresh one.
*/
function conductor_get_workflow($name, $reset = FALSE) {
$workflow = FALSE;
$cache = &drupal_static('ctools_export_load_object');
if ($reset) {
unset($cache['conductor_workflow'][$name]);
cache_clear_all('conductor_workflow_' . $name);
}
if (isset($cache['conductor_workflow'][$name])) {
$workflow = $cache['conductor_workflow'][$name];
}
else if ($cached_data = cache_get('conductor_workflow_' . $name)) {
$workflow = $cached_data->data;
$cache['conductor_workflow'][$name] = $workflow;
}
if (!$workflow) {
ctools_include('export');
$workflow = ctools_export_crud_load('conductor_workflow', $name);
if ($workflow) {
cache_set('conductor_workflow_' . $name, $workflow);
$workflow->indexActivities();
}
}
if ($workflow) {
$workflow = clone $workflow;
}
// Return a clone of the workflow to ensure that we never inadvertantly cache a
return $workflow;
}
/**
* Load a single workflow.
*/
function conductor_load_workflow($name) {
ctools_include('export');
$result = ctools_export_load_object('conductor_workflow', 'names', array($name));
if (isset($result[$name])) {
return $result[$name]->cloneWorkflow();
}
}
/**
* Save a single workflow.
*/
function conductor_save_workflow(ConductorWorkflow &$workflow) {
cache_clear_all('conductor_workflow_' . $workflow->name);
return $workflow->save();
}
/**
* Delete a single workflow.
*/
function conductor_delete_workflow(ConductorWorkflow &$workflow) {
$workflow->destroy();
return $workflow->delete();
}
/**
* Export CRUD callback to export a workflow.
*/
function conductor_export_workflow(ConductorWorkflow &$workflow, $indent = '') {
return $workflow->export($indent);
}
/**
* Create an empty workflow to work with.
*
* This function simply instantiates a new ConductorWorkflow, populating it
* with the `start` and `end` workflow activities required by all Conductor
* workflows for processing.
*
* @return ConductorWorkflow
* A fully formed, empty $workflow object.
*/
function conductor_new_workflow() {
$workflow = new ConductorWorkflow();
$workflow->wid = 'new';
$activity = $workflow->newActivity('start');
$activity->name = 'start';
$activity->title = t('Start');
$activity->x = 100;
$activity->y = 100;
$activity = $workflow->newActivity('end');
$activity->name = 'end';
$activity->title = t('End');
$activity->x = 300;
$activity->y = 100;
return $workflow;
}
/**
* Start an instance of a workflow.
*
* @param $name
* The name of the workflow to start.
* @param $options
* An array of options passed to conductor_create_workflow_instace().
*/
function conductor_start_workflow($name, $options = array()) {
$workflow = conductor_create_workflow_instance($name, $options);
return $workflow->run();
}
/**
* Factory function for workflow instances.
*
* A workflow instance is an
*
* Dependency injection is expected to keep Conductor flexible
* and to allow us to extend it unforseen ways. This may have
* gotten out of control though.
*
* @param $options
* An array of options used for dependency injection to configure
* a create the desired workflow instance.
* - "context": An associative array of context information handed
* into the first activity.
* - "observers": TODO: document.
* - "state": a ConductorState descended object to be used as the workflow
* state for this instance.
* - "activity_state": TODO: document.
*/
function conductor_create_workflow_instance($name, $options = array()) {
// Get the workflow object.
$workflow = conductor_get_workflow($name);
// If a state object has been provided, use it.
if (isset($options['state'])) {
$workflow->setState($options['state']);
}
// If no state object has been created the workflow will instantiate the default.
$workflowState = $workflow->getState();
$startActivity = $workflow->getFirstActivity();
// If an activity state object has been provided, use it.
if (isset($options['activity_state'])) {
$startActivity->setState($options['activity_state']);
}
$startState = $startActivity->getState();
// If we have a context with which to start this workflow, set it.
if (isset($options['context'])) {
$startState->context = $options['context'];
}
// Set the activity state on the workflow.
$workflowState->setActivityState($startState);
// Get the first activity, we may need it to set the initial context.
// TODO: Decide if workflow state should instantiate the activity state? how could we inject the dependency?
// NOTE: it would prevent the call to setActivityState.
//$startState = $workflowState->getActivityState($startActivity->name);
// Register any necessary observers for workflow processing.
// TODO: Load the default observers if any are configured when this is blank.
if (isset($options['observers'])) {
foreach ($options['observers'] as $observer) {
$workflow->state->registerObserver($observer);
}
}
return $workflow;
}
/**
* TODO: recieve context here?
*/
function conductor_load_workflow_instance_from_pointer($pointer_key) {
// TODO: Allow this to be overridden with options.
$storage = conductor_get_storage_handler(variable_get('conductor_default_storage_handler', 'database'));
if ($pointer = $storage->loadPointer($pointer_key)) {
$workflow = conductor_get_workflow($pointer['workflowName']);
$state = $workflow->getState()->loadFromUniqueId($pointer['instanceId']);
}
else {
$workflow = FALSE;
}
return $workflow;
}