5.x-1.x branch
Enables extremely simple adding/removing features to your site with minimal to no configuration
| Name | Description |
|---|---|
| patterns_disable_pattern | |
| patterns_disable_pattern_submit | |
| patterns_edit | Menu callback to edit a patterns data |
| patterns_edit_submit | Submit edits to the pattern |
| patterns_edit_validate | Validate pattern modifications (make sure proper XML) |
| patterns_enable_pattern | |
| patterns_enable_pattern_submit | |
| patterns_execute_action | Execute an action |
| patterns_execute_pattern | |
| patterns_executing | |
| patterns_exit | |
| patterns_form_alter | |
| patterns_form_helper | |
| patterns_from_source | Create a pattern from an XML data source |
| patterns_get_pattern | |
| patterns_get_patterns | |
| patterns_import_file | Display the import pattern file form |
| patterns_import_source | Display the import pattern form |
| patterns_import_submit | |
| patterns_import_url | Display the import pattern url form |
| patterns_import_validate | |
| patterns_init | |
| patterns_invoke | |
| patterns_list | |
| patterns_load | |
| patterns_load_xml | |
| patterns_menu | Implementation of hook_menu(). |
| patterns_modules | Return a list of modules for a pattern |
| patterns_modules_page | List the modules used by a particular pattern |
| patterns_perm | Implementation of hook_perm(). |
| patterns_process_modules | |
| patterns_rearrange_data | |
| patterns_revert | Menu callback to undo a patterns update changes |
| patterns_save_pattern | |
| patterns_token_values | Implementation of hook_token_values() |
| theme_patterns_form_helper | |
| theme_patterns_form_helper_menu | |
| _patterns_check_file_dir | Check if a .htaccess file exists to prevent downloads of pattern files |
| _patterns_modify_value | Function callback |
| _patterns_parse_tag | Recurse through the values of a parsed xml file to create a multi-dimensional representation of the data. |
| _patterns_rearrange_data | |
| _patterns_recurse_tokens | Recurse an array and replace with tokens @ This is used instead of array_walk_recursive because of some strange issues with token_get_values failing. |
| _patterns_replace_tokens | Array walk callback to replace tokens inside form values |
- <?php
-
- /**
- * @file
- * Enables extremely simple adding/removing features to your site with minimal to no configuration
- */
-
- /**
- * @todo:
- * @ Enable pattern configurations
- * @ **done**Enable actions to see ids created/updated from other actions inside the pattern (tokens?)
- * @ **semi-done** Reset patterns
- * @ Enable components to analyze the current pattern for better validation
- * @ Allow module version restricting
- * @ Put in functionality to auto-download modules (and the correct version)
- * @ Enable backups before running patterns and reverting back to those backups
- * @ Implement a progress meter
- * @ Handle default values better to allow for absolute minimal actions
- * @ Enable interactive actions by allowing patterns to resume from a saved position
- * @ Implement an export feature for all available form_ids
- * @ Allow resuming failed patterns
- * @ In the pattern details, list any sub-patterns directly on the patterns listing page
- */
-
- /**
- * Implementation of hook_perm().
- */
- function patterns_perm() {
- return array('administer patterns');
- }
-
- /**
- * Implementation of hook_menu().
- */
- function patterns_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'admin/build/patterns',
- 'description' => t('Administer patterns available for your site'),
- 'access' => user_access('administer patterns'),
- 'title' => t('Patterns'),
- 'callback' => 'patterns_list',
- 'type' => MENU_NORMAL_ITEM
- );
- $items[] = array('path' => 'admin/build/patterns/list',
- 'title' => t('List'),
- 'callback' => 'patterns_list',
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- 'weight' => -10
- );
- // $items[] = array('path' => 'admin/build/patterns/settings',
- // 'title' => t('Settings'),
- // 'callback' => 'drupal_get_form',
- // 'callback arguments' => array('patterns_settings'),
- // 'type' => MENU_LOCAL_TASK,
- // 'weight' => 10
- // );
- $items[] = array('path' => 'admin/build/patterns/configure',
- 'title' => t('Configure Pattern'),
- 'callback' => 'drupal_get_form',
- 'callback arguments' => array('patterns_configure_pattern'),
- 'type' => MENU_CALLBACK
- );
- $items[] = array('path' => 'admin/build/patterns/edit',
- 'title' => t('Edit Pattern'),
- 'callback' => 'drupal_get_form',
- 'callback arguments' => array('patterns_edit'),
- 'type' => MENU_CALLBACK
- );
- // $items[] = array('path' => 'admin/build/patterns/info',
- // 'title' => t('Pattern Details'),
- // 'callback' => 'patterns_info',
- // 'type' => MENU_CALLBACK
- // );
- $items[] = array('path' => 'admin/build/patterns/enable',
- 'access' => user_access('administer patterns'),
- 'title' => t('Enable Pattern'),
- 'callback' => 'drupal_get_form',
- 'callback arguments' => array('patterns_enable_pattern'),
- 'type' => MENU_CALLBACK
- );
- $items[] = array('path' => 'admin/build/patterns/disable',
- 'access' => user_access('administer patterns'),
- 'title' => t('Disable Pattern'),
- 'callback' => 'drupal_get_form',
- 'callback arguments' => array('patterns_disable_pattern'),
- 'type' => MENU_CALLBACK
- );
- $items[] = array('path' => 'admin/build/patterns/modules',
- 'access' => user_access('administer patterns'),
- 'title' => t('Pattern Modules'),
- 'callback' => 'patterns_modules_page',
- 'type' => MENU_CALLBACK
- );
- $items[] = array('path' => 'admin/build/patterns/revert',
- 'access' => user_access('administer patterns'),
- 'title' => t('Revert Pattern'),
- 'callback' => 'patterns_revert',
- 'type' => MENU_CALLBACK
- );
- $items[] = array('path' => 'admin/build/patterns/import',
- 'access' => user_access('administer patterns'),
- 'title' => t('Import'),
- 'callback' => 'drupal_get_form',
- 'callback arguments' => array('patterns_import_source'),
- 'type' => MENU_LOCAL_TASK
- );
- $items[] = array('path' => 'admin/build/patterns/import/xmltext',
- 'access' => user_access('administer patterns'),
- 'title' => t('Import via XML Source'),
- 'type' => MENU_DEFAULT_LOCAL_TASK
- );
- $items[] = array('path' => 'admin/build/patterns/import/xmlfile',
- 'access' => user_access('administer patterns'),
- 'title' => t('Import via XML File'),
- 'callback' => 'drupal_get_form',
- 'callback arguments' => array('patterns_import_file'),
- 'type' => MENU_LOCAL_TASK
- );
- $items[] = array('path' => 'admin/build/patterns/import/xmlurl',
- 'access' => user_access('administer patterns'),
- 'title' => t('Import via XML URL'),
- 'callback' => 'drupal_get_form',
- 'callback arguments' => array('patterns_import_url'),
- 'type' => MENU_LOCAL_TASK
- );
- // $items[] = array('path' => 'admin/build/patterns/import/server',
- // 'access' => user_access('administer patterns'),
- // 'title' => t('Import via Patterns Server'),
- // 'callback' => 'drupal_get_form',
- // 'callback arguments' => array('patterns_import_server'),
- // 'type' => MENU_LOCAL_TASK
- // );
- }
-
- return $items;
- }
-
- /**
- * Display the import pattern form
- */
- function patterns_import_source() {
- $form['xmlname'] = array(
- '#type' => 'textfield',
- '#title' => t('Pattern Identifier'),
- '#description' => t('Machine readable name for the pattern. The actual title should be included in the pattern itself.'),
- '#required' => true
- );
- $form['xmlsource'] = array(
- '#type' => 'textarea',
- '#rows' => 15,
- '#title' => t('Enter Pattern XML'),
- '#description' => t('Imported patterns are not executed until you run them manually. You can leave off the <?xml> tag at the top.')
- );
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Submit')
- );
- $form['#base'] = 'patterns_import';
-
- return $form;
- }
-
- /**
- * Display the pattern settings form
- */
- // function patterns_settings() {
- // drupal_set_message('This feature not implemented yet.');
- // }
-
- /**
- * Display the import pattern from server form
- */
- // function patterns_import_server() {
- // drupal_set_message('This feature not implemented yet.');
- // }
-
- /**
- * Display the import pattern file form
- */
- function patterns_import_file() {
- $form['#attributes']['enctype'] = 'multipart/form-data';
- $form['xmlfile'] = array(
- '#type' => 'file',
- '#title' => t('Upload XML Pattern File'),
- '#description' => t('Imported patterns are not executed until you run them manually.'),
- '#size' => 48
- );
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Submit')
- );
- $form['#base'] = 'patterns_import';
-
- return $form;
- }
-
- /**
- * Display the import pattern url form
- */
- function patterns_import_url() {
- $form['xmlurl'] = array(
- '#type' => 'textfield',
- '#title' => t('Specify an XML url'),
- '#description' => t('Import a pattern from a remote URL. Imported patterns are not executed until you run them manually.'),
- '#size' => 48
- );
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Submit')
- );
- $form['#base'] = 'patterns_import';
-
- return $form;
- }
-
- function patterns_import_validate($form_id, $values) {
- global $form_values;
-
- if ($file = file_check_upload('xmlfile')) {
- $form_values['xmlsource'] = file_get_contents($file->filepath);
- }
- else if (isset($values['xmlfile'])) {
- form_set_error('xmlfile', t('Error uploading XML file.'));
- }
- else if ($values['xmlurl']) {
- if (!ini_get('allow_url_fopen')) {
- form_set_error('xmlsource', t('allow_url_fopen must be enabled in your php configuration in order to use this feature.'));
- return;
- }
-
- if (!($form_values['xmlsource'] = file_get_contents($values['xmlurl']))) {
- form_set_error('xmlsource', t('Failed to retreive the pattern specified.'));
- return;
- }
-
- $form_values['xmlname'] = preg_replace('/\.[^\.]*$/', '', basename($form_values['xmlurl']));
- }
-
- if (strpos($form_values['xmlsource'], '<?xml') !== 0) {
- $form_values['xmlsource'] = '<?xml version="1.0" encoding="ISO-8859-1"?>' . $form_values['xmlsource'];
- }
-
- if ($form_values['xmlname'] && preg_match('/[^a-zA-Z0-9_]/', $form_values['xmlname'])) {
- form_set_error('xmlname', t('You can only include letters, numbers, and underscores in the pattern identifier.'));
- }
- else if ($form_values['xmlname'] && preg_match('/^_/', $form_values['xmlname'])) {
- form_set_error('xmlname', t('You cannot start the pattern identifier with an underscore.'));
- }
-
-
- $parse = xml_parser_create();
- xml_parse_into_struct($parse, $form_values['xmlsource'], $vals, $index);
-
- // Check that the xml was properly parsed and also that the
- // root <pattern> tag and also an <info> tag were used.
- if (!$vals || $vals[0]['tag'] != 'PATTERN' || $vals[1]['tag'] != 'INFO') {
- form_set_error('xmlsource', t('Error parsing the XML, please check your syntax and try again.'));
- }
- }
-
- function patterns_import_submit($form_id, $form_values) {
- if ($file = file_check_upload('xmlfile')) {
- // Currently nothing is happening with files saved here....
- if (file_check_directory(file_create_path(variable_get('patterns_save_xml', 'patterns')))) {
- file_save_upload('xmlfile', variable_get('patterns_save_xml', 'patterns'));
- }
- else {
- drupal_set_message(t('Pattern was registered but the file was not saved on the server because of improperly setup files directory.'));
- }
- }
- else if ($form_values['xmlsource']) {
- file_save_data($form_values['xmlsource'], variable_get('patterns_save_xml', 'patterns') .'/'. $form_values['xmlname'] .'.xml', FILE_EXISTS_REPLACE);
- }
-
- // Reload patterns
- patterns_get_patterns(true);
-
- return 'admin/build/patterns';
- }
-
- function patterns_list() {
-
- drupal_add_css(drupal_get_path('module', 'patterns') .'/patterns.css');
- drupal_add_js(drupal_get_path('module', 'patterns') .'/patterns.js');
-
- patterns_load();
- $patterns = patterns_get_patterns();
- if (empty($patterns)) return t('No patterns available.');
-
- // $header = array(t('Title'), t('Status'), t('Version'), t('Public'), t('Actions'));
- $header = array(t('Title'), t('Status'), t('Version'), t('Actions'));
-
- // List all patterns
- $rows = array();
- foreach($patterns as $pid => $pattern) {
- $actions = array();
- if (!$pattern->status) {
- $actions[] = l(t('Run'), 'admin/build/patterns/enable/'. $pid);
- }
- else if ($pattern->enabled >= $pattern->updated) {
- $actions[] = l(t('Re-Run'), 'admin/build/patterns/enable/'. $pid);
- }
- else {
- $actions[] = l(t('Run Update'), 'admin/build/patterns/enable/'. $pid);
- }
- $actions[] = l(t('Edit'), 'admin/build/patterns/edit/'. $pid);
- $actions = implode(' ', $actions);
-
- $cells = array();
- // $title = l($pattern->title, 'admin/build/patterns/info/'. $pid, array('class' => 'pattern-title', 'id' => 'pid-'. $pid));
- $title = '<span id="pid-'. $pid .'" class="pattern-title">'. $pattern->title .'</span>';
- // $view_more = '<div>'. t('Clik on pattern title to see more details.') .'</div>';
- $info = array();
- $info[] = t('Author: ') . $pattern->info['author'];
- $info[] = t('Email: ') . $pattern->info['author_email'];
- $info[] = t('Web: ') . $pattern->info['author_website'];
- $author = theme('item_list', $info);
- $title .= '<div id="pid-'. $pid .'-info" class="pattern-info">'. $author . $pattern->description . $view_more .'</div>';
- $cells[] = $title;
- $cells[] = $pattern->status ? t('Enabled') : t('Disabled');
- $cells[] = $pattern->info['version'];
- // $cells[] = $pattern->public ? t('Yes') : t('No');
- $cells[] = $actions;
- $category = $pattern->info['category'] ? $pattern->info['category'] : t('Other');
- $rows[$category][] = $cells;
- }
-
- ksort($rows);
- foreach ($rows as $title => $category) {
- $fieldset = array(
- '#title' => t($title),
- '#collapsible' => TRUE,
- '#collapsed' => FALSE,
- '#value' => theme('table', $header, $category),
- );
- $output .= theme('fieldset', $fieldset);
- }
-
- return $output;
- }
-
- /**
- * Menu callback to undo a patterns update changes
- */
- function patterns_revert($pid) {
- if ($name = db_result(db_query('SELECT name FROM {patterns} WHERE pid = "%d"', $pid))) {
- $path = file_create_path(variable_get('patterns_save_xml', 'patterns') .'/enabled/'. $name .'.xml');
- $new = db_result(db_query('SELECT file FROM {patterns} WHERE pid = "%d"'));
- }
- else {
- drupal_set_message(t('The pattern you specified does not exist.'), 'error');
- drupal_goto('admin/build/patterns');
- }
-
- if (file_exists($path)) {
- if (file_move($path, $new, FILE_EXISTS_REPLACE)) {
- drupal_set_message(t('This pattern was reverted to the state it was at when it was enabled.'));
- drupal_goto();
- }
- }
- else {
- drupal_set_message(t('The patterns enabled-state was not saved properly, therefore it cannot be reverted.'), 'error');
- }
-
- drupal_goto('admin/build/patterns');
- }
-
- /**
- * Menu callback to display patterns details page
- */
- // function patterns_info($pid = null) {
- // if (!is_numeric($pid)) {
- // drupal_set_message(t('You must specify a pattern.'));
- // return;
- // }
- //
- // $pattern = patterns_get_pattern($pid);
- //
- // $output = '';
- // return $output;
- // }
-
- /**
- * Menu callback to edit a patterns data
- */
- function patterns_edit($pid = null) {
- if (!is_numeric($pid)) {
- drupal_set_message(t('You must specify a pattern to edit.'));
- return;
- }
-
- $pattern = patterns_get_pattern($pid);
-
- // TODO: Turn php into xml here.
-
- // For now just allow modifying the original xml, which
- // means the modification cannot be further modified
-
- if (!$pattern->file) {
- drupal_set_message(t('This pattern does not seem to have an XML source file to base the modifications off of.'), 'error');
- return;
- }
-
- $xml = file_get_contents($pattern->file);
-
- $form['name'] = array(
- '#type' => 'value',
- '#value' => $pattern->name
- );
- $form['pid'] = array(
- '#type' => 'value',
- '#value' => $pattern->pid
- );
- if ($pattern->enabled <= $pattern->updated) {
- $form['revert'] = array(
- '#type' => 'markup',
- '#value' => l(t('Undo update changes to the state when you enabled the pattern.'), 'admin/build/patterns/revert/'. $pid, array(), drupal_get_destination())
- );
- }
- $form['xml'] = array(
- '#type' => 'textarea',
- '#title' => t('Pattern\'s XML'),
- '#rows' => 25,
- '#default_value' => $xml
- );
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Submit')
- );
-
- return $form;
- }
-
- /**
- * Validate pattern modifications (make sure proper XML)
- */
- function patterns_edit_validate($form_id, $form_values) {
- // Do validations....
- $path = file_create_path(variable_get('patterns_save_xml', 'patterns'));
-
- if (!file_check_directory($path, true)) {
- form_set_error('form_token', t('Unable to create @path to save the new pattern to.', array('@path' => $path)));
- }
- }
-
- /**
- * Submit edits to the pattern
- */
- function patterns_edit_submit($form_id, $form_values) {
- // If this is an enabled pattern, make sure the enabled pattern is saved in its current state
- if ($file = db_result(db_query('SELECT file FROM {patterns} WHERE status = 1 AND name = "%s"', $form_values['name']))) {
- $dir = file_directory_path() .'/'. variable_get('patterns_save_xml', 'patterns') .'/enabled';
- file_check_directory($dir, true);
- $path = $dir .'/'. $form_values['name'] .'.xml';
-
- if (!file_exists($path)) {
- file_copy($file, $path, FILE_EXISTS_ERROR);
- }
- }
-
- // Save the new pattern into the pattern files dir.
- $path = file_directory_path() .'/'. variable_get('patterns_save_xml', 'patterns') .'/'. $form_values['name'] .'.xml';
-
- file_save_data($form_values['xml'], $path, FILE_EXISTS_REPLACE);
-
- $old = db_result(db_query('SELECT file FROM {patterns} WHERE name = "%s"', $form_values['name']));
-
- // Load and save pattern
- if ($pattern = patterns_load_xml($path)) {
- if ($old) {
- db_query('UPDATE {patterns} SET file = "%s", updated = "%s" WHERE pid = "%d"', $path, time(), $form_values['pid']);
- }
-
- patterns_save_pattern($pattern, $path);
- }
-
- drupal_set_message(t('%name was saved.', array('%name' => $form_values['name'])));
-
- return 'admin/build/patterns';
- }
-
- /**
- * List the modules used by a particular pattern
- */
- function patterns_modules_page($pid) {
- $pattern = patterns_get_pattern($pid);
-
- drupal_set_title($pattern->title .' '. t('(Pattern Modules)'));
-
- $modules = patterns_modules($pattern);
- $modules_info = module_rebuild_cache();
- $modules_list = module_list();
-
- // Get module name, whether its to be disabled or enabled,
- // whether the module is available or not, and whether it is
- // currently enabled or not
- foreach($modules as $module) {
- $row = array();
- $delete = is_array($module) ? $module['delete'] : false;
- $module = is_array($module) ? $module['value'] : $module;
- $available = array_key_exists($module, $modules_info);
- $enabled = array_key_exists($module, $modules_list);
- $row[] = $module;
- $row[] = $available ? t('Yes') : '<span class="alert">'. t('No') .'</span>';
- $row[] = $enabled ? t('Yes') : '<span class="alert">'. t('No') .'</span>';
- $row[] = $delete ? t('Delete') : t('Enable');
- $rows[] = $row;
-
- if (!$available) {
- $not_available = true;
- }
- }
-
- if ($not_available) {
- drupal_set_message(t('Some modules are not availalble, please download them before running this pattern.'), 'error');
- }
- else {
- drupal_set_message(t('All modules required by this module are available. Click !here to run this pattern.', array('!here' => l(t('here'), 'admin/build/patterns/enable/'. $pid))));
- }
-
- return theme('table', array(t('Name'), t('Available'), t('Enabled'), t('Pattern Action')), $rows, t('Modules used for this pattern'));
- }
-
- function patterns_load() {
- static $loaded = false;
-
- if ($loaded) {
- return;
- }
-
- require_once drupal_get_path('module', 'patterns') .'/patterns.inc';
-
- foreach(file_scan_directory(drupal_get_path('module', 'patterns') .'/components', '.\.inc') as $file) {
- include_once $file->filename;
- }
-
- $loaded = true;
- }
-
- function patterns_get_patterns($reset = true) {
- patterns_load();
-
- if ($reset || !variable_get('patterns_loaded', false)) {
- // Get a listing of enabled patterns
- $enabled = array();
- $result = db_query('SELECT file FROM {patterns} WHERE status = 1');
-
- while ($pattern = db_fetch_object($result)) {
- $enabled[] = $result->file;
- }
-
- $path = file_create_path(variable_get('patterns_save_xml', 'patterns'));
- $priority = array();
-
- // Get uploaded patterns first
- if (file_check_directory($path)) {
- // Check if .htaccess file exists in path, if not insert it
- _patterns_check_file_dir();
-
- foreach(file_scan_directory($path, '.\.xml') as $file) {
- // Don't save enabled pattern backups
- if (strpos($file->filename, $path .'/enabled/') === 0) {
- continue;
- }
-
- // Set priority so these patterns won't get over-written
- $priority[] = $file->name;
-
- // Can't update existing patterns that are enabled
- if (in_array($file->filename, $enabled)) {
- continue;
- }
-
- // Load and save pattern
- if ($pattern = patterns_load_xml($file->filename)) {
- patterns_save_pattern($pattern, $file->filename);
- }
- }
- }
-
- // Get per-site patterns next
- foreach(file_scan_directory(conf_path() .'/patterns', '.\.xml') as $file) {
- // Can't update existing patterns that are enabled
- if (in_array($file->filename, $enabled) || in_array($file->name, $priority)) {
- continue;
- }
-
- $priority[] = $file->name;
-
- // Load and save pattern
- if ($pattern = patterns_load_xml($file->filename)) {
- patterns_save_pattern($pattern, $file->filename);
- }
- }
-
- // Get profile patterns next
- global $profile;
- foreach(file_scan_directory('profiles/'. $profile .'/patterns', '.\.xml') as $file) {
- // Can't update existing patterns that are enabled
- if (in_array($file->filename, $enabled) || in_array($file->name, $priority)) {
- continue;
- }
-
- $priority[] = $file->name;
-
- // Load and save pattern
- if ($pattern = patterns_load_xml($file->filename)) {
- patterns_save_pattern($pattern, $file->filename);
- }
- }
-
- // Last get the default patterns
- foreach(file_scan_directory(drupal_get_path('module', 'patterns') .'/patterns', '.\.xml') as $file) {
- // Can't update existing patterns that are enabled
- if (in_array($file->filename, $enabled) || in_array($file->name, $priority)) {
- continue;
- }
-
- $pattern = patterns_load_xml($file->filename);
-
- // Load and save pattern
- if ($pattern = patterns_load_xml($file->filename)) {
- patterns_save_pattern($pattern, $file->filename);
- }
- }
-
- variable_set('patterns_loaded', time());
- }
-
- $result = db_query('SELECT * FROM {patterns}');
-
- while ($pattern = db_fetch_object($result)) {
- $patterns[$pattern->pid] = $pattern;
- $data = unserialize($patterns[$pattern->pid]->pattern);
- $patterns[$pattern->pid]->pattern = $data;
- foreach ($data as $key => $section) {
- if ($data[$key]['tag'] == 'info') {
- $patterns[$pattern->pid]->info = array();
- array_shift($section);
- foreach($section as $property) {
- $patterns[$pattern->pid]->info[$property['tag']] = $property['value'];
- }
- }
- }
- }
-
- return $patterns;
- }
-
- function patterns_save_pattern($pattern, $xmlpath = '') {
- $name = basename($xmlpath, '.xml');
-
- foreach($pattern[0] as $index => $value) {
- if (is_numeric($index)) {
- switch($value['tag']) {
- case 'title':
- $title = $value['value'];
- break;
- case 'description':
- $description = $value['value'];
- break;
- case 'author':
- $author = $value['value'];
- break;
- }
- }
- }
-
- if ($pid = db_result(db_query('SELECT pid FROM {patterns} WHERE name = "%s"', $name))) {
- $updated = db_result(db_query('SELECT updated FROM {patterns} WHERE pid = "%d"', $pid));
- if (($new_updated = filemtime(file_create_path($xmlpath))) > $updated) {
- db_query('UPDATE {patterns} SET pattern = "%s", title = "%s", file = "%s", updated = "%s", description = "%s" WHERE pid = %d', serialize($pattern), $title, $xmlpath, $new_updated, $description, $pid);
- }
- else {
- db_query('UPDATE {patterns} SET pattern = "%s", title = "%s", file = "%s", description = "%s" WHERE pid = %d', serialize($pattern), $title, $xmlpath, $description, $pid);
- }
- }
- else {
- $pid = db_next_id('{patterns}_pid');
- db_query('INSERT INTO {patterns} VALUES(%d, "%s", 0, "%s", "%s", 0, "%s", "%s", "%s")', $pid, $name, $xmlpath, time(), $title, $description, serialize($pattern));
- }
- }
-
- function patterns_get_pattern($id) {
- if (is_numeric($id)) {
- $pattern = db_fetch_object(db_query('SELECT * FROM {patterns} WHERE pid = "%d"', $id));
- }
- else if (is_string($id)) {
- $pattern = db_fetch_object(db_query('SELECT * FROM {patterns} WHERE name = "%s"', $id));
- }
-
- // Get the actual data. Data is stored in serialized form in the db.
- $pattern->pattern = unserialize($pattern->pattern);
-
- // Rearrange the data in a nice way for each component.
- // Make sure actions are processed differently so order is preserved.
- $pattern->pattern = patterns_rearrange_data($pattern->pattern);
-
- return $pattern;
- }
-
- function patterns_load_xml($path) {
- if (!file_exists($path)) {
- return;
- }
-
- $xml = file_get_contents($path);
-
- $pattern = patterns_from_source($xml);
-
- return $pattern;
- }
-
- /**
- * Create a pattern from an XML data source
- */
- function patterns_from_source($xml) {
- $parse = xml_parser_create();
- xml_parse_into_struct($parse, $xml, $vals, $index);
-
- // Create a multi-dimensional array representing the XML structure
- $pattern = current(_patterns_parse_tag($vals, 0));
-
- return $pattern;
- }
-
- /**
- * Recurse through the values of a parsed xml file to create a
- * multi-dimensional representation of the data.
- */
- function _patterns_parse_tag($data, $index) {
- $pattern = array();
-
- while ($current = $data[$index]) {
- $type = $current['type'];
-
- foreach((array)$current['attributes'] as $key => $value) {
- $current[strtolower($key)] = $value;
- }
-
- $current['tag'] = strtolower($current['tag']);
-
- unset($current['type'], $current['level'], $current['attributes']);
-
- if (!trim($current['value']) && $current['value'] != "0") {
- unset($current['value']);
- }
-
- switch($type) {
- case 'open':
- $index++;
- $current += _patterns_parse_tag($data, &$index);
- $pattern[] = $current;
- break;
- case 'close':
- $index++;
- return $pattern;
- break;
- case 'complete':
- // In order to support more complex/non-standard features we can use serialized data
- if ($current['attributes']['serialized']) {
- $value = unserialize($current['value']);
-
- if (isset($value)) {
- $current['value'] = $value;
- }
- }
-
- // This enables tags like <blog /> to turn into array('blog' => 'blog')
- // which is useful for checkbox/select type forms
- if (!isset($current['value'])) {
- $current['value'] = $current['tag'];
- }
-
- $pattern[] = $current;
- break;
- }
-
- $index++;
- }
-
- return $pattern;
- }
-
- function patterns_disable_pattern($pid) {
- $form['pid'] = array(
- '#type' => 'value',
- '#value' => $pid
- );
-
- $pattern = patterns_get_pattern($pid);
-
- return confirm_form($form, t('Proceed with disabling pattern %pattern?', array('%pattern' => $pattern->title)), 'admin/build/patterns', '');
- }
-
- function patterns_enable_pattern($pid) {
- $form['pid'] = array(
- '#type' => 'value',
- '#value' => $pid
- );
-
- $disclaimer = t('Please be sure to backup your site before running a pattern. Patterns are not guaranteed to be reversable in case they do not execute well or if unforseen side effects occur.');
-
- $pattern = patterns_get_pattern($pid);
-
- return confirm_form($form, t('Proceed with running pattern %pattern?', array('%pattern' => $pattern->title)), 'admin/build/patterns', $disclaimer);
- }
-
- function patterns_disable_pattern_submit($form_id, $form_values) {
- $pid = $form_values['pid'];
- $pattern = patterns_get_pattern($pid);
-
- if (patterns_execute_pattern($pattern, true, true)) {
- return 'admin/build/patterns';
- }
- }
-
- function patterns_enable_pattern_submit($form_id, $form_values) {
- $pid = $form_values['pid'];
-
- patterns_load();
- $pattern = patterns_get_pattern($pid);
-
- if (patterns_execute_pattern($pattern, false, true)) {
- return 'admin/build/patterns';
- }
- }
-
- function patterns_execute_pattern($pattern, $reverse = false, $interact = false, $jump = null) {
- patterns_load();
- set_time_limit(0);
-
- if (!is_object($pattern)) {
- $pattern = patterns_get_pattern($pattern);
-
- if (!$pattern) {
- return false;
- }
- }
-
- $errors = $action_list = $identifiers = array();
- $error = true;
-
- // Pattern info
- $status = $pattern->status;
- $title = $pattern->title;
- $pid = $pattern->pid;
-
- // From here on out just need the actual pattern data
- $pattern = $pattern->pattern;
-
- // Split the pattern up into modules and actions. Submit modules as its
- // own pattern programattically.
- $modules = $actions = array();
- for($i=0;$tag=$pattern[$i];$i++) {
- if ($tag['tag'] == 'modules') {
- $modules = $tag;
- }
- else if ($tag['tag'] == 'actions') {
- $actions = $tag;
- unset($actions['tag']);
- }
- }
-
- // If there are no actions or modules, most likely the pattern
- // was not created correctly.
- if (empty($actions) && empty($modules)) {
- drupal_set_message(t('Could not recognize pattern %title, aborting.', array('%title' => $title)), 'error');
- return true;
- }
-
- if ($modules && (!$interact || ($interact && !$jump))) {
- // Make the modules look like a normal pattern so they can be executed
- // on their own.
- $obj = new stdClass();
- $obj->title = t('Enable/disable %title pattern modules', array('%title' => $title));
- $obj->status = $status;
- $obj->pattern = array(array('tag' => 'actions', $modules));
- $modules = $obj;
-
- // Modules need to be enabled first so the rest of the pattern
- // can proceed smoothly.
- if (!$reverse) {
- $error = patterns_execute_pattern($modules, $reverse, $interact);
- module_rebuild_cache();
- }
- }
-
- // Keep a list of what modules handle what tags
- $tag_modules = patterns_invoke($empty, 'tag modules');
-
- // If an interactive pattern needs resuming, remove the
- // already executed actions
- if ($interact && $jump > 0) {
- array_splice($actions, 0, $jump);
- }
-
- // Prepare actions for validation/processing
- foreach($actions as $key => $data) {
- patterns_invoke($actions[$key], 'prepare');
- }
-
- // Reverse a pattern for disabling
- if ($reverse && $status) {
- $actions = array_reverse($actions);
-
- foreach($actions as $key => $data) {
- $continue = patterns_invoke($data, 'reverse');
-
- if ($continue === false) {
- drupal_set_message(t('[Error] Disabling of this pattern is not supported at this time.'));
- return false;
- }
-
- $actions[$key] = $data;
- }
- }
-
- // Pre validate tags with their appropriate components
- foreach($actions as $key => $data) {
- if (!array_key_exists($data['tag'], $tag_modules)) {
- $errors[$data['tag']][] = t('Invalid Pattern: <%tag> is not a valid tag', array('%tag' => $data['tag']));
- }
- else {
- $error = patterns_invoke($data, 'pre-validate');
- if ($error) {
- $errors[$data['tag']][] = t('Invalid Pattern: !msg', array('!msg' => $error));
- }
- }
- }
-
- if (count($errors)) {
- foreach($errors as $error) {
- drupal_set_message(implode('<br>', $error), 'error');
- }
-
- return;
- }
-
- // Build and execute a list of actions
- foreach($actions as $key => $data) {
- // Prepare actions for processing, ensure smooth pattern executions, and return form ids for execution
- $return = patterns_invoke($data, 'form_id');
-
- // If prepare removed the data, dont continue with this action
- if (!$data || !$return) {
- continue;
- }
-
- if (is_string($return)) {
- $form_ids = array($return);
- }
- else if ($return) {
- $form_ids = $return;
- }
-
- // Build the action
- foreach($form_ids as $form_id) {
- $clone = $data;
- $error = patterns_invoke($clone, 'validate', $form_id);
-
- if ($error) {
- if (is_array($error)) {
- foreach($error as $msg) {
- drupal_set_message($msg, 'error');
- }
-
- $errors[$clone['tag']] = t('Broken Pattern: %msg', array('%msg' => implode('<br>', $error)));
- }
- else {
- drupal_set_message($error, 'error');
- $errors[$clone['tag']] = t('Broken Pattern: %msg', array('%msg' => $error));
- }
-
-
- return;
- }
-
- // If tokens are enabled, apply tokens to the action values
- // before processing
- if (module_exists('token')) {
- _patterns_recurse_tokens($clone, $identifiers);
- //array_walk($clone, '_patterns_replace_tokens', $identifiers);
- }
-
- // Get the form data for the action
- $values = patterns_invoke($clone, 'build', $form_id);
-
- // Dont execute the action if a string was returned, indicating the pattern component
- // most likely handled the action on its own and this is the message to display.
- if (is_string($values)) {
- drupal_set_message($values);
- }
- else {
- // Get any extra parameters required for the action
- $params = patterns_invoke($clone, 'params', $form_id, $values);
-
- if (isset($params) && !is_array($params)) {
- $params = array($params);
- }
-
- // Execute action
- patterns_execute_action($form_id, $values, $params);
-
- if (form_get_errors()) {
- $descriptions = patterns_invoke($clone, 'actions');
-
- drupal_set_message(t('An error occured running action #%num (%action)', array('%num' => $key+1, '%action' => $descriptions[$form_id])), 'error');
- $error = true;
- break;
- }
- }
-
- patterns_invoke($clone, 'cleanup', $form_id);
-
- // Clear the cache in case it causes problems
- cache_clear_all();
- }
- if ($error) {
- break;
- }
-
- // Get any primary identifiers from the action for further actions to take advantage of
- $id = null;
- $id = patterns_invoke($clone, 'identifier', $form_id);
- if (isset($id)) {
- $identifiers[$key+1] = $id;
- }
- }
-
- if (empty($errors)) {
- if ($reverse) {
- if ($modules) {
- // Modules need to be disabled last so the rest of the pattern
- // can reverse itself properly
- $error = patterns_execute_pattern($modules, $reverse, $interact);
- }
-
- // Mark pattern as disabled
- if ($pid) {
- db_query('UPDATE {patterns} SET status = 0 WHERE pid = %d', $pid);
- }
-
- drupal_set_message(t('Pattern reversed successfully.'));
- }
- else {
- // Mark pattern as enabled
- if ($pid) {
- db_query('UPDATE {patterns} SET status = 1, enabled = "%s" WHERE pid = %d', time(), $pid);
- }
-
- drupal_set_message(t('Pattern ran successfully.'));
- }
- }
-
- return !$error;
- }
-
- /**
- * Execute an action
- */
- function patterns_execute_action($form_id, $values, $params) {
- // Make sure we always have a clear cache for everything
- $result = db_query('SHOW TABLES LIKE "cache_%"');
-
- while ($table = db_fetch_array($result)) {
- $table = current($table);
- cache_clear_all(null, $table);
- }
-
- $args = array($form_id, $values);
-
- if (is_array($params)) {
- $args = array_merge($args, $params);
- }
-
- patterns_executing(true);
-
- //$form = call_user_func_array('drupal_retrieve_form', $args);
- //$form['#post'] = $values;
- //$return = drupal_process_form($form_id, $form);
-
- $return = call_user_func_array('drupal_execute', $args);
-
- patterns_executing(false);
-
- return $return;
- }
-
- function patterns_executing($b = null) {
- static $executing = false;
-
- if (is_bool($b)) {
- $executing = $b;
- }
-
- return $executing;
- }
-
- function patterns_rearrange_data($pattern) {
- foreach($pattern as $key => $value) {
- if (is_string($key)) {
- unset($pattern[$key]);
- }
- else {
- if ($value['tag'] == 'actions') {
- $pattern[$key] = patterns_rearrange_data($value);
- $pattern[$key]['tag'] = 'actions';
- }
- else {
- $pattern[$key] = _patterns_rearrange_data($value);
- }
- }
- }
-
- return $pattern;
- }
-
- /**
- * Return a list of modules for a pattern
- */
- function patterns_modules($pattern) {
- $pattern = $pattern->pattern;
-
- for($i=0;$tag=$pattern[$i];$i++) {
- if ($tag['tag'] == 'modules') {
- unset($tag['tag']);
- $modules = $tag;
- break;
- }
- }
-
- return $modules;
- }
-
- function patterns_process_modules($modules, $op = 'enable') {
- // Enable at the beginning of the pattern. Disable at the end.
- for($i=0;$module=$modules[$i];$i++) {
- if (($op == 'enable' && $module['delete']) || ($op == 'disable' && !$module['delete'])) {
- unset($modules[$i]);
- }
- }
-
- patterns_invoke($empty, 'tag modules');
- $error = patterns_invoke($modules, 'pre-validate');
-
- // Error validating modules
- if ($error) {
- return $error;
- }
-
- patterns_invoke($modules, 'prepare');
- }
-
- function patterns_invoke(&$data, $op, $form_id = null, $a = null) {
- static $tag_modules;
-
- if (!is_array($tag_modules) || $op == 'tag modules') {
- // Get a list of tags and their modules
- foreach(module_implements('patterns') as $module) {
- $tags = module_invoke($module, 'patterns', 'tags');
-
- foreach($tags as $tag => $value) {
- if (is_array($value)) {
- $tag_modules[$tag] = $module;
- }
- else {
- $tag_modules[$value] = $module;
- }
- }
- }
- }
-
- // If you just want the modules list
- if ($op == 'tag modules') {
- return $tag_modules;
- }
-
- $tag = $data['tag'];
- unset($data['tag']);
-
- $module = $tag_modules[$tag];
- $func = $module .'_patterns';
-
- if (function_exists($func)) {
- if ($form_id) {
- $return = $func($op, $form_id, $data, $a);
- }
- else {
- $return = $func($op, $tag, $data, $a);
- }
- }
-
- $data['tag'] = $tag;
- return $return;
- }
-
- function _patterns_rearrange_data($data, $parent = '') {
- $numeric = array();
- $count=0;
-
- foreach($data as $key => $value) {
- if ($value['value'] == 'false') {
- $value['value'] = false;
- }
- else if ($value['value'] == 'true') {
- $value['value'] = true;
- }
-
- if (is_numeric($key) && is_array($value) && count($value) == 2 && isset($value['tag']) && isset($value['value'])) {
- unset($data[$key]);
- if (isset($data[$value['tag']])) {
- $numeric[] = $value['tag'];
- $data[$count++] = $data[$value['tag']];
- $data[$count++] = $value['value'];
- unset($data[$value['tag']]);
- }
- else if (in_array($value['tag'], $numeric)) {
- $data[$count++] = $value['value'];
- }
- else {
- $data[$value['tag']] = $value['value'];
- }
- }
- else if (is_numeric($key)) {
- $tag = $value['tag'];
- unset($value['tag']);
- $data[$tag][] = _patterns_rearrange_data($value, $tag);
- unset($data[$key]);
- }
- }
-
- foreach($data as $key => $value) {
- if (is_array($value) && count($value) == 1 && $value[0]) {
- $data[$key] = $data[$key][0];
- }
- }
-
- return $data;
- }
-
-
- function patterns_form_alter($form_id, &$form) {
- if (user_access('administer patterns') && variable_get('patterns_form_helper', true)) {
- $form['#after_build'][] = 'patterns_form_helper';
- }
- }
-
- function patterns_form_helper($form) {
- global $form_submitted;
- // static $form_id, $form_values;
- $args = func_get_args();
-
- if (!$form_id && $form_submitted) {
- $form_values = $args[1];
- $form_id = $form_values['form_id'];
-
- $_SESSION['patterns_form_helper'] = array('form_id' => $form_id, 'form_values' => $form_values);
- }
-
- return $form;
- }
-
- function patterns_init() {
- if (variable_get('patterns_form_helper', true) && $_SESSION['patterns_form_helper']) {
- drupal_add_css(drupal_get_path('module', 'patterns') .'/patterns.css');
- drupal_add_js(drupal_get_path('module', 'patterns') .'/patterns.js');
- }
- }
-
- function patterns_exit($destination = null) {
- if (variable_get('patterns_form_helper', true) && $_SESSION['patterns_form_helper'] && !$destination) {
- print theme('patterns_form_helper', $_SESSION['patterns_form_helper']['form_id'], $_SESSION['patterns_form_helper']['form_values']);
- //unset($_SESSION['patterns_form_helper']);
- }
- }
-
- /**
- * Implementation of hook_token_values()
- *
- * @If these get implementated directly into token.module, this should be removed
- */
- function patterns_token_values($type, $object = NULL, $options = array()) {
- if ($type == 'global') {
- $path = conf_path();
- $tokens['confpath'] = $path;
- return $tokens;
- }
- }
-
- /**
- * Function callback
- */
- function _patterns_modify_value(&$form) {
- foreach($form as $key => $value) {
- if (is_array($value) && isset($value['#type']) && $value['#type'] == 'value') {
- $form[$key]['#default_value'] = $value['#value'];
- unset($form[$key]['#value']);
- }
- else if (is_array($value)) {
- _patterns_modify_value($form[$key]);
- }
- }
- }
-
- /**
- * Recurse an array and replace with tokens
- * @ This is used instead of array_walk_recursive because
- * of some strange issues with token_get_values failing.
- */
- function _patterns_recurse_tokens(&$object, $identifiers) {
- foreach($object as $key => $value) {
- if (is_array($value)) {
- _patterns_recurse_tokens($object[$key], $identifiers);
- }
- else if (is_scalar($value)) {
- $old_key = $key;
- _patterns_replace_tokens($object[$key], $key, $identifiers);
-
- // The key was changed, change it
- if ($old_key != $key) {
- $keys = array_keys($object);
- $keys[array_search($old_key, $keys)] = $key;
- $object = array_combine($keys, array_values($object));
- }
- }
- }
- }
-
- /**
- * Array walk callback to replace tokens inside form values
- */
- function _patterns_replace_tokens(&$a, &$b, $identifiers = array()) {
- static $count = 0;
-
- // Replace IDs with identifiers from the current executing pattern
- if (preg_match('/@([0-9]+)@/', $a, $match)) {
- $a = str_replace($match[0], $identifiers[$match[1]], $a);
- }
- if (preg_match('/__([0-9]+)__/', $b, $match)) {
- $b = str_replace($match[0], $identifiers[$match[1]], $a);
- }
-
- // Replace tokens
- $a = token_replace($a, 'global', NULL, '@', '@');
- $b = token_replace($b, 'global', NULL, '__', '__');
- }
-
- /**
- * Check if a .htaccess file exists to prevent downloads of pattern files
- */
- function _patterns_check_file_dir() {
- return false;
- $path = file_create_path(variable_get('patterns_save_xml', 'patterns'));
-
- if (!is_file($path .'/.htaccess')) {
- $content = '# Prevent downloading site patterns
- <FilesMatch "\.xml$">
- Order allow,deny
- </FilesMatch>
- ';
- file_save_data($content, $path .'/.htaccess');
- }
- }
-
- function theme_patterns_form_helper_menu($forms) {
- $output = '<ul class="patterns-form-menu">';
-
- foreach ($forms as $form_id => $values) {
- $output .= '<li class="patterns-form-menu-item">'. $form_id .'</li>';
- }
-
- $output .= '</li>';
-
- return $output;
- }
-
- function theme_patterns_form_helper($form_id, $values) {
- $output = '<div class="patterns-form" id="patterns-form-'. $form_id .'">';
-
- $output .= '<div class="patterns-form-title">'. t('Form values for %key', array('%key' => $form_id)) .'</div>';
-
- foreach($values as $key => $value) {
- $output .= '<div class="patterns-form-item"><div class="patterns-form-key">'. $key .' => </div>';
- $output .= '<div class="patterns-form-value">'. print_r($value, true) .'</div></div>';
- }
-
- $output .= '</div>';
-
- return $output;
- }
-