You are here

patterns.module in Patterns 7

File

patterns.module
View source
<?php

/**
 * @file
 * Enables extremely simple adding/removing features to your site with minimal to no configuration.
 *
 * It works by reading a pattern file and then creating a form which contains already the values for
 * specific elements. The form is then validated and submitted, exactly as the 'submit' button was
 * pressed from the browser window.
 *
 * The following steps (operations) are performed sequentially:
 *
 * * Preparation Phase
 * -------------------
 *
 *  - 'prepare':      - Transform the patterns tags into the actual element of the form to be submitted.
 *                    - Add eventual standard values which are missing (this makes writing the pattern
 *                      easier and more synthetic).
 *                    - No errors are raised here.
 *
 *  - 'validate':      - Validate the data for the form_id and raise errors, if any.
 *
 *
 * * Batch Phase
 * -----------------
 *
 * - 'callbacks'      - Return the array of functions that need to be called sequentially in later stages.
 *                      Those functions which are among the form_ids returned by hook_patterns() will be
 *                      treated as form functions, while others will be treated as regular callback functions.
 *
 * - 'build':         - Get the form data for the action. This can either just be the form values, or
 *                      it can be the full //form_state object//.
 *
 * - 'params':        - Get any extra parameters required by the form function.
 *
 *
 * * Execution Phase
 * -----------------
 *  drupal_form_submit() is called. The arguments are supplied to the form (is it is expects extra arguments)
 *  and the data is passed to the form callback.
 *
 *
 * - 'cleanup':       - cleanup operations are performed, if any.
 *
 *
 * * Post Batch
 * ---------------
 *
 * // NOT USED NOW: Check if we need it later
 * - 'identifiers':    - ????? Get any primary identifiers from the action for further actions to take advantage of
 *
 */
module_load_include('inc', 'patterns', 'includes/variables');
module_load_include('inc', 'patterns', 'includes/config');
module_load_include('inc', 'patterns', 'includes/path');
module_load_include('inc', 'patterns', 'includes/utils');
module_load_include('inc', 'patterns', 'includes/tagmodules');
module_load_include('inc', 'patterns', 'includes/db');
module_load_include('inc', 'patterns', 'includes/error');
module_load_include('inc', 'patterns', 'includes/forms/forms');
module_load_include('inc', 'patterns', 'includes/parser/parser');
module_load_include('inc', 'patterns', 'includes/io/io');
module_load_include('inc', 'patterns', 'includes/io/import');
module_load_include('inc', 'patterns', 'includes/io/download');
module_load_include('inc', 'patterns', 'includes/api/api');
module_load_include('inc', 'patterns', 'theme/theme');

// TODO: Check
module_load_include('inc', 'patterns', 'includes/feed');
module_load_include('inc', 'patterns', 'includes/servers');
module_load_include('inc', 'patterns', 'includes/forms/first_install');

/**
 * @todo:
 * @ 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
 * @ Enable interactive actions by allowing patterns to resume from a saved position
 * @ Allow resuming failed patterns
 * @ Create a Pattern class (?) check performance
 */

//variable_set(PATTERNS_FIRST_INSTALL, TRUE);

/**
 * Implements hook_menu().
 */
