/[drupal]/contributions/modules/flexifilter/flexifilter.module
ViewVC logotype

Contents of /contributions/modules/flexifilter/flexifilter.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.19 - (show annotations) (download) (as text)
Sun Apr 13 02:35:27 2008 UTC (19 months, 2 weeks ago) by cwgordon7
Branch: MAIN
CVS Tags: HEAD
Changes since 1.18: +21 -10 lines
File MIME type: text/x-php
General cleanup, plus caching options added
1 <?php
2 // $Id: flexifilter.module,v 1.18 2008/04/12 23:25:24 cwgordon7 Exp $
3
4 $path = drupal_get_path('module', 'flexifilter');
5 include_once ($path .'/flexifilter.components.inc');
6 include_once ($path .'/flexifilter.conditions.inc');
7 include_once ($path .'/flexifilter.flexifilters.inc');
8
9 define('FLEXIFILTER_PART_TYPE_COMPONENT', 0);
10 define('FLEXIFILTER_PART_TYPE_CONDITION', 1);
11 define('FLEXIFILTER_PART_TYPE_ROOT', 2);
12
13 define('FLEXIFILTER_MAX_FILTERS', 128);
14
15 /**
16 * Implementation of hook_perm().
17 */
18 function flexifilter_perm() {
19 return array('administer flexifilter');
20 }
21
22 /**
23 * Implementation of hook_menu().
24 */
25 function flexifilter_menu() {
26 $items = array();
27 $items['admin/build/flexifilters'] = array(
28 'title' => 'Flexifilters',
29 'description' => 'Create new flexible input filters without writing any code.',
30 'page callback' => 'drupal_get_form',
31 'access arguments' => array('administer flexifilter'),
32 'page arguments' => array('flexifilter_filter_list_form'),
33 'file' => 'flexifilter.admin.inc',
34 );
35 $items['admin/build/flexifilters/list'] = array(
36 'title' => 'List',
37 'type' => MENU_DEFAULT_LOCAL_TASK,
38 );
39 $items['admin/build/flexifilters/add'] = array(
40 'title' => 'Add new flexifilter',
41 'page callback' => 'drupal_get_form',
42 'page arguments' => array('flexifilter_filter_edit_form'),
43 'type' => MENU_LOCAL_TASK,
44 'weight' => 1,
45 'access arguments' => array('administer flexifilter'),
46 'file' => 'flexifilter.admin.inc',
47 );
48 $items['admin/build/flexifilters/import'] = array(
49 'title' => 'Import a flexifilter',
50 'page callback' => 'drupal_get_form',
51 'page arguments' => array('flexifilter_filter_input_form'),
52 'type' => MENU_LOCAL_TASK,
53 'weight' => 2,
54 'access arguments' => array('administer flexifilter'),
55 'file' => 'flexifilter.admin.inc',
56 );
57 $items['admin/build/flexifilters/defaults'] = array(
58 'title' => 'Load a default flexifilter',
59 'page callback' => 'drupal_get_form',
60 'page arguments' => array('flexifilter_filter_default_form'),
61 'type' => MENU_LOCAL_TASK,
62 'weight' => 3,
63 'access arguments' => array('administer flexifilter'),
64 'file' => 'flexifilter.admin.inc',
65 );
66 $items['admin/build/flexifilters/%flexifilter'] = array(
67 'title callback' => 'flexifilter_get_field',
68 'title arguments' => array(3, 'label'),
69 'type' => MENU_CALLBACK,
70 'page callback' => 'drupal_get_form',
71 'page arguments' => array('flexifilter_filter_edit_form', 3),
72 'access arguments' => array('administer flexifilter'),
73 'file' => 'flexifilter.admin.inc',
74 );
75 $items['admin/build/flexifilters/%flexifilter/edit'] = array(
76 'type' => MENU_DEFAULT_LOCAL_TASK,
77 'access arguments' => array('administer flexifilter'),
78 'title' => 'Edit',
79 );
80 $items['admin/build/flexifilters/%flexifilter/export'] = array(
81 'title' => 'Export',
82 'type' => MENU_LOCAL_TASK,
83 'page callback' => 'drupal_get_form',
84 'page arguments' => array('flexifilter_filter_export_form', 3),
85 'access arguments' => array('administer flexifilter'),
86 'file' => 'flexifilter.admin.inc',
87 );
88 $items['admin/build/flexifilters/%flexifilter/preview'] = array(
89 'title' => 'Preview',
90 'type' => MENU_LOCAL_TASK,
91 'page callback' => 'drupal_get_form',
92 'page arguments' => array('flexifilter_filter_preview_form', 3),
93 'access arguments' => array('administer flexifilter'),
94 'file' => 'flexifilter.admin.inc',
95 );
96 $items['admin/build/flexifilters/%flexifilter/delete'] = array(
97 'title' => 'Delete',
98 'type' => MENU_CALLBACK,
99 'page callback' => 'drupal_get_form',
100 'page arguments' => array('flexifilter_filter_delete_form', 3),
101 'access arguments' => array('administer flexifilter'),
102 'file' => 'flexifilter.admin.inc',
103 );
104 $items['admin/build/flexifilters/%flexifilter/enable'] = array(
105 'title' => 'Enable',
106 'type' => MENU_CALLBACK,
107 'page callback' => 'drupal_get_form',
108 'page arguments' => array('flexifilter_filter_enable_form', 3),
109 'access arguments' => array('administer flexifilter'),
110 'file' => 'flexifilter.admin.inc',
111 );
112 $items['admin/build/flexifilters/%flexifilter/disable'] = array(
113 'title' => 'Disable',
114 'type' => MENU_CALLBACK,
115 'page callback' => 'drupal_get_form',
116 'page arguments' => array('flexifilter_filter_disable_form', 3),
117 'access arguments' => array('administer flexifilter'),
118 'file' => 'flexifilter.admin.inc',
119 );
120 return $items;
121 }
122 function flexifilter_init() { menu_rebuild(); }
123 /**
124 * Implementation of hook_help()
125 */
126 function flexifilter_help($path, $arg) {
127 switch ($path) {
128 case 'admin/help#flexifilter':
129 $output = '<p>'. t('Flexifilters are flexible filters that can be used by <a href="@input_formats">Input Formats</a> to filter created content.', array('@input_formats' => url('admin/settings/filters'))) .'</p>';
130 //$output .= '<p>'. t('For more information, see the online handbook entry for <a href="@flexifilter">Flexifilter module</a>.', array('@flexifilter' => 'http://drupal.org/handbook/modules/flexifilter/')) .'</p>';
131 return $output;
132
133 case 'admin/build/flexifilters':
134 return '<p>'. t('Flexifilters are flexible filters that can be used by <a href="@input_formats">Input Formats</a> to filter created content.', array('@input_formats' => url('admin/settings/filters'))) .'</p>';
135
136 case 'admin/build/flexifilters/%/edit':
137 $output = '<p>'. t('Flexifilters are defined in terms of components. A component can range from something simple like "regex replacement" to "invoke other filter" or "while loop". By combining these components, you can create a filter to do what you want. Use the "Add component" dropdown to add a new component to the end of your filter, and then use the "Re/move" dropdown within the component to remove the component, or to move it up/down within the list of components.') .'</p>';
138 $output .= '<p>'. t('Some components (for example, the if/while loops) can have components and conditions within them. In this case, the if/while has a list of components, just like the filter itself has a list of components. The if/while will also have a condition, which controls when the list of components happen.');
139 return $output;
140 }
141 }
142
143 /**
144 * An API function that gets a list of all the flexifilter components present.
145 *
146 * @param $reset Set to TRUE to clear the cache (cache is per-page-request)
147 *
148 * @return An array containing the components. Each component will be a key/value pair in
149 * this array. The key is a unique identifer of the component, also called the component
150 * class. The values are associative arrays containing the following keys:
151 * - label : A human readable name for the component
152 * - description : A human readable description of what the component does
153 * - is_container : TRUE if any of the #contains_ fields are TRUE
154 * - contains_condition : TRUE if the component has a condition associated with it
155 * - contains_components : TRUE if the component has children components
156 * - callback : A callback function which implements the component
157 * - callback_arguments : An array of arguments to pass to the callback function
158 * - group : A human readable name of the group that the component belongs to
159 * - is_advanced : TRUE if the component is only shown in advanced mode
160 */
161 function flexifilter_get_component_list($include_advanced_items = TRUE, $reset = FALSE) {
162 static $cache = array();
163 if (!isset($cache[$include_advanced_items]) || $reset) {
164 $components = module_invoke_all('flexifilter_components');
165 foreach ($components as $key => $component) {
166 if (!isset($component['group'])) {
167 $components[$key]['group'] = t('Other');
168 }
169 if (!isset($component['description'])) {
170 $components[$key]['description'] = '';
171 }
172 if (!isset($component['callback_arguments'])) {
173 $components[$key]['callback_arguments'] = array();
174 }
175 if (!isset($component['contains_condition'])) {
176 $components[$key]['contains_condition'] = FALSE;
177 }
178 if (!isset($component['contains_components'])) {
179 $components[$key]['contains_components'] = FALSE;
180 }
181 if (!isset($component['is_container'])) {
182 $components[$key]['is_container'] = $components[$key]['contains_condition'] || $components[$key]['contains_components'];
183 }
184 if (!isset($component['is_advanced'])) {
185 $components[$key]['is_advanced'] = FALSE;
186 }
187 if (!$include_advanced_items && $components[$key]['is_advanced']) {
188 unset($components[$key]);
189 }
190 }
191 $cache[$include_advanced_items] = $components;
192 }
193 return $cache[$include_advanced_items];
194 }
195
196 /**
197 * An API function that gets a list of all the flexifilter components present.
198 */
199 function flexifilter_get_condition_list($include_advanced_items = TRUE, $reset = FALSE) {
200 static $cache = array();
201 if (!isset($cache[$include_advanced_items]) || $reset) {
202 $conditions = module_invoke_all('flexifilter_conditions');
203 foreach ($conditions as $key => $condition) {
204 if (!isset($condition['group'])) {
205 $conditions[$key]['group'] = t('Other');
206 }
207 if (!isset($condition['description'])) {
208 $conditions[$key]['description'] = '';
209 }
210 if (!isset($condition['callback_arguments'])) {
211 $conditions[$key]['callback_arguments'] = array();
212 }
213 if (!isset($condition['contains_conditions'])) {
214 $conditions[$key]['contains_conditions'] = FALSE;
215 }
216 if (!isset($condition['is_container'])) {
217 $conditions[$key]['is_container'] = $conditions[$key]['contains_conditions'];
218 }
219 if (!isset($condition['is_advanced'])) {
220 $conditions[$key]['is_advanced'] = FALSE;
221 }
222 if (!$include_advanced_items && $conditions[$key]['is_advanced']) {
223 unset($conditions[$key]);
224 }
225 }
226 $cache[$include_advanced_items] = $conditions;
227 }
228 return $cache[$include_advanced_items];
229 }
230
231 /**
232 * Causes a component to run
233 *
234 * @param $component A component (e.g. from flexifilter_get_component_list())
235 * @param $op The operation to run on the component. "settings", "prepare" and "process" are valid operations.
236 * @param $settings The values from the component's settings form (can be an empty array for "settings" operation)
237 * @param $text The text to run the component over (ignored for "settings" operation)
238 *
239 * @return "prepare" and "process" operations return the new text, "settings" returns an FAPI table. For any
240 * operations that the component doesn't support, it will return $text.
241 */
242 function flexifilter_invoke_component($component, $op, $settings = array(), $text = '') {
243 $text = call_user_func_array($component['callback'], array_merge($component['callback_arguments'], array($op, $settings, $text)));
244 $_flexifilter_preview = variable_get('flexifilter_preview', FALSE);
245 if ($_flexifilter_preview) {
246 $_flexifilter_preview_text = variable_get('flexifilter_preview_text', array());
247 $_flexifilter_preview_text[] = array('value' => $text, 'type' => 'component', 'step' => $op, 'class' => $component['label'], 'settings' => $settings);
248 variable_set('flexifilter_preview_text', $_flexifilter_preview_text);
249 }
250 return $text;
251 }
252
253 /**
254 * Causes a condition to run
255 *
256 * @param $data Array containing condition data. Should have at least two keys:
257 * - class : The class name of the component
258 * - settings : Array of settings to pass to the component
259 * @param $op The operation to run on the condition. "settings", "prepare" and "process" are valid operations.
260 * @param $text The text to run the condition over (ignored for "settings" operation)
261 *
262 * @return "prepare" and "process" operations return either TRUE or FALSE, "settings" returns an FAPI table
263 */
264 function flexifilter_invoke_condition($data, $op, $text = '') {
265 // Blank class name is the faux condition "No Condition"
266 if ($data['class'] === '') {
267 if ($op == 'settings') {
268 return array();
269 }
270 else {
271 return FALSE;
272 }
273 }
274
275 // Anything else is a proper condition, so dispatch it
276 $conditions = flexifilter_get_condition_list();
277 $condition = $conditions[$data['class']];
278 $settings = $data['settings'];
279 $return_value = call_user_func_array($condition['callback'], array_merge($condition['callback_arguments'], array($op, $settings, $text)));
280
281 switch ($op) {
282 // For settings, return the value as-is
283
284 case 'settings':
285 return $return_value;
286
287 // For prepare and process, force the return value to TRUE or FALSE
288 case 'prepare':
289 case 'process':
290 if ($return_value) {
291 return TRUE;
292 }
293 else {
294 return FALSE;
295 }
296 }
297 }
298
299 /**
300 * Causes a series of components to run
301 *
302 * @param $components An array containing components. Each value in this array must be an array with the following keys:
303 * - class : The name of a component class (i.e. one of the keys in the flexifilter_get_component_list() array)
304 * - step : If the component can run in either step, should contain "prepare" or "process". Ignored if the component runs in a set step.
305 * - settings : An array of settings for the component, suitable for passing to flexifilter_invoke_component()
306 * @param $op The operation to perform on the components. Should be "prepare" or "process".
307 * @param $text The text to prepare or process
308 *
309 * @return The new text, after preparation / processing
310 */
311 function flexifilter_invoke_components($components, $op, $text) {
312 $component_list = flexifilter_get_component_list();
313 foreach ($components as $key => $component) {
314 if ($key !== 'id_prefix' && $key !== 'id_next') {
315 $class = $component_list[$component['class']];
316 if ($class['step'] == 'both' || $component['settings']['step'] == $op || $class['step'] == $op) {
317 $text = flexifilter_invoke_component($class, $op, $component['settings'], $text);
318 }
319 }
320 }
321 return $text;
322 }
323
324 /**
325 * API function that returns TRUE if the components are involved in the step.
326 *
327 * @param $components
328 * An array of components to check for involvement.
329 * @param $step
330 * The step to check for involvement, either 'process' or 'prepare'.
331 */
332 function flexifilter_components_involve_step($components, $step) {
333 $component_list = flexifilter_get_component_list();
334 foreach ($components as $key => $component) {
335 if ($key !== 'id_prefix' && $key !== 'id_next') {
336 $class = $component_list[$component['class']];
337 // If step is explicity set to the one being tested, then it is involved
338 if ((isset($component['step']) && $component['step'] == $step) || $class['step'] == $step) {
339 return TRUE;
340 }
341 // If step is set to both, then it is involved (unless it is a container)
342 if ($class['is_container'] !== TRUE && $class['step'] == 'both') {
343 return TRUE;
344 }
345 // Finally, if one of its children is involed, then it is
346 if (isset($component['components']) && flexifilter_components_involve_step($component['components'], $step)) {
347 return TRUE;
348 }
349 }
350 }
351 return FALSE;
352 }
353
354 /**
355 * Turns an array of elements into an array of groups containing element labels
356 *
357 * @param $elements An array of elements. Each key/value pair is an element, with the
358 * value being an associative array containing at least label and group.
359 *
360 * @return An array of groups, such that: $returned['element_group']['element_key'] = 'element_label'.
361 */
362 function flexifilter_get_grouped_labels($elements) {
363 $grouped = array();
364
365 foreach ($elements as $name => $element) {
366 $grouped[$element['group']][$name] = $element['label'];
367 }
368 array_map('asort', $grouped);
369 ksort($grouped);
370 return $grouped;
371 }
372
373 /**
374 * Implementation of hook_filter
375 *
376 * Maps flexifilters into the existing filters system. Currently maps $delta to the
377 * flexifilter ID. This will have to change, as the filter system stores delta as a
378 * tinyint, and flexifilter IDs might exceed 127. Possible solutions:
379 * - Limit the number of enabled flexifilters to 128 at any one time (and unlimited disabled ones)
380 * - Expose a single "Flexifilter" filter, and allow it to be configured, setting which flexifilters to use
381 */
382 function flexifilter_filter($op, $delta = 0, $format = -1, $text = '') {
383 switch ($op) {
384 case 'list':
385 $filters = array();
386 foreach (flexifilter_get_filters(FALSE) as $fid => $filter) {
387 if ($filter['enabled']) {
388 $filters[$filter['delta']] = $filter['label'];
389 }
390 }
391 return $filters;
392
393 case 'description':
394 $filter = flexifilter_get_filter_by_delta($delta);
395 return $filter['description'];
396
397 case 'prepare':
398 case 'process':
399 $filter = flexifilter_get_filter_by_delta($delta);
400 return flexifilter_invoke_components($filter['components'], $op, $text);
401
402 case 'no cache':
403 $filter = flexifilter_get_filter_by_delta($delta);
404 return !($filter['cache']);
405
406 default:
407 return $text;
408 }
409 }
410
411 /**
412 * Implementation of hook_filter_tips.
413 */
414 function flexifilter_filter_tips($delta, $format, $long = FALSE) {
415 $flexifilter = flexifilter_get_filter_by_delta($delta);
416 if ($long) {
417 return str_replace('<--break-->', '', $flexifilter['description']);
418 }
419 else {
420 $pos = strpos($flexifilter['description'], '<!--break-->');
421 if ($pos === FALSE) {
422 return substr($flexifilter['description'], 0);
423 }
424 return substr($flexifilter['description'], 0, $pos);
425 }
426 }
427
428 /**
429 * Menu callback; loads a flexifilter object.
430 *
431 * @param $fid
432 * The uniqe flexifilter id of the flexifilter to load.
433 * @return
434 * The flexifilter upon success; FALSE upon failure.
435 */
436 function flexifilter_load($fid) {
437 if (!is_numeric($fid)) {
438 return FALSE;
439 }
440 $result = db_query('SELECT * FROM {flexifilters} WHERE fid = %d', $fid);
441 if ($row = db_fetch_object($result)) {
442 return _flexifilter_filter_from_db_row($row, TRUE);
443 }
444 return FALSE;
445 }
446
447 function flexifilter_get_field($flexifilter, $field) {
448 return $flexifilter[$field];
449 }
450
451 /**
452 * Returns all flexifilters
453 *
454 * @param $include_components If TRUE, then the filter's components are not retrieved. This saves on alot of work, so set it to FALSE if you only require the flexifilter names and other simple details.
455 * @param $reset May be set to true to clear the cache.
456 *
457 * @return An array of flexifilters. For each key/value pair, the key is the flexifilter ID and the value
458 * is an array containing the following fields:
459 * - label : A human readable name for the flexifilter
460 * - description : The description given to the flexifilter
461 * - id : The flexifilter ID number
462 * - enabled : TRUE if the flexifilter is enabled, FALSE otherwise
463 * - advanced
464 * - delta : If enabled, then a value between 0 and 127 which is unique in the enabled flexifilters
465 * - components : An array containing the components of the flexifilter
466 */
467 function flexifilter_get_filters($include_components = TRUE, $reset = FALSE) {
468 static $cache = array();
469 if (!isset($cache[$include_components]) || $reset) {
470 $filters = array();
471 $result = db_query('SELECT * FROM {flexifilters}');
472 while ($row = db_fetch_object($result)) {
473 $filters[$row->fid] = _flexifilter_filter_from_db_row($row, $include_components);
474 }
475 $cache[$include_components] = $filters;
476 }
477 return $cache[$include_components];
478 }
479
480 /**
481 * Saves a flexifilter to the database.
482 *
483 * @param $filter A flexifilter to save. Should be of same form as the flexifilters returned from flexifilter_get_filters
484 * (e.g. an array containing the label,description,id,enabled,advanced,delta and components fields), although id can be
485 * set to 'new' to create a new flexifilter rather than update an existing one (in which case, delta is ignored). Do
486 * not use
487 *
488 * @return The fid.
489 */
490 function flexifilter_save_filter($filter) {
491 $fid = $filter['fid'];
492 $label = $filter['label'];
493 $description = $filter['description'];
494 $advanced = $filter['advanced'];
495 $cache = isset($filter['cache']) ? $filter['cache'] : 1;
496 if ($fid === 'new') {
497 db_query("INSERT INTO {flexifilters} (label, description, enabled, delta, pid_root, advanced, cache) VALUES ('%s', '%s', 0, 0, 0, %d)",
498 $label, $description, $advanced, $cache);
499 $fid = db_last_insert_id('flexifilters', 'fid');
500 $pids_to_reuse = array();
501 $pid_root = _flexifilter_save_filter_components($filter['components'], $fid, $pids_to_reuse);
502 db_query('UPDATE {flexifilters} SET pid_root = %d WHERE fid = %d', $pid_root, $fid);
503 }
504 else {
505 $pids_to_reuse = array();
506 $result = db_query('SELECT pid FROM {flexifilters_parts} WHERE fid = %d', $fid);
507 while ($row = db_fetch_object($result)) {
508 $pids_to_reuse[] = $row->pid;
509 }
510 sort($pids_to_reuse, SORT_NUMERIC);
511 $pid_root = _flexifilter_save_filter_components($filter['components'], $fid, $pids_to_reuse);
512 if (count($pids_to_reuse) > 0) {
513 db_query('DELETE FROM {flexifilters_parts} WHERE pid = '. implode($pids_to_reuse, ' OR pid = '), $fid);
514 }
515 db_query("UPDATE {flexifilters} SET label = '%s', description = '%s', pid_root = %d, advanced = %d, cache = %d WHERE fid = %d",
516 $label, $description, $pid_root, $advanced, $cache, $fid);
517 }
518 $existing_filters = flexifilter_get_filters(FALSE, TRUE);
519 if (isset($existing_filters[$fid]['enabled']) && isset($filter['enabled'])) {
520 if ($existing_filters[$fid]['enabled'] != $filter['enabled']) {
521 if ($filter['enabled']) {
522 if (flexifilter_get_number_enabled_filters() < FLEXIFILTER_MAX_FILTERS) {
523 $delta = flexifilter_get_unused_delta();
524 if ($delta !== FALSE) {
525 db_query('UPDATE {flexifilters} SET enabled = 1, delta = %d WHERE fid = %d', $delta, $fid);
526 }
527 }
528 }
529 else {
530 db_query('UPDATE {flexifilters} SET enabled = 0 WHERE fid = %d', $fid);
531 }
532 }
533 }
534 return $fid;
535 }
536
537 function _flexifilter_save_filter_components($components, $fid, &$pids_to_reuse, $parent = FALSE) {
538 if ($parent === FALSE) {
539 if ($reuse_pid = array_shift($pids_to_reuse)) {
540 db_query("UPDATE {flexifilters_parts} SET fid = %d, parent_pid = 0, type = %d, class_name = '', settings = '' WHERE pid = %d",
541 $fid, FLEXIFILTER_PART_TYPE_ROOT, $reuse_pid);
542 $parent = $reuse_pid;
543 }
544 else {
545 db_query("INSERT INTO {flexifilters_parts} (fid, parent_pid, type, class_name, settings) VALUES (%d, 0, %d, '', '')",
546 $fid, FLEXIFILTER_PART_TYPE_ROOT);
547 $parent = db_last_insert_id('flexifilters_parts', 'pid');
548 }
549 }
550 foreach ($components as $key => $child) {
551 if (is_numeric($key)) {
552 if (isset($child['settings']['components'])) {
553 $child_components = $child['settings']['components'];
554 unset($child['settings']['components']);
555 }
556 if (isset($child['settings']['condition'])) {
557 $child_condition = $child['settings']['condition'];
558 unset($child['settings']['condition']);
559 }
560 if ($child_cid = array_shift($pids_to_reuse)) {
561 db_query("UPDATE {flexifilters_parts} SET fid = %d, parent_pid = %d, type = %d, class_name = '%s', settings = '%s' WHERE pid = %d",
562 $fid, $parent, FLEXIFILTER_PART_TYPE_COMPONENT, $child['class'], serialize($child['settings']), $child_cid);
563 }
564 else {
565 db_query("INSERT INTO {flexifilters_parts} (fid, parent_pid, type, class_name, settings) VALUES (%d, %d, %d, '%s', '%s')",
566 $fid, $parent, FLEXIFILTER_PART_TYPE_COMPONENT, $child['class'], serialize($child['settings']));
567 $child_cid = db_last_insert_id('flexifilters_parts', 'pid');
568 }
569 if (isset($child_components)) {
570 _flexifilter_save_filter_components($child_components, $fid, $pids_to_reuse, $child_cid);
571 }
572 if (isset($child_condition)) {
573 _flexifilter_save_filter_condition($child_condition, $fid, $pids_to_reuse, $child_cid);
574 }
575 }
576 }
577 return $parent;
578 }
579
580 function _flexifilter_save_filter_condition($condition, $fid, &$pids_to_reuse, $parent) {
581 if (isset($condition['settings']['conditions'])) {
582 $condition_conditions = $condition['settings']['conditions'];
583 unset($condition['settings']['conditions']);
584 }
585 if ($our_pid = array_shift($pids_to_reuse)) {
586 db_query("UPDATE {flexifilters_parts} SET fid = %d, parent_pid = %d, type = %d, class_name = '%s', settings = '%s' WHERE pid = %d",
587 $fid, $parent, FLEXIFILTER_PART_TYPE_CONDITION, $condition['class'], serialize($condition['settings']), $our_pid);
588 }
589 else {
590 db_query("INSERT INTO {flexifilters_parts} (fid, parent_pid, type, class_name, settings) VALUES (%d, %d, %d, '%s', '%s')",
591 $fid, $parent, FLEXIFILTER_PART_TYPE_CONDITION, $condition['class'], serialize($condition['settings']));
592 $our_pid = db_last_insert_id('flexifilters_parts', 'pid');
593 }
594 if (isset($condition_conditions)) {
595 foreach ($condition_conditions as $sub_condition) {
596 _flexifilter_save_filter_condition($sub_condition, $fid, $pids_to_reuse, $our_pid);
597 }
598 }
599 }
600
601 /**
602 * Fetches flexifilters by delta.
603 */
604 function flexifilter_get_filter_by_delta($delta, $reset = FALSE) {
605 static $cache;
606 if (!isset($cache)) {
607 $cache = array();
608 }
609 if (!isset($cache[$delta]) || $reset) {
610 $result = db_query('SELECT * FROM {flexifilters} WHERE enabled = 1 AND delta = %d', $delta);
611 if ($row = db_fetch_object($result)) {
612 $cache[$delta] = _flexifilter_filter_from_db_row($row, TRUE);
613 }
614 else {
615 $cache[$delta] = FALSE;
616 }
617 }
618 return $cache[$delta];
619 }
620
621 /**
622 * Helper function for flexifilter_get_filters; converts a row from the flexifilters table
623 * into a full flexifilter.
624 */
625 function _flexifilter_filter_from_db_row($row, $include_components) {
626 $filter = array(
627 'label' => $row->label,
628 'description' => $row->description,
629 'id' => $row->fid,
630 'enabled' => ($row->enabled == 1),
631 'advanced' => ($row->advanced == 1),
632 'delta' => $row->delta,
633 'cache' => $row->cache,
634 );
635 $pid_root = $row->pid_root;
636
637 if ($include_components) {
638 // Fetch all the parts of the filter and store them in a flat array
639 $result = db_query('SELECT * FROM {flexifilters_parts} WHERE fid = %d', $row->fid);
640 $components_flat = array();
641 $component_children = array();
642 $id_next = 0;
643 while ($row = db_fetch_object($result)) {
644 $components_flat[$row->pid] = array(
645 'class' => $row->class_name,
646 'settings' => $row->settings,
647 'type' => $row->type,
648 'pid' => $row->pid,
649 );
650 $id_next = max($id_next, $row->pid + 1);
651 if (!isset($component_children[$row->parent_pid])) {
652 $component_children[$row->parent_pid] = array($row->pid);
653 }
654 else {
655 $component_children[$row->parent_pid][] = $row->pid;
656 }
657 }
658 $filter['components'] = _flexifilter_reconstruct_components($pid_root, $components_flat, $component_children);
659 $filter['components']['id_next'] = $id_next;
660 $filter['components']['id_prefix'] = 'flexifilter_component_';
661 }
662 return $filter;
663 }
664
665 /**
666 * Helper function for _flexifilter_filter_from_db_row; recursively collects the component
667 * children of a flexifilter part.
668 */
669 function _flexifilter_reconstruct_components($root, &$components_flat, &$component_children) {
670 $component_list = flexifilter_get_component_list();
671 $components = array();
672 if (isset($component_children[$root])) {
673 foreach ($component_children[$root] as $child_pid) {
674 $child = $components_flat[$child_pid];
675 if ($child['type'] == FLEXIFILTER_PART_TYPE_COMPONENT) {
676 $class = $component_list[$child['class']];
677 $child_reconstructed = array(
678 'class' => $child['class'],
679 'settings' => unserialize($child['settings']),
680 'id' => $child['pid'],
681 );
682 if ($class['contains_components']) {
683 $child_reconstructed['settings']['components'] = _flexifilter_reconstruct_components($child_pid, $components_flat, $component_children);
684 }
685 if ($class['contains_condition']) {
686 $conditions = _flexifilter_reconstruct_conditions($child_pid, $components_flat, $component_children);
687 $child_reconstructed['settings']['condition'] = $conditions[0];
688 }
689 $components[] = $child_reconstructed;
690 }
691 }
692 }
693 return $components;
694 }
695
696 /**
697 * Helper function for _flexifilter_filter_from_db_row; recursively collects the condition
698 * children of a flexifilter part.
699 */
700 function _flexifilter_reconstruct_conditions($root, &$conditions_flat, &$condition_children) {
701 $condition_list = flexifilter_get_condition_list();
702 $conditions = array();
703 if (isset($condition_children[$root])) {
704 foreach ($condition_children[$root] as $child_pid) {
705 $child = $conditions_flat[$child_pid];
706 if ($child['type'] == FLEXIFILTER_PART_TYPE_CONDITION) {
707 if ($child['class'] == '') {
708 $child_reconstructed = array(
709 'class' => '',
710 'settings' => array(),
711 );
712 }
713 else {
714 $class = $condition_list[$child['class']];
715 $child_reconstructed = array(
716 'class' => $child['class'],
717 'settings' => unserialize($child['settings']),
718 );
719 if ($class['contains_conditions']) {
720 $child_reconstructed['settings']['conditions'] = _flexifilter_reconstruct_conditions($child_pid, $conditions_flat, $condition_children);
721 }
722 }
723 $conditions[] = $child_reconstructed;
724 }
725 }
726 }
727 return $conditions;
728 }
729
730 /**
731 * Returns the number of enabled flexifilters. This is useful because only 128 are allowed to be enabled at one time.
732 */
733 function flexifilter_get_number_enabled_filters($reset = FALSE) {
734 static $count;
735 if (!isset($count) || $reset) {
736 $count = 0;
737 $filters = flexifilter_get_filters($reset);
738 foreach ($filters as $filter) {
739 if ($filter['enabled']) {
740 $count++;
741 }
742 }
743 }
744 return $count;
745 }
746
747 /**
748 * API function: installs flexifilters.
749 *
750 * @param $module - The name of the module whose flexifilters should be installed.
751 * @param $flexifilters - Optional. An array of flexifilters to be installed. If passed in, $module is irrelevant.
752 * @return
753 * An array of fids of the filters saved.
754 */
755 function flexifilter_install_flexifilters($module, $flexifilters = NULL) {
756 if (is_null($flexifilters)) {
757 $flexifilters = module_invoke($module, 'flexifilters');
758 }
759 $fids = array();
760 foreach ($flexifilters as $flexifilter) {
761 $fid = flexifilter_save_filter($flexifilter);
762 drupal_set_message(t('The !url flexifilter has been saved.', array('!url' => l($flexifilter['label'], "admin/build/flexifilters/$fid"))));
763 watchdog('flexifilter', 'The !url flexifilter has been saved.', array('!url' => l($flexifilter['label'], "admin/build/flexifilters/$fid")));
764 $fids[] = $fid;
765 }
766 return $fids;
767 }
768
769 function flexifilter_get_unused_delta() {
770 $deltas = array();
771 for ($i = 0; $i < FLEXIFILTER_MAX_FILTERS; $i++) {
772 $deltas[$i] = TRUE;
773 }
774 foreach (flexifilter_get_filters(FALSE) as $flexifilter) {
775 if ($flexifilter['enabled']) {
776 $deltas[$flexifilter['delta']] = FALSE;
777 }
778 }
779 for ($i = 0; $i < FLEXIFILTER_MAX_FILTERS; $i++) {
780 if ($deltas[$i] == TRUE) {
781 return $i;
782 }
783 }
784 return FALSE;
785 }
786
787 /**
788 * Implementation of hook_enable.
789 */
790 function flexifilter_enable() {
791 // We install flexifilter's flexifilters here so the tables, etc., will already have been set up.
792 // Other modules can put this in hook_install.
793 flexifilter_install_flexifilters('flexifilter');
794 }

  ViewVC Help
Powered by ViewVC 1.1.2