You are here

path_metatags_ui.module in Path metatags 7

Provide user interface for CRUD operations with path metatags.

File

path_metatags_ui/path_metatags_ui.module
View source
<?php

/**
 * @file
 * Provide user interface for CRUD operations with path metatags.
 */

/**
 * Implements hook_menu().
 */
function path_metatags_ui_menu() {
  $items['admin/structure/path-metatags'] = array(
    'title' => 'Path metatags',
    'description' => 'Manage Metatags for your site\'s pages.',
    'access arguments' => array(
      'administer path metatags',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'path_metatags_ui_metatags_list',
    ),
    'file' => 'path_metatags_ui.admin.inc',
  );
  $items['admin/structure/path-metatags/list'] = array(
    'title' => 'Path Metatags list',
    'file' => 'path_metatags_ui.admin.inc',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 0,
  );
  $items['admin/structure/path-metatags/settings'] = array(
    'title' => 'Path Metatags settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'path_metatags_ui_settings',
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'file' => 'path_metatags_ui.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 1,
  );
  $items['admin/structure/path-metatags/create'] = array(
    'title' => 'Create new Path Metatags',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'path_metatags_ui_add_form',
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'type' => MENU_LOCAL_ACTION,
  );
  $items['admin/structure/path-metatags/clone/%path_metatags_ui_cache'] = array(
    'title' => 'Clone Path Metatags',
    'page callback' => 'path_metatags_clone_metatags',
    'page arguments' => array(
      4,
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'file' => 'path_metatags_ui.admin.inc',
  );
  $items['admin/structure/path-metatags/export/%path_metatags_ui_cache'] = array(
    'title' => 'Export Path Metatags',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'path_metatags_export_form',
      4,
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'file' => 'path_metatags_ui.admin.inc',
  );
  $items['admin/structure/path-metatags/import'] = array(
    'title' => 'Import Path Metatags',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'path_metatags_import_form',
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'file' => 'path_metatags_ui.admin.inc',
    'type' => MENU_LOCAL_ACTION,
  );
  $items['admin/structure/path-metatags/edit/%path_metatags_ui_cache'] = array(
    'title' => 'Edit Path Metatags',
    'page callback' => 'path_metatags_ui_edit_page',
    'page arguments' => array(
      4,
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'file' => 'path_metatags_ui.pages.inc',
  );
  $items['admin/structure/path-metatags/edit/%path_metatags_ui_cache/main'] = array(
    'title' => 'Main',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/structure/path-metatags/edit/%path_metatags_ui_cache/ajax/%'] = array(
    'page callback' => 'path_metatags_ui_edit_form_ajax_links',
    'page arguments' => array(
      4,
      6,
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'delivery callback' => 'ajax_deliver',
    'theme callback' => 'ajax_base_page_theme',
    'type' => MENU_CALLBACK,
    'file' => 'path_metatags_ui.pages.inc',
  );
  $items['admin/structure/path-metatags/delete/%path_metatags_ui_cache'] = array(
    'title' => 'Delete Path Metatags',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'path_metatags_ui_delete_form',
      4,
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
  );
  $items['admin/structure/path-metatags/disable/%path_metatags_ui_cache'] = array(
    'title' => 'Disable Path Metatags',
    'page callback' => 'path_metatags_ui_disable_metatags_page',
    'page arguments' => array(
      4,
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'path_metatags_ui.pages.inc',
  );
  $items['admin/structure/path-metatags/enable/%path_metatags_ui_cache'] = array(
    'title' => 'Enable Path Metatags',
    'page callback' => 'path_metatags_ui_enable_metatags_page',
    'page arguments' => array(
      4,
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'path_metatags_ui.pages.inc',
  );
  $items['admin/structure/path-metatags/arguments/settings/%/%'] = array(
    'page callback' => 'path_metatags_ui_configure_arguments_page',
    'page arguments' => array(
      5,
      6,
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'delivery callback' => 'ajax_deliver',
    'theme callback' => 'ajax_base_page_theme',
    'type' => MENU_CALLBACK,
    'file' => 'path_metatags_ui.pages.inc',
  );
  $items['admin/structure/path-metatags/arguments/change/%/%'] = array(
    'page callback' => 'path_metatags_ui_change_arguments_page',
    'page arguments' => array(
      5,
      6,
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'delivery callback' => 'ajax_deliver',
    'theme callback' => 'ajax_base_page_theme',
    'type' => MENU_CALLBACK,
    'file' => 'path_metatags_ui.pages.inc',
  );
  $items['path_metatags_ui/%path_metatags_ui_cache'] = array(
    'page callback' => 'path_metatags_ui_tokens_ajax_autocomplete',
    'page arguments' => array(
      1,
      2,
    ),
    'access arguments' => array(
      'administer path metatags',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'path_metatags_ui.pages.inc',
  );
  return $items;
}

/**
 * Implements hook_permission().
 */
function path_metatags_ui_permission() {
  return array(
    'administer path metatags' => array(
      'title' => t('Administer path metatags'),
      'description' => t('Allows to set or delete path metatags'),
    ),
  );
}

/**
 * Implementats hook_theme().
 */
function path_metatags_ui_theme() {
  return array(
    'path_metatags_ui_form_step_arguments_selection_table' => array(
      'render element' => 'form',
      'file' => 'path_metatags_ui.theme.inc',
    ),
    'path_metatags_ui_metatags_list' => array(
      'render element' => 'form',
      'file' => 'path_metatags_ui.theme.inc',
    ),
    'path_metatags_ui_edit_form' => array(
      'render element' => 'form',
      'file' => 'path_metatags_ui.theme.inc',
    ),
    'path_metatags_ui_add_form' => array(
      'render element' => 'form',
      'file' => 'path_metatags_ui.theme.inc',
    ),
  );
}

/**
 * Provides multistep form for create/edit path metatags.
 *
 * @param $form
 * @param $form_state
 * @param bool $show_navigation
 * @param string $class
 * @return array
 */
function path_metatags_ui_add_form($form, &$form_state, $show_navigation = TRUE, $class = '') {
  $form['#prefix'] = '<div id="path-metatags-ui-form-wrapper" class="' . $class . '">';
  $form['#suffix'] = '</div>';
  if (!isset($form_state['storage']['step'])) {
    $form_state['storage']['step'] = 1;
  }
  $step = $form_state['storage']['step'];
  if ($show_navigation) {
    $navigation = array();
    $navigation[] = t('Basic settings');
    $navigation[] = t('Choose arguments');
    $navigation[] = t('Selection rules');
    $navigation[] = t('Metatags settings');

    // Wrap current navigation item in <span>.
    $navigation[$step - 1] = '<span class="current-nav-item">' . $navigation[$step - 1] . '</span>';
    $form['navigation'] = array(
      '#type' => 'item',
      '#markup' => implode(' » ', $navigation),
    );
  }
  $form_build_function = '';
  switch ($step) {
    case 1:
      $form_build_function = 'basic_settings';
      break;
    case 2:
      $form_build_function = 'arguments_selection';
      break;
    case 3:
      $form_build_function = 'selection_rules';
      break;
    case 4:
      $form_build_function = 'metatags_settings';
      break;
  }

  // Provide default submit callback for all forms.
  $form['#submit'][] = 'path_metatags_ui_add_form_submit';

  // Attach styles to form.
  $form['#attached']['css'][] = drupal_get_path('module', 'path_metatags_ui') . '/css/path_metatags_ui.css';

  // Build form according to current step.
  $function = '_path_metatags_ui_form_step_' . $form_build_function;
  if (function_exists($function)) {
    $function($form, $form_state);
  }
  return $form;
}

/**
 * Submit callback for path_metatags_ui_add_form.
 *
 * @param $form
 * @param $form_state
 */
function path_metatags_ui_add_form_submit($form, &$form_state) {
  $values = $form_state['values'];
  if (!empty($form_state['storage']['machine_name']) && !empty($values['machine_name'])) {

    // Move temporary cache to new storage if machine name was changed..
    if ($form_state['storage']['machine_name'] != $values['machine_name']) {
      $metatags = path_metatags_object_cache_get($form_state['storage']['machine_name']);
      path_metatags_object_cache_clear($form_state['storage']['machine_name']);
      path_metatags_object_cache_set($values['machine_name'], $metatags);
      $form_state['storage']['machine_name'] = $values['machine_name'];
    }
  }
  elseif (isset($values['machine_name'])) {

    // Save machine name in form storage.
    $form_state['storage']['machine_name'] = $values['machine_name'];
  }

  // Get current metatags machine name.
  $machine_name = $form_state['storage']['machine_name'];

  // Update cache.
  $metatags = path_metatags_object_cache_get($machine_name);
  if (empty($metatags)) {

    // Initialize cache data.
    $metatags = array();
  }

  // Merge data into cache storage.
  $metatags = array_merge((array) $metatags, $values);
  path_metatags_object_cache_set($machine_name, $metatags);

  // Switch current step.
  if (isset($values['next']) && $values['op'] == $values['next']) {
    $form_state['storage']['step']++;
  }
  elseif (isset($values['prev']) && $values['op'] == $values['prev']) {
    $form_state['storage']['step']--;
  }
  elseif (isset($values['finish']) && $values['op'] == $values['finish']) {
    $form_state['rebuild'] = FALSE;
  }
  $form_state['rebuild'] = TRUE;
}

/**
 * Ajax callback for path_metatags_ui_add_form.
 *
 * @param $form
 * @param $form_state
 * @return mixed
 */
function path_metatags_ui_add_form_ajax_callback($form, $form_state) {

  // Simple reload form.
  return $form;
}

/**
 * Ajax callback for last step of metatags creating.
 * Saves new path metatags.
 *
 * @param $form
 * @param $form_state
 * @return array
 */
function _path_metatags_ui_add_form_finish_ajax_callback($form, &$form_state) {
  if (!form_get_errors()) {

    // Save metatags from cache to permanent storage.
    $metatags = path_metatags_object_cache_get($form_state['storage']['machine_name']);
    path_metatags_save($metatags);
    drupal_set_message(filter_xss(t('New metatags !name was created.', array(
      '!name' => $metatags->name,
    ))));

    // Redirect user to page with metatags list.
    ctools_include('ajax');

    // Ajax responder not required since CTools 7.x-1.0 but this line
    // should be added to keep module works properly.
    ctools_add_js('ajax-responder');
    $commands = array();
    $commands[] = ctools_ajax_command_redirect('admin/structure/path-metatags');
    return array(
      '#type' => 'ajax',
      '#commands' => $commands,
    );
  }
  return $form;
}

/**
 * FIRST STEP.
 * Provide form with basic settings.
 *
 * @param $form
 * @param $form_state
 */
function _path_metatags_ui_form_step_basic_settings(&$form, &$form_state) {
  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Metatags definition'),
    '#description' => t('Human-readable name of metatags bundle'),
    '#required' => TRUE,
    '#maxlength' => 64,
  );
  $form['machine_name'] = array(
    '#type' => 'machine_name',
    '#maxlength' => 64,
    '#machine_name' => array(
      'exists' => 'path_metatags_load_by_name',
      'source' => array(
        'name',
      ),
    ),
  );
  $form['path'] = array(
    '#type' => 'textfield',
    '#title' => t('Path'),
    '#description' => t('The URL path where metatags will be displayed. You may create named placeholders for variable parts of the path by using %name for required elements. For example: "node/%node/foo" or "forum/%forum". These named placeholders can be turned into contexts on the arguments form.'),
    '#required' => TRUE,
    '#maxlength' => 256,
    '#field_prefix' => url(NULL, array(
      'absolute' => TRUE,
    )) . (variable_get('clean_url', 0) ? '' : '?q='),
  );
  $form['#validate'][] = '_path_metatags_ui_form_step_basic_settings_validate';
  $form['#submit'][] = '_path_metatags_ui_form_step_basic_settings_submit';
  if (!empty($form_state['storage']['machine_name'])) {
    _path_metatags_ui_form_apply_default_values($form, $form_state['storage']['machine_name']);
  }
  _path_metatags_ui_form_attach_buttons($form, array(
    'next',
  ), 'path_metatags_ui_add_form');
}

/**
 * Validate function for basic settings form (first step).
 *
 * @param $form
 * @param $form_state
 */
function _path_metatags_ui_form_step_basic_settings_validate(&$form, &$form_state) {
  $path = explode('/', $form_state['values']['path']);
  $correct_path = array();
  foreach ($path as $position => $bit) {
    if (!($trimmed_bit = trim($bit))) {
      continue;
    }

    // Correct some mistakes that user might make.
    $correct_path[] = $trimmed_bit;
    if ($trimmed_bit[0] == '%' && $position == 0) {
      form_error($form['path'], t('The first element in a path may not be dynamic.'));
    }
  }

  // Save correct path.
  $form_state['values']['path'] = implode('/', $correct_path);
  $args = array();
  foreach ($correct_path as $position => $bit) {
    if ($bit[0] == '%' && drupal_strlen($bit) === 1) {
      form_error($form['path'], t('Invalid arg <em>%</em>. All arguments must be named with keywords.'));
    }
    elseif ($bit[0] == '%') {
      $args[substr($bit, 1)]['position'] = $position;
    }
  }
  $form_state['values']['keywords'] = $args;
}

/**
 * Submit function for basic settings form (first step).
 *
 * @param $form
 * @param $form_state
 * @return mixed
 */
function _path_metatags_ui_form_step_basic_settings_submit($form, &$form_state) {

  // Check if argument matches all keywords.
  if (!empty($form_state['values']['keywords'])) {

    // Load metatags from the cache.
    $metatags = path_metatags_object_cache_get($form_state['storage']['machine_name']);
    if (empty($metatags)) {
      return;
    }
    $arguments = array();
    foreach ($form_state['values']['keywords'] as $keyword => $position) {

      // Ensure that keywords were not changed after path update.
      if (!empty($metatags->arguments[$keyword])) {
        $arguments[$keyword] = $metatags->arguments[$keyword];
      }

      // Save keyword position.
      $arguments[$keyword]['position'] = $position['position'];
    }
    $metatags->arguments = $arguments;
    path_metatags_object_cache_set($form_state['storage']['machine_name'], $metatags);
  }
}

/**
 * SECOND STEP.
 * Provide form for arguments selection.
 *
 * @param $form
 * @param $form_state
 * @return mixed
 */
function _path_metatags_ui_form_step_arguments_selection(&$form, &$form_state) {

  // Load metatags from the cache.
  $metatags = path_metatags_object_cache_get($form_state['storage']['machine_name']);
  if (empty($metatags)) {
    return;
  }
  $form['table'] = array(
    '#theme' => 'path_metatags_ui_form_step_arguments_selection_table',
    '#metatags-path' => $metatags->path,
    'argument' => array(),
  );
  foreach ($metatags->arguments as $keyword => $argument) {

    // Default context title it context doesn't attached to the argument.
    $context_title = t('No context assigned');

    // Get context from cached data if presented.
    if (!empty($argument['argument'])) {
      ctools_include('context');
      $plugin = ctools_get_argument($argument['argument']);
      if (!empty($plugin['title'])) {
        $context_title = $plugin['title'];
      }
    }
    $form['table']['argument'][$keyword]['#keyword'] = $keyword;
    $form['table']['argument'][$keyword]['#position'] = $argument['position'];
    $form['table']['argument'][$keyword]['#context'] = $context_title;

    // The URL for this ajax button.
    $form['table']['argument'][$keyword]['change-url'] = array(
      '#attributes' => array(
        'class' => array(
          "page-manager-context-{$keyword}-change-url",
        ),
      ),
      '#type' => 'hidden',
      '#value' => url('admin/structure/path-metatags/arguments/settings/' . $metatags->machine_name . '/' . $keyword, array(
        'absolute' => TRUE,
      )),
    );
    $form['table']['argument'][$keyword]['change'] = array(
      '#type' => 'submit',
      '#value' => t('Change'),
      '#attributes' => array(
        'class' => array(
          'ctools-use-modal',
        ),
      ),
      '#id' => "page-manager-context-{$keyword}-change",
    );

    // Only show the button if this has a settings form available.
    if (!empty($plugin)) {

      // The URL for this ajax button.
      $form['table']['argument'][$keyword]['settings-url'] = array(
        '#attributes' => array(
          'class' => array(
            "page-manager-context-{$keyword}-settings-url",
          ),
        ),
        '#type' => 'hidden',
        '#value' => url('admin/structure/path-metatags/arguments/change/' . $metatags->machine_name . '/' . $keyword, array(
          'absolute' => TRUE,
        )),
      );
      $form['table']['argument'][$keyword]['settings'] = array(
        '#type' => 'submit',
        '#value' => t('Settings'),
        '#attributes' => array(
          'class' => array(
            'ctools-use-modal',
          ),
        ),
        '#id' => "page-manager-context-{$keyword}-settings",
      );
    }
  }
  _path_metatags_ui_form_attach_buttons($form, array(
    'next',
    'prev',
  ), 'path_metatags_ui_add_form');
}

/**
 * THIRD STEP.
 * Provide form with selection rules.
 *
 * @param $form
 * @param $form_state
 * @return mixed
 */
function _path_metatags_ui_form_step_selection_rules(&$form, &$form_state) {

  // Load metatags from the cache.
  $metatags = path_metatags_object_cache_get($form_state['storage']['machine_name']);
  if (empty($metatags)) {
    return;
  }

  // Provide metatags access params.
  if (!empty($metatags->access)) {
    $form_state['access'] = $metatags->access;
  }
  else {
    $form_state['access'] = array();
  }

  // Include ctools libraries.
  ctools_include('modal');
  ctools_include('ajax');
  ctools_include('context');
  ctools_include('context-access-admin');
  ctools_modal_add_plugin_js(ctools_get_access_plugins());

  // Build form states for selection rules.
  $contexts = path_metatags_get_contexts_from_arguments($metatags->arguments, TRUE);
  $form_state['module'] = 'path_metatags';
  $form_state['callback argument'] = $form_state['storage']['machine_name'];
  $form_state['no buttons'] = TRUE;
  $form_state['contexts'] = $contexts;
  $form['markup'] = array(
    '#markup' => '<div class="description">' . t('If there is more than one variant on a page, when the page is visited each variant is given an opportunity to be displayed. Starting from the first variant and working to the last, each one tests to see if its selection rules will pass. The first variant that meets its criteria (as specified below) will be used.') . '</div>',
  );

  // Load ctools form for selection rules.
  $form = ctools_access_admin_form($form, $form_state);

  // Attach buttons to the form.
  _path_metatags_ui_form_attach_buttons($form, array(
    'next',
    'prev',
  ), 'path_metatags_ui_add_form');
}

/**
 * Callback for access control ajax form on behalf of context task handler (third step).
 * Returns the cached access config and contexts used.
 *
 * @param $path_name
 *   Machine name of metatags.
 * @return array
 */
function path_metatags_ctools_access_get($path_name) {
  $metatags = path_metatags_object_cache_get($path_name);
  $contexts = path_metatags_get_contexts_from_arguments($metatags->arguments, TRUE);
  $access = array();
  if (!empty($metatags->access)) {
    $access = $metatags->access;
  }
  return array(
    $access,
    $contexts,
  );
}

/**
 * Callback for access control ajax form on behalf of context task handler (third step).
 * Saves the changed access to the cache.
 *
 * @param $path_name
 *   Machine name of metatags.
 * @param $access
 *   Array with user-defines selection rules.
 */
function path_metatags_ctools_access_set($path_name, $access) {
  $data = path_metatags_object_cache_get($path_name);
  $data->access = $access;
  path_metatags_object_cache_set($path_name, $data);
}

/**
 * FOURTH STEP.
 * Provide form for metatags settings.
 *
 * @param $form
 * @param $form_state
 * @return mixed
 */
function _path_metatags_ui_form_step_metatags_settings(&$form, &$form_state) {

  // Load metatags from the cache.
  $path_metatags = path_metatags_object_cache_get($form_state['storage']['machine_name']);
  if (empty($path_metatags)) {
    return;
  }
  $form['translatable'] = array(
    '#type' => 'checkbox',
    '#title' => t('Translatable'),
    '#default_value' => isset($path_metatags->translatable) ? $path_metatags->translatable : 0,
  );

  // Add help message about path_metatags_i18n module.
  if (!module_exists('path_metatags_i18n')) {
    $form['translatable']['#disabled'] = TRUE;
    $form['translatable']['#description'] = t('You have to enable <a href="@modules_url">@module_name</a> module if you need translatable metatags.', array(
      '@module_name' => 'path metatags translation',
      '@modules_url' => url('admin/modules'),
    ));
  }
  else {
    $form['translatable']['#description'] = t('All metatags values will be available for translation.');
  }
  $form['metatags_table'] = array(
    '#tree' => TRUE,
  );

  // Initialize default amount of rows for titles and paths.
  if (empty($form_state['storage']['rows_count'])) {
    $form_state['storage']['rows_count'] = 1;
  }

  // If metatags already has titles and paths we should build appropriate amount of rows.
  if (isset($path_metatags->metatags)) {
    $count = count($path_metatags->metatags);
    if ($count > $form_state['storage']['rows_count']) {
      $form_state['storage']['rows_count'] = $count;
    }
  }
  $metatags = path_metatags_load_metatags_list();
  for ($i = 0; $i < $form_state['storage']['rows_count']; $i++) {

    // Unique hash required for updating form values in browser when deleting title-and-path row.
    $unique_hash = md5(rand());
    $form['metatags_table'][$unique_hash]['metatag'] = array(
      '#type' => 'select',
      '#options' => drupal_map_assoc(array_keys($metatags)),
      '#default_value' => isset($path_metatags->metatags[$i]) ? $path_metatags->metatags[$i] : '',
    );
    $form['metatags_table'][$unique_hash]['metatag_value'] = array(
      '#type' => 'textfield',
      '#default_value' => isset($path_metatags->metatags_values[$i]) ? $path_metatags->metatags_values[$i] : '',
      '#maxlength' => 250,
    );
    $form['metatags_table'][$unique_hash]['delete'] = array(
      '#name' => 'delete_' . $unique_hash,
      '#type' => 'submit',
      '#value' => t('Delete'),
      '#ajax' => array(
        'callback' => '_path_metatags_ui_form_step_metatags_settings_ajax',
        'wrapper' => 'path-metatags-ui-form-wrapper',
      ),
    );
  }

  // Button allows add new empty title-and-path row.
  $form['more'] = array(
    '#type' => 'submit',
    '#value' => t('Add more'),
    '#ajax' => array(
      'callback' => '_path_metatags_ui_form_step_metatags_settings_ajax',
      'wrapper' => 'path-metatags-ui-form-wrapper',
    ),
  );
  $form['#attached']['library'][] = array(
    'system',
    'ui.autocomplete',
  );
  $form['#attached']['library'][] = array(
    'system',
    'ui.button',
  );
  $form['#attached']['js'][] = drupal_get_path('module', 'path_metatags_ui') . '/js/path_metatags_ui.js';
  $form['#attached']['js'][] = array(
    'data' => array(
      'pathMetatagsUI' => array(
        'machineName' => $form_state['storage']['machine_name'],
      ),
    ),
    'type' => 'setting',
  );
  $form['#submit'][] = '_path_metatags_ui_form_step_metatags_settings_submit';
  _path_metatags_ui_form_attach_buttons($form, array(
    'prev',
    'finish',
  ), 'path_metatags_ui_add_form');
  if (isset($form_state['storage']['machine_name'])) {
    _path_metatags_ui_form_apply_default_values($form, $form_state['storage']['machine_name']);
  }
}

/**
 * Submit callback for metatags settings form  (fourth step).
 *
 * @param $form
 * @param $form_state
 */
function _path_metatags_ui_form_step_metatags_settings_submit($form, &$form_state) {
  $values = $form_state['values'];

  // Remove row when DELETE button submitted.
  $delete = array_search(t('Delete'), $values, TRUE);
  if ($delete !== FALSE) {
    $deleted_key = drupal_substr($delete, 7);
    unset($values['metatags_table'][$deleted_key]);
    unset($form['metatags_table'][$deleted_key]);
    $form_state['storage']['rows_count']--;
    $form_state['rebuild'] = TRUE;
  }

  // Action for ADD MORE button.
  if (isset($values['more']) && $values['op'] == $values['more']) {
    $form_state['storage']['rows_count']++;
    $form_state['rebuild'] = TRUE;
  }

  // Rebuild metatags titles and paths.
  $metatags = array();
  $metatags_values = array();
  foreach ($values['metatags_table'] as $value) {
    $metatags[] = $value['metatag'];
    $metatags_values[] = $value['metatag_value'];
  }

  // Update titles and paths in cache.
  $machine_name = $form_state['storage']['machine_name'];
  $path_metatags = path_metatags_object_cache_get($machine_name);
  $path_metatags->metatags = $metatags;
  $path_metatags->metatags_values = $metatags_values;
  path_metatags_object_cache_set($machine_name, $path_metatags);
}

/**
 * Ajax callback for ADD MORE button.
 *
 * @param $form
 * @param $form_state
 * @return mixed
 */
function _path_metatags_ui_form_step_metatags_settings_ajax($form, $form_state) {

  // Simple form reload.
  return $form;
}

/**
 * Add default values to form if presented.
 *
 * @param $form
 *   Form to which values will be applied from cache.
 * @param $machine_name
 *   Machine name of metatags
 */
function _path_metatags_ui_form_apply_default_values(&$form, $machine_name) {

  // Load breacrumb from cache by machine_name.
  $metatags = path_metatags_object_cache_get($machine_name);
  if (empty($metatags)) {
    return;
  }

  // Try to apply values from cache to form.
  foreach ($form as $key => $value) {
    if (!empty($metatags->{$key}) && is_array($metatags->{$key})) {
      foreach ($metatags->{$key} as $array_key => $value) {
        $form[$key][$array_key]['#default_value'] = $value;
      }
    }
    elseif (!empty($metatags->{$key})) {
      $form[$key]['#default_value'] = $metatags->{$key};
    }
  }
}

/**
 * Attach ajax-processed buttons to the form.
 *
 * @param $form
 *   Form which will be added to the button.
 * @param array $buttons
 *   Array with buttons that should be added ('prev', 'next', 'finish').
 * @param $form_name
 *   Name of form that addes buttons.
 */
function _path_metatags_ui_form_attach_buttons(&$form, $buttons = array(), $form_name) {
  if ($form_name == 'path_metatags_ui_add_form' || $form_name == 'path_metatags_ui_edit_form') {
    $form_wrapper = 'path-metatags-ui-form';
  }
  else {
    $form_wrapper = str_replace('_', '-', $form_name);
  }

  // Default part for all buttons.
  $base = array(
    '#type' => 'submit',
    '#ajax' => array(
      'callback' => $form_name . '_ajax_callback',
      'wrapper' => $form_wrapper . '-wrapper',
    ),
  );
  $form['actions'] = array(
    '#type' => 'actions',
  );
  if (in_array('prev', $buttons)) {
    $form['actions']['prev'] = array(
      '#value' => t('Back'),
      // Do not validate form when BACK button clicked.
      '#limit_validation_errors' => array(),
      '#submit' => array(
        $form_name . '_submit',
      ),
    ) + $base;
  }
  if (in_array('next', $buttons)) {
    $form['actions']['next'] = array(
      '#value' => t('Continue'),
    ) + $base;
  }
  if (in_array('finish', $buttons)) {
    $form['actions']['finish'] = array(
      '#value' => t('Finish'),
    ) + $base;
    if ($form_name == 'path_metatags_ui_add_form') {
      $form['actions']['finish']['#ajax']['callback'] = '_path_metatags_ui_add_form_finish_ajax_callback';
    }
  }
}

/**
 * Provide form that allows to match arguments with contexts.
 *
 * @param $form
 * @param $form_state
 * @return array
 */
function path_metatags_ui_argument_context_form($form, &$form_state) {
  $form['#prefix'] = '<div id="path-metatags-ui-argument-context-form-wrapper">';
  $form['#suffix'] = '</div>';
  $form['#tree'] = TRUE;

  // Load values from form storage.
  $path_name = $form_state['storage']['path_name'];
  $keyword = $form_state['storage']['keyword'];

  // Load metatags from cache.
  $metatags = path_metatags_object_cache_get($path_name);
  if (!$metatags) {
    return array();
  }

  // Get current form step.
  if (!isset($form_state['storage']['step'])) {
    $form_state['storage']['step'] = 1;
  }

  // Include ctools library for contexts.
  ctools_include('context');
  switch ($form_state['storage']['step']) {

    // Show form with selection radios.
    case 1:

      // Get ctools plugins for arguments.
      $options = array();
      $plugins = ctools_get_arguments();
      foreach ($plugins as $id => $plugin) {
        if (empty($plugin['no ui'])) {
          $options[$id] = $plugin['title'];
        }
      }
      asort($options);
      $options = array(
        '' => t('No context selected'),
      ) + $options;
      $form['argument'] = array(
        '#type' => 'radios',
        '#options' => $options,
      );

      // Apply default value for argument.
      if (isset($metatags->arguments[$keyword]['argument'])) {
        $form['argument']['#default_value'] = $metatags->arguments[$keyword]['argument'];
      }

      // Attach buttons to form.
      _path_metatags_ui_form_attach_buttons($form, array(
        'next',
      ), 'path_metatags_ui_argument_context_form');
      break;
    case 2:

      // Load argument's plugin.
      $plugins = ctools_get_arguments();
      $plugin = $plugins[$form_state['storage']['argument']];
      $form['settings']['identifier'] = array(
        '#title' => t('Context identifier'),
        '#type' => 'textfield',
        '#default_value' => $plugin['title'],
      );

      // Apply plugin's settings form to previously builded form.
      if ($function = (string) ctools_plugin_get_function($plugin, 'settings form')) {
        $settings_form = $settings_form_state = array();
        $function($settings_form, $settings_form_state);
        $form['settings'] += $settings_form['settings'];
      }

      // Apply argument's stored values as default form values.
      if (!empty($metatags->arguments[$keyword]['settings'])) {
        foreach ($metatags->arguments[$keyword]['settings'] as $name => $value) {
          if (isset($form['settings'][$name])) {
            $form['settings'][$name]['#default_value'] = $value;
          }
        }
      }

      // Attach buttons to the form.
      _path_metatags_ui_form_attach_buttons($form, array(
        'prev',
        'finish',
      ), 'path_metatags_ui_argument_context_form');
      break;
  }
  return $form;
}

/**
 * Submit callback for contexts selection form.
 *
 * @param $form
 * @param $form_state
 */
function path_metatags_ui_argument_context_form_submit($form, &$form_state) {
  $values = $form_state['values'];
  $actions = $form_state['values']['actions'];
  $keyword = $form_state['storage']['keyword'];
  $path_name = $form_state['storage']['path_name'];

  // Load metatags from cache storage.
  $metatags = path_metatags_object_cache_get($path_name);
  if (!$metatags) {
    return;
  }

  // Save keyword's argument.
  if (!empty($values['argument'])) {

    // Unset argument's settings and selection rules if it was changed.
    if (!empty($metatags->arguments[$keyword]['argument']) && $metatags->arguments[$keyword]['argument'] != $values['argument']) {

      // Unset arguments settings.
      unset($metatags->arguments[$keyword]['settings']);

      // Search for selection rules for old argument.
      if (!empty($metatags->access['plugins'])) {
        foreach ($metatags->access['plugins'] as $key => $access) {
          if ($access['context'] == $keyword) {
            unset($metatags->access['plugins'][$key]);
          }
        }
      }
    }

    // Save new argument.
    $metatags->arguments[$keyword]['argument'] = $values['argument'];

    // Save argument in storage.
    $form_state['storage']['argument'] = $values['argument'];
  }

  // Save argument's settings.
  if (!empty($values['settings'])) {
    $metatags->arguments[$keyword]['settings'] = $values['settings'];
  }

  // Save metatags in cache.
  path_metatags_object_cache_set($path_name, $metatags);

  // Switch current step.
  if (isset($actions['next']) && $values['op'] == $actions['next']) {
    $form_state['storage']['step']++;
  }
  elseif (isset($actions['prev']) && $values['op'] == $actions['prev']) {
    $form_state['storage']['step']--;
  }
  elseif (isset($actions['finish']) && $values['op'] == $actions['finish']) {
    $form_state['rebuild'] = FALSE;
  }
  $form_state['rebuild'] = TRUE;
}

/**
 * Ajax callback for contexts selection form.
 *
 * @param $form
 * @param $form_state
 * @return array|mixed
 */
function path_metatags_ui_argument_context_form_ajax_callback($form, $form_state) {
  if (!form_get_errors()) {
    $values = $form_state['values'];
    $actions = $form_state['values']['actions'];
    $path_name = $form_state['storage']['path_name'];
    if (isset($actions['finish']) && $values['op'] == $actions['finish']) {

      // Add library for ctools modal dialog.
      ctools_include('modal');

      // Update second step of path metatags add form.
      $add_form_state = array(
        'storage' => array(
          'step' => 2,
          'machine_name' => $path_name,
        ),
      );
      $form = drupal_build_form('path_metatags_ui_add_form', $add_form_state);

      // Include additinal ajax commands.
      $commands[] = ajax_command_replace('#path-metatags-ui-argument-table', drupal_render($form['table']));
      $commands[] = ctools_modal_command_dismiss();
      return array(
        '#type' => 'ajax',
        '#commands' => $commands,
      );
    }
  }
  return $form;
}

/**
 * Form for editing path metatags.
 *
 * @param $form
 * @param $form_state
 * @return array
 */
function path_metatags_ui_edit_form($form, &$form_state) {

  // Load form for creation of metatags but with existing metatags in cache.
  $form = path_metatags_ui_add_form($form, $form_state, FALSE, 'edit-form');

  // Disable machine name edition.
  if (isset($form['machine_name'])) {
    $form['machine_name']['#disabled'] = TRUE;
  }

  // Set two submit buttons for every form.
  $form['actions'] = array(
    '#type' => 'action',
    '#weight' => 100,
  );
  $base = array(
    '#type' => 'submit',
    '#ajax' => array(
      'callback' => 'path_metatags_ui_edit_form_ajax_callback',
      'wrapper' => 'path-metatags-ui-form-wrapper',
    ),
  );
  $form['actions']['update'] = array(
    '#value' => t('Update'),
  ) + $base;
  $form['actions']['update_save'] = array(
    '#value' => t('Update and Save'),
  ) + $base;
  $form['#submit'][] = 'path_metatags_ui_edit_form_submit';
  return $form;
}

/**
 * Submit callback for path metatags EDIT form.
 *
 * @param $form
 * @param $form_state
 */
function path_metatags_ui_edit_form_submit($form, &$form_state) {
  $values = $form_state['values'];
  if (isset($values['update']) && $values['op'] == $values['update']) {

    // If user clicks UPDATE button metatags will be automatically moved to cache.
    // We just need to show user some notifications.
    drupal_set_message(t('The metatags has been updated.'));
    drupal_set_message(t('Changes will not be permanent until you save it.'), 'warning');
  }
  elseif (isset($values['update_save']) && $values['op'] == $values['update_save']) {
    $metatags = path_metatags_object_cache_get($form_state['storage']['machine_name']);
    path_metatags_save($metatags);
    drupal_set_message(t('Metatags %name was updated.', array(
      '%name' => $metatags->name,
    )));
  }
}

/**
 * Ajax callback for path metatags EDIT form.
 * Redirect user to metatags list when metatags is updated.
 *
 * @param $form
 * @param $form_state
 * @return array
 */
function path_metatags_ui_edit_form_ajax_callback($form, $form_state) {
  if (!form_get_errors()) {
    $values = $form_state['values'];
    if (isset($values['update_save']) && $values['op'] == $values['update_save']) {

      // Redirect user to page with metatags list.
      ctools_include('ajax');

      // Ajax responder not required since CTools 7.x-1.0 but this line
      // should be added to keep module works properly.
      ctools_add_js('ajax-responder');
      $commands = array();
      $commands[] = ctools_ajax_command_redirect('admin/structure/path-metatags');
      return array(
        '#type' => 'ajax',
        '#commands' => $commands,
      );
    }
  }
  return $form;
}

/**
 * Form for deletion or revert path metatags from database.
 *
 * @param $form
 * @param $form_state
 * @param $path_metatags
 *   Object with path metatags params.
 * @return array
 */
function path_metatags_ui_delete_form($form, $form_state, $path_metatags) {
  $form['machine_name'] = array(
    '#type' => 'value',
    '#value' => $path_metatags->machine_name,
  );
  if ($path_metatags->is_overwritten) {
    $form['message'] = array(
      '#markup' => t('Are you sure that you want to revert %name from database?', array(
        '%name' => $path_metatags->name,
      )),
    );
  }
  else {
    $form['message'] = array(
      '#markup' => t('Are you sure that you want to delete %name from database?', array(
        '%name' => $path_metatags->name,
      )),
    );
  }
  $form['actions'] = array(
    '#type' => 'actions',
  );
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => $path_metatags->is_overwritten ? t('Revert') : t('Delete'),
  );
  $form['actions']['cancel'] = array(
    '#type' => 'submit',
    '#value' => t('Cancel'),
  );
  return $form;
}

/**
 * Submit callback for deletion form.
 * Removes path metatags from database.
 *
 * @param $form
 * @param $form_state
 */
function path_metatags_ui_delete_form_submit($form, &$form_state) {
  $values = $form_state['values'];

  // Delete metatags.
  if ($values['op'] == $values['submit']) {
    path_metatags_delete($values['machine_name']);
    drupal_set_message(t('Path metatags was successfully deleted.'));
  }

  // Redirect to the settings page.
  $form_state['redirect'] = 'admin/structure/path-metatags';
}

/**
 * Load path_metatags object for UI editing.
 *
 * @param $machine_name
 * @return bool|object
 */
function path_metatags_ui_cache_load($machine_name) {
  $path_metatags = path_metatags_object_cache_get($machine_name);
  if (empty($path_metatags)) {

    // If there is no cache load original object.
    $path_metatags = path_metatags_load_by_name($machine_name);
  }
  if (empty($path_metatags)) {
    return FALSE;
  }
  return $path_metatags;
}

Functions

Namesort descending Description
path_metatags_ctools_access_get Callback for access control ajax form on behalf of context task handler (third step). Returns the cached access config and contexts used.
path_metatags_ctools_access_set Callback for access control ajax form on behalf of context task handler (third step). Saves the changed access to the cache.
path_metatags_ui_add_form Provides multistep form for create/edit path metatags.
path_metatags_ui_add_form_ajax_callback Ajax callback for path_metatags_ui_add_form.
path_metatags_ui_add_form_submit Submit callback for path_metatags_ui_add_form.
path_metatags_ui_argument_context_form Provide form that allows to match arguments with contexts.
path_metatags_ui_argument_context_form_ajax_callback Ajax callback for contexts selection form.
path_metatags_ui_argument_context_form_submit Submit callback for contexts selection form.
path_metatags_ui_cache_load Load path_metatags object for UI editing.
path_metatags_ui_delete_form Form for deletion or revert path metatags from database.
path_metatags_ui_delete_form_submit Submit callback for deletion form. Removes path metatags from database.
path_metatags_ui_edit_form Form for editing path metatags.
path_metatags_ui_edit_form_ajax_callback Ajax callback for path metatags EDIT form. Redirect user to metatags list when metatags is updated.
path_metatags_ui_edit_form_submit Submit callback for path metatags EDIT form.
path_metatags_ui_menu Implements hook_menu().
path_metatags_ui_permission Implements hook_permission().
path_metatags_ui_theme Implementats hook_theme().
_path_metatags_ui_add_form_finish_ajax_callback Ajax callback for last step of metatags creating. Saves new path metatags.
_path_metatags_ui_form_apply_default_values Add default values to form if presented.
_path_metatags_ui_form_attach_buttons Attach ajax-processed buttons to the form.
_path_metatags_ui_form_step_arguments_selection SECOND STEP. Provide form for arguments selection.
_path_metatags_ui_form_step_basic_settings FIRST STEP. Provide form with basic settings.
_path_metatags_ui_form_step_basic_settings_submit Submit function for basic settings form (first step).
_path_metatags_ui_form_step_basic_settings_validate Validate function for basic settings form (first step).
_path_metatags_ui_form_step_metatags_settings FOURTH STEP. Provide form for metatags settings.
_path_metatags_ui_form_step_metatags_settings_ajax Ajax callback for ADD MORE button.
_path_metatags_ui_form_step_metatags_settings_submit Submit callback for metatags settings form (fourth step).
_path_metatags_ui_form_step_selection_rules THIRD STEP. Provide form with selection rules.