function patterns_menu() {
  $items = array();
  $first_install = variable_get(PATTERNS_FIRST_INSTALL, TRUE);
  if ($first_install) {
    module_load_include('inc', 'patterns', 'includes/forms/first_install');
    $items['admin/patterns'] = array(
      'title' => 'Patterns',
      'description' => 'Patterns was just installed or important updates have been found',
      'page callback' => 'patterns_first_install_page',
      'access arguments' => array(
        'administer patterns',
      ),
    );
    return $items;
  }
  $items['admin/patterns'] = array(
    'title' => 'Patterns',
    'description' => 'Administer patterns available for your site',
    'page callback' => 'patterns_list',
    'access arguments' => array(
      'administer patterns',
    ),
  );
  $items['admin/patterns/list'] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  if (patterns_utils_is_public_page_enabled()) {
    $items[variable_get('patterns_public_url', '')] = array(
      'title' => 'Public',
      'type' => MENU_LOCAL_TASK,
      'description' => 'Patterns',
      'page callback' => 'patterns_list_public',
      'access arguments' => array(
        'public patterns',
      ),
      'weight' => -9,
    );
  }

  // MAIN ROUTES
  $items['admin/patterns/enable'] = array(
    'title' => 'Enable Pattern',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'patterns_enable_pattern',
    ),
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/patterns/edit/%'] = array(
    'title' => 'Edit Pattern',
    'page callback' => 'patterns_edit_page',
    'page arguments' => array(
      3,
    ),
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/patterns/quickrun'] = array(
    'title' => 'Quick Run',
    'page callback' => 'patterns_editor',
    'page arguments' => array(
      'patterns_quickrun',
    ),
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/patterns/lab'] = array(
    'title' => 'Lab',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'patterns_lab',
    ),
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/patterns/create/%'] = array(
    'title' => 'Create a new Pattern',
    'page callback' => 'patterns_editor_create_page',
    'page arguments' => array(
      3,
    ),
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );

  // SETTINGS
  $items['admin/patterns/settings'] = array(
    'title' => 'Settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'patterns_config_settings',
    ),
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 10,
  );

  // TRASH, RESTORE, REMOVE
  $items['admin/patterns/trash'] = array(
    'title' => 'Move Pattern to Recycle Bin',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'patterns_trash_pattern',
    ),
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/patterns/restore'] = array(
    'title' => 'Restore a pattern from the recycle bin',
    'page callback' => 'patterns_restore_pattern',
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/patterns/remove'] = array(
    'title' => 'Remove Pattern',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'patterns_remove_pattern',
    ),
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );

  // UTILS
  $items['admin/patterns/refresh'] = array(
    'title' => 'Refresh Patterns',
    'page callback' => 'patterns_io_get_patterns_service',
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/patterns/validate'] = array(
    'title' => 'Validate Pattern YAML Source',
    'page callback' => 'patterns_validate_service',
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );

  // SERVICES
  $items['patterns/patterns.xml'] = array(
    'title' => 'Published Patterns',
    'page callback' => 'patterns_feed',
    'access arguments' => array(
      'get patterns',
    ),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['patterns/get'] = array(
    'title' => 'Download Pattern Source',
    'page callback' => 'patterns_io_get_pattern_service',
    'access arguments' => array(
      'get patterns',
    ),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );

  // IMPORT
  $items['admin/patterns/import'] = array(
    'title' => 'Import',
    'page callback' => 'patterns_editor',
    'page arguments' => array(
      'patterns_import_source',
    ),
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/patterns/import/source'] = array(
    'title' => 'Import Source Code',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/patterns/import/file'] = array(
    'title' => 'Import File',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'patterns_import_file',
    ),
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/patterns/import/url'] = array(
    'title' => 'Import from URL',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'patterns_import_url',
    ),
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_LOCAL_TASK,
  );

  // PUBLISH / UNPUBLISH
  $items['admin/patterns/publish'] = array(
    'title' => 'Publish Pattern',
    'page callback' => 'patterns_publish_pattern',
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/patterns/unpublish'] = array(
    'title' => 'Unpublish Pattern',
    'page callback' => 'patterns_unpublish_pattern',
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );

  // UTILITY
  $items['admin/patterns/info'] = array(
    'title' => 'Info',
    'page callback' => 'patterns_info_page',
    'access arguments' => array(
      'administer patterns',
    ),
    'weight' => 15,
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/patterns/check'] = array(
    'title' => 'Pattern Modules',
    'page callback' => 'patterns_modules_page',
    'access arguments' => array(
      'administer patterns',
    ),
    'type' => MENU_CALLBACK,
  );

  // Disable for now
  //  $items['admin/patterns/server'] = array(
  //    'title' => 'Patterns Server',
  //    'page callback' => 'drupal_get_form',
  //    'page arguments' => array('patterns_import_server'),
  //    'access arguments' => array('administer patterns'),
  //    'type' => MENU_LOCAL_TASK,
  //    'weight' => -5
  //  );
  // This items were already commented.

  /////////////////////////////////////////////////////

  // Not ready
  //     $items[] = array('path' => 'admin/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/patterns/revert',
  //       'access' => user_access('administer patterns'),
  //       'title' => t('Revert Pattern'),
  //       'callback' => 'patterns_revert',
  //       'type' => MENU_CALLBACK
  //     );
  return $items;
}

/**
 * Implements hook_libraries_info().
 *
 * Requires Codemirror >= 3.01 (new path for addons).
 */
function patterns_libraries_info() {
  $libraries = array();
  $libraries['codemirror'] = array(
    'name' => 'codemirror',
    'vendor url' => 'http://codemirror.net/',
    'download url' => 'https://github.com/marijnh/CodeMirror/',
    //"version":"3.10.01"
    'version arguments' => array(
      'file' => 'package.json',
      'pattern' => '@"version":"([0-9a-zA-Z\\.-]+)"@',
    ),
    'files' => array(
      'js' => array(
        'lib/codemirror.js',
        'addon/search/search.js',
        'addon/search/searchcursor.js',
        'addon/dialog/dialog.js',
        'addon/mode/loadmode.js',
        'mode/yaml/yaml.js',
        'mode/xml/xml.js',
        'mode/php/php.js',
      ),
      'css' => array(
        'lib/codemirror.css',
        'theme/default.css',
        'addon/dialog/dialog.css',
      ),
    ),
  );
  return $libraries;
}

/**
 * First function to be called for displaying the pattern list page.
 */
function patterns_list() {
  $form = array();
  if (!patterns_parser_ready()) {
    $messages = t('No available patterns parser was found.</br>');
    $messages .= t(' Go to the !modules page to enable more Patterns parsers.', array(
      '!modules' => l(t('modules'), 'admin/modules'),
    ));
    drupal_set_message($messages, 'warning');
  }
  drupal_add_js('misc/ajax.js');
  drupal_add_css(drupal_get_path('module', 'patterns') . '/css/patterns.css');
  drupal_add_js(drupal_get_path('module', 'patterns') . '/js/patterns.js');
  $form['commands'] = array(
    '#type' => 'fieldset',
    '#title' => 'Commands',
  );
  $form['commands']['refresh'] = array(
    '#type' => 'button',
    '#value' => t('Refresh All'),
    '#attributes' => array(
      'style' => 'float:right',
      'id' => 'refresh-all',
    ),
  );
  $form['commands']['create'] = array(
    '#type' => 'button',
    '#value' => t('Create New'),
    '#attributes' => array(
      'style' => 'float:right',
    ),
    '#prefix' => '<a href="' . url('admin/patterns/import') . '">',
    '#suffix' => '</a>',
  );
  $form['commands']['quickrun'] = array(
    '#type' => 'button',
    '#value' => t('Quick Run'),
    '#attributes' => array(
      'style' => 'float:right',
    ),
    '#prefix' => '<a href="' . url('admin/patterns/quickrun') . '">',
    '#suffix' => '</a>',
  );
  $output = drupal_render($form);

  // Load the the pattern handlers from compontents directory
  // or from modules which implement hook_patterns().
  if (variable_get('patterns_refresh_components', TRUE)) {
    patterns_io_load_components();
  }

  // Load the patterns from database.
  $patterns = patterns_io_get_patterns(variable_get('patterns_refresh_from_fs', TRUE));
  $output .= theme('patterns_list', $patterns);
  return $output;
}
function patterns_list_public() {
  drupal_add_css(drupal_get_path('module', 'patterns') . '/css/patterns.css');
  drupal_add_js(drupal_get_path('module', 'patterns') . '/js/patterns.js');
  $patterns = patterns_io_get_public_patterns(variable_get('patterns_refresh_from_fs', TRUE), FALSE);
  $output = theme('patterns_list', $patterns);
  return $output;
}

/**
 * Form constructor for the Patterns enabling form.
 *
 * It prompts the user with a submenu of options regarding default behavior with
 * sub-patterns.
 *
 * @TODO Find a way to skip this in pattern_list, to quickly execute the pattern.
 *
 * @param integer $pid The ID of the Pattern to enable.
 *
 * @see patterns_enable_pattern_submit()
 * @ingroup forms
 */
function patterns_enable_pattern($form, &$form_state, $pid) {
  $form = array();
  if (!patterns_parser_ready()) {
    $messages = t('No available patterns parser was found.</br>');
    $messages .= t(' Go to the !modules page to enable more Patterns parsers.', array(
      '!modules' => l(t('modules'), 'admin/modules'),
    ));
    drupal_set_message($messages, 'warning');
    return $form;
  }
  $pattern = patterns_utils_if_invalid_go_back($pid);
  if (patterns_db_is_pattern_updated($pid)) {
    $form['modified'] = array(
      '#markup' => t('A more recent version of this pattern was found in the file system. The version stored in the database is going to be executed. If unsure, ') . ' ' . l(t('click here to check it out.'), 'admin/patterns/edit/' . $pid),
      // TODO: proper t()
      '#prefix' => '<div id="important"><strong>',
      '#suffix' => '</strong></br></br></div>',
    );
  }
  $scan = patterns_scan_pattern($pattern->pattern);
  if (!_patterns_scan_validate_patternscan($scan)) {
    $link = l('syntax and content', '/admin/patterns/edit/' . $pid);
    $errors = implode('<br/> ', _patterns_scan_analyze_patternscan($scan));
    $form['invalid'] = array(
      '#markup' => t('This pattern is invalid. Please check !stuff before running it.', array(
        '!stuff' => $link,
      )),
      '#prefix' => '<div id="important"><strong>Ooops! </strong>',
      '#suffix' => '<p>' . $errors . '</p>',
    );
    return $form;
  }
  $form['pid'] = array(
    '#type' => 'value',
    '#value' => $pid,
  );

  // Add the execution options
  $form = patterns_forms_get_execution_options($form);
  $disclaimer = t('Please be sure to backup your site before running a pattern. Patterns are not guaranteed to be reversible in case they do not execute well or if unforeseen side effects occur.');
  return confirm_form($form, t('Proceed with running pattern %pattern?', array(
    '%pattern' => $pattern->title,
  )), 'admin/patterns', $disclaimer);
}

/**
 * <hook_validate>
 */
function patterns_enable_pattern_validate($form, &$form_state) {
  if (!in_array($form_state['values']['mode'], array(
    PATTERNS_EXEC_BATCH,
    PATTERNS_EXEC_PHP,
  ))) {
    form_error($element, t('Mode must be one of \'batch\' or \'php\'.'));
  }
  return TRUE;
}

/**
 * Form submission handler for patterns_enable_pattern().
 *
 * Takes the values from the form enable_pattern and starts the whole process
 * by calling execute_pattern.
 *
 * @see patterns_enable_pattern()
 */
function patterns_enable_pattern_submit($form, &$form_state) {
  $pid = $form_state['values']['pid'];
  patterns_io_load_components();
  $pattern = patterns_get_pattern($pid);
  patterns_start_engine($pattern, $form_state['values'], $form_state['values']['mode']);
  $form_state['redirect'] = 'admin/patterns';
}

/**
 * Restores a previously trashed pattern.
 *
 * Sets the pattern status in the database to PATTERNS_STATUS_OK
 *
 * @param mixed $pid The ID of the Pattern to enable.
 *
 */
function patterns_restore_pattern($pid = NULL) {
  $pattern = patterns_utils_if_invalid_go_back($pid);
  $result = patterns_db_restore_pattern($pid);
  $params = array(
    '%name' => @$pattern->name,
  );
  if ($result) {
    drupal_set_message(t('Pattern %name was correctly restored from the trash bin.', $params));
  }
  else {
    drupal_set_message(t('An error occurred. Patterns was unable to restore pattern %name from the trash bin.', $params), 'error');
  }
  drupal_goto('admin/patterns');
}

/**
 * Form constructor for the Patterns removing form.
 *
 * Creates a confirm_form to make sure the removing operation was intentional.
 *
 * @param mixed $pid The ID of the Pattern to enable.
 *
 * @see patterns_remove_pattern_submit()
 * @ingroup forms
 */
function patterns_trash_pattern($form, &$form_state, $pid = NULL) {
  $pattern = patterns_utils_if_invalid_go_back($pid);
  $form['pid'] = array(
    '#type' => 'value',
    '#value' => $pid,
  );
  $form['name'] = array(
    '#type' => 'value',
    '#value' => @$pattern->name,
  );
  $disclaimer = t('Trashed patterns can be restored later on.');
  return confirm_form($form, t('Proceed with moving pattern %pattern into the trash bin?', array(
    '%pattern' => $pattern->title,
  )), 'admin/patterns', $disclaimer);
}

/**
 * Executes patterns_db_trash_pattern() and display a message.
 *
 * @see patterns_db_trash_pattern()
 */
function patterns_trash_pattern_submit($form, &$form_state) {
  $pid = $form_state['values']['pid'];
  $name = $form_state['values']['name'];
  $id = !empty($name) ? $name : $pid;
  $result = patterns_db_trash_pattern($pid);
  if (!$result) {
    drupal_set_message(t('Pattern %id could not be moved into the trash bin.', array(
      '%id' => $id,
    )), 'error');
  }
  else {
    drupal_set_message(t('Pattern %id successfully moved into the trash bin.', array(
      '%id' => $id,
    )), 'status');
  }
  $form_state['redirect'] = 'admin/patterns';
}

/**
 * Form constructor for the Patterns removing form.
 *
 * Creates a confirm_form to make sure the removing operation was intentional.
 *
 * @param mixed $pid The ID of the Pattern to enable.
 *
 * @see patterns_remove_pattern_submit()
 * @ingroup forms
 */
function patterns_remove_pattern($form, &$form_state, $pid = NULL) {
  $pattern = patterns_utils_if_invalid_go_back($pid);
  $form['pid'] = array(
    '#type' => 'value',
    '#value' => $pid,
  );
  $form['name'] = array(
    '#type' => 'value',
    '#value' => @$pattern->name,
  );
  $disclaimer = t('This operation cannot be undone. Are you sure?');
  return confirm_form($form, t('Proceed with removing pattern %pattern?', array(
    '%pattern' => $pattern->title,
  )), 'admin/patterns', $disclaimer);
}

/**
 * Form submission handler for patterns_remove_pattern().
 *
 * @see patterns_remove_pattern()
 */
function patterns_remove_pattern_submit($form, &$form_state) {
  $pid = $form_state['values']['pid'];
  $name = $form_state['values']['name'];
  $id = !empty($name) ? $name : $pid;

  // Prints output
  $result = patterns_io_remove_pattern($pid, TRUE);
  $form_state['redirect'] = 'admin/patterns';
}

/**
 * The beginning of the whole Patterns logic. Starts the execution in 'batch'
 * mode (default) or 'php' mode, which makes things easier for debugging.
 *
 * @param stdClass $pattern Pattern object as loaded by patterns_get_pattern().
 * @param array $params Parameters for executing the pattern. Array format is as follows:
 *
 * - pid => Pid of the pattern as it is in the database.
 * - run-subpatterns => ['first-update', always', 'update', 'first', 'never']
 *
 * If coming from form_confirm some other parameters are addes, such as:
 * - confirm [1]
 * - submit  [Confirm]
 * - form_build_id
 * - form_token
 * - form_id
 * - op  [Confirm]
 *
 * @param string $mode The running mode, one of {'batch', 'php'}.
 *
 * @return The result of the appropriate patterns_execute_pattern_$mode function.
 *
 * @see patterns_execute_pattern_batch()
 * @see patterns_execute_pattern_php()
 */
function patterns_start_engine($pattern, $params = array(), $mode = 'batch') {
  module_load_include('inc', 'patterns', 'includes/core/common');
  module_load_include('inc', 'patterns', 'includes/core/modules');
  module_load_include('inc', 'patterns', 'includes/core/token');
  module_load_include('inc', 'patterns', 'includes/core/' . $mode);
  if (empty($pattern)) {
    drupal_set_message(t('The pattern seems empty...I cannot run it!'));
    return FALSE;
  }

  /////////////////////////////////////////

  // Get Patterns details
  $patterns_details = patterns_parser_get_pattern_details($pattern);
  if ($patterns_details === FALSE) {
    drupal_set_message(t('Errors where found in the pattern. Aborting.'), 'error');
    return FALSE;
  }

  // TODO: handle multiple patterns correctly
  // Let us assume we have only one pattern in the returned array of details
  $pid = key($patterns_details);
  $pattern_details = current($patterns_details);
  $info = $pattern_details['info'];

  // Array of infos of multiple patterns (we have only one for now).
  $infos = array(
    $pid => $info,
  );
  $modules = $pattern_details['modules'];

  // Install modules, if necessary.
  $installed_modules = patterns_install_modules($modules);
  if (!$installed_modules['success']) {
    drupal_set_message($installed_modules['error_message'], 'error');
    return FALSE;
  }
  $sections = $pattern_details['sections'];

  // Building execution parameters
  if (!isset($params['quickrun']) || !$params['quickrun']) {
    if (!is_object($pattern)) {
      $pattern = patterns_get_pattern($pattern);
      if (!$pattern) {
        return FALSE;
      }
    }
  }

  // This is needed for the Patterns installation profile.
  if (isset($params['run-subpatterns'])) {
    $pattern->subpatterns_run_mode = $params['run-subpatterns'];
  }

  // Ste: Actions_map seems to be used with includes. Probably we do not need it now.

  //$actions_map = array('patterns' => $pattern_details['info'], 'map' => $pattern_details['actions_map']);

  // TODO: Patterns details was returning this in case of recursive includes.
  //  $result['actions_map'][] = array(
  //          'pid'   => $pattern->pid,
  //          'index' => $key,
  //        );
  $actions_map = array(
    'patterns' => $infos,
    'map' => NULL,
  );

  // Fix this.

  ////////////////////////////////////////
  drupal_set_time_limit(0);
  $fun = 'patterns_execute_pattern_' . $mode;

  // Execute the Pattern using the selected mode.
  $fun($pattern, $params, $patterns_details, $actions_map);

  // Disable the modules enabled just for executing the pattern
  if (!empty($installed_modules['installed'])) {
    patterns_disable_modules($installed_modules['installed']);
  }
  return TRUE;
}

/**
 * Execute hook_patterns with the given operation and return the results.
 *
 * It also builds up, if necessary, the list of available action-tags and
 * binds them to the correct module.
 *
 * @param string $hook
 *   The hook to be called. One of {'prepare', 'validate', 'callbacks', 'build',
 *   'params', 'cleanup'}.
 * @param string $action
 *   The action to be executed. One of {'create', 'modify', 'delete'}.
 * @param array $data
 *   (optional) The data supplied as an associative array. Defaults NULL.
 * @param mixed $form_id
 *   (optional) The name of the form to be used. Defaults NULL.
 * @param array $extra
 *   (optional) Extra parameters to pass to the function. Defaults NULL.
 *
 * @return array $return An array packed by patterns_results().
 */
function patterns_invoke($hook, $action, &$data = NULL, $form_id = NULL, &$extra = NULL) {
  $tag_modules = patterns_tagmodules_get_index($data);

  // TODO: check the parameters
  // Unsetting it while the module performs operations.
  $tag = $data['tag'];
  unset($data['tag']);
  if (isset($tag_modules[$tag])) {
    $module = $tag_modules[$tag]['module'];
    $func = $module . '_patterns_' . $hook;
    if (function_exists($func)) {
      if ($form_id) {

        // We are past hook_patterns_build().
        $return = $func($action, $form_id, $data, $extra);
      }
      else {
        $return = $func($action, $tag, $data, $extra);
      }
    }
    else {
      $msg = t('Function %func not found. Hook %hook skipped', array(
        '%func' => $func,
        '%hook' => $hook,
      ));
      $return = patterns_results(PATTERNS_SUCCESS, $msg);
    }
  }
  else {
    $msg = t('Required patterns tag %tag is not provided by any component.', array(
      '%tag' => $tag,
    ));
    $return = patterns_results(PATTERNS_ERR, $msg);
  }

  // Check if the output is correctly formatted, and eventually try to correct it.
  if (!_patterns_is_patterns_results($return)) {
    if ($return === TRUE) {
      $return = array(
        'status' => PATTERNS_SUCCESS,
      );
    }
    elseif ($return === FALSE) {
      $msg = t('Unspecified error occurred in %func.', array(
        '%func' => $func,
      ));
      $return = patterns_results(PATTERNS_ERR, $msg);
    }
    else {
      $msg = t('The return value of %func is not properly formatted.', array(
        '%func' => $func,
      ));
      $return = patterns_results(PATTERNS_ERR, $msg);
    }
  }
  $return['details']['action'] = $action;
  $return['details']['hook'] = $hook;
  $return['details']['function'] = $form_id;
  $data['tag'] = $tag;
  return $return;
}

/**
 * Constructs a Pattern object with default standard values.
 *
 * @param array $pattern Input array structure of the pattern. Defaults to an empty array.
 * @return StdClass $pattern The result object.
 */
function patterns_get_pattern_obj($pattern = array()) {
  $pattern = is_object($pattern) ? $pattern : (object) $pattern;
  $defaults = array(
    'pid' => '',
    'name' => '',
    'status' => 0,
    // disabled
    'public' => 0,
    'updated' => '',
    // when enabled
    'enabled' => '',
    // when enabled
    'title' => t('New Pattern'),
    'pattern' => '',
  );
  foreach ($defaults as $key => $value) {
    if (!isset($pattern->{$key})) {
      $pattern->{$key} = $value;
    }
  }
  return $pattern;
}

/**
 * Returns the current option for included patterns, as it is in
 * the database. If the option stored in the database is not valid,
 * by default patterns' inclusion is disabled.
 *
 */
function patterns_get_include_mode($mode = NULL) {
  if (!is_null($mode)) {
    if (patterns_parser_is_valid_include_mode($mode)) {
      return $mode;
    }
  }
  $mode = variable_get('patterns_default_include_mode', PATTERNS_INCLUDE_NEVER);
  if (!patterns_parser_is_valid_include_mode($mode)) {
    $mode = PATTERNS_INCLUDE_NEVER;
  }
  return $mode;
}

/**
 * Extract all the values of the modules tag from a pattern and
 * returns them as an array.
 *
 * If succesfull returns TRUE, FALSE otherwise.
 *
 * @param mixed $pattern A pattern object, an array representing
 *  the pattern object, a numeric id or alphanumeric name of
 *  the pattern as it is in the database. Array is the only input
 *  that does not get further converted.
 *
 */
function patterns_install_modules_in_pattern($pattern) {
  $pattern = _patterns_db_get_pattern_array($pattern);
  if (!$pattern) {
    return FALSE;
  }
  $modules = patterns_parser_extract_modules($pattern);
  $results = patterns_install_modules($modules);
  if (!$results['success']) {

    // TODO: handle properly.
    drupal_set_message($results['error_message'], 'error');
    return FALSE;
  }
  return TRUE;
}

Functions

Namesort descending Description
patterns_enable_pattern Form constructor for the Patterns enabling form.
patterns_enable_pattern_submit Form submission handler for patterns_enable_pattern().
patterns_enable_pattern_validate <hook_validate>
patterns_get_include_mode Returns the current option for included patterns, as it is in the database. If the option stored in the database is not valid, by default patterns' inclusion is disabled.
patterns_get_pattern_obj Constructs a Pattern object with default standard values.
patterns_install_modules_in_pattern Extract all the values of the modules tag from a pattern and returns them as an array.
patterns_invoke Execute hook_patterns with the given operation and return the results.
patterns_libraries_info Implements hook_libraries_info().
patterns_list First function to be called for displaying the pattern list page.
patterns_list_public
patterns_menu Implements hook_menu().
patterns_remove_pattern Form constructor for the Patterns removing form.
patterns_remove_pattern_submit Form submission handler for patterns_remove_pattern().
patterns_restore_pattern Restores a previously trashed pattern.
patterns_start_engine The beginning of the whole Patterns logic. Starts the execution in 'batch' mode (default) or 'php' mode, which makes things easier for debugging.
patterns_trash_pattern Form constructor for the Patterns removing form.
patterns_trash_pattern_submit Executes patterns_db_trash_pattern() and display a message.