You are here

draggableviews.module in DraggableViews 7.2

File

draggableviews.module
View source
<?php

/**
 * Implements hook_views_api().
 */
function draggableviews_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'draggableviews') . '/views',
  );
}

/**
 * Gets the Draggableviews field if it exists on the passed-in view.
 *
 * @return
 *  The field object if found. Otherwise, FALSE.
 */
function _draggableviews_get_field($view) {
  foreach ($view->field as $field_name => $field) {
    if ($field instanceof draggableviews_handler_field_draggable) {

      // Add in the view object for convenience.
      $field->view = $view;
      return $field;
    }
  }
  return FALSE;
}

/**
 * Implements hook_views_pre_view().
 */
function draggableviews_views_pre_view(&$view, &$display_id, &$args) {

  // User has Views UI access however they do not have access to draggableviews.
  // To prevent confusion, display a warning message when they are previewing the
  // current display when it has a draggableviews field.
  if (isset($view->preview) && isset($view->display_handler->handlers['field']['draggableviews']) && !user_access('access draggableviews')) {
    drupal_set_message(t("Current display has a draggableviews field, however you cannot see it in the preview because you do not have\n      '@permission' permission.", array(
      '@permission' => 'access draggableviews',
    )), 'warning');
  }
}

/**
 * Implements hook_form_alter().
 *
 * Alter views form to change button label.
 */
function draggableviews_form_alter(&$form, &$form_state, $form_id) {
  if (strpos($form_id, 'views_form_') === 0) {
    $draggableviews = _draggableviews_get_field($form_state['build_info']['args'][0]);
  }

  // Not a draggableviews-enabled views form.
  if (empty($draggableviews)) {
    return;
  }

  // Remove the default submit button.
  // The submit button added by Views Form API might be used by a non-Draggableviews
  // form handler. If there is no such handler on the view, hide the button.
  $has_other_views_form_handlers = FALSE;
  foreach ($draggableviews->view->field as $field) {
    if (property_exists($field, 'views_form_callback') || method_exists($field, 'views_form')) {
      if (!$field instanceof draggableviews_handler_field_draggable) {
        $has_other_views_form_handlers = TRUE;
      }

      // If there is another handler that wants remove the button, however, will leave it there
      // it if there is another handler exist, then this will causes a problem, as the button will
      // show up when neither one wanted it. A known example is the Views Bulk Operations module.
      if ($field instanceof views_bulk_operations_handler_field_operations) {

        // VBO Does wants it on the confirmation page, but not the initial view form page.
        if ($form_state['step'] == 'views_form_views_form') {

          // Reset it back to FALSE.
          $has_other_views_form_handlers = FALSE;
        }
      }
    }
  }
  if (!$has_other_views_form_handlers) {
    $form['actions']['submit']['#access'] = FALSE;
  }

  // Create draggableviews save order button.
  $form['actions']['save-order'] = array(
    '#type' => 'submit',
  );

  // If there is no results remove the save-order button.
  if (!isset($form['draggableviews'][0])) {
    $form['actions']['save-order']['#access'] = FALSE;
    return;
  }

  // Check permissions and number of results.
  if (!user_access('access draggableviews') || count($draggableviews->view->result) < 2) {
    $form['actions']['save-order']['#access'] = FALSE;
    return;
  }

  // Do not show "Submit" button on preview and warn user they cannot save in Preview mode.
  $current_path = current_path();
  if (strpos($current_path, 'admin/structure/views/nojs/preview') !== FALSE) {
    $form['actions']['message'] = array(
      '#markup' => '<div class="messages warning">' . t('It is not possible to save order in Preview mode.') . '</div>',
    );
    $form['actions']['save-order']['#access'] = FALSE;
    return;
  }
  $options = $draggableviews->view->field['draggableviews']->options['draggableviews'];
  $form['actions']['save-order']['#value'] = t($options['save_button_label']);
  $form['actions']['save-order']['#submit'][] = 'draggableviews_views_submit';
  if ($options['ajax']) {
    $form['actions']['save-order']['#ajax'] = array(
      'callback' => 'draggableviews_view_draggabletable_form_ajax',
    );
    $form['actions']['save-order']['#attributes']['class'][] = 'js-draggableviews-save-order';
    $form['actions']['save-order']['#attributes']['class'][] = 'element-invisible';
  }

  // Set action as current path.
  $form['#action'] = url(current_path());

  // Keep destination and other GET params.
  if (count($_GET) > 1) {
    $get = $_GET;
    unset($get['q']);
    if (!isset($_GET['destination'])) {
      $get['destination'] = current_path() . '?' . drupal_http_build_query($get);
    }
    $form['#action'] .= '?' . drupal_http_build_query($get);
  }
}

/**
 * Save weight records after form submit.
 */
function draggableviews_views_submit($form, &$form_state) {
  $view = $form_state['build_info']['args'][0];

  // Use 'input' instead of mapped 'values' here. This is done because if in
  // table display we sort by header then set weights and save, we got
  // totally wrong results ($form_state['values']['draggableviews'] mapped
  // wrong from $form_state['input']['draggableviews'])
  $form_state['values']['draggableviews'] = $form_state['input']['draggableviews'];

  // Set the weight.
  $handler_object = draggableviews_get_handler_class($view->field['draggableviews']->options['draggableviews']['handler']);
  $handler_object
    ->set($form_state);
  module_invoke_all('draggableviews_sorted', $view);

  // Trigger the event "A view has been sorted"
  if (module_exists('rules')) {
    rules_invoke_event('draggableviews_rules_event_sorted', $view->name, $view->current_display);
  }
}

/**
 * Implements hook_preprocess_views_view_table().
 */
function draggableviews_preprocess_views_view_table(&$vars) {
  if ($order_view = _draggableviews_load_order_view($vars['view'])) {

    // Add indentation if hierarchy is available.
    if (!empty($order_view->field['draggableviews']->options['draggableviews']['hierarchy_handler'])) {
      $hierarchy_handler_object = draggableviews_get_handler_class($order_view->field['draggableviews']->options['draggableviews']['hierarchy_handler'], 'hierarchy_handler');
      foreach ($vars['rows'] as $key => $row) {
        $first_column = current(array_keys($row));
        $field = (object) array(
          'view' => $vars['view'],
        );
        $depth = $hierarchy_handler_object
          ->get_depth($field, $key);
        $vars['rows'][$key][$first_column] = theme('indentation', array(
          'size' => $depth,
        )) . $vars['rows'][$key][$first_column];
        $vars['row_classes'][$key][] = 'depth-' . $depth;
      }
    }
  }

  // If this view is not the sort view, then stop here.
  if (!isset($vars['view']->field['draggableviews'])) {
    return;
  }

  // Check permissions.
  if (!user_access('access draggableviews')) {

    // Remove column "draggableviews" from results and header.
    foreach ($vars['rows'] as &$row) {
      unset($row['draggableviews']);
    }
    unset($vars['header']['draggableviews']);
    return;
  }

  // Add table class.
  $vars['classes_array'][] = 'draggable';

  // Add row class.
  foreach ($vars['row_classes'] as &$row_classes) {
    $row_classes[] = 'draggable';
  }
  $vars['attributes_array']['id'] = drupal_html_id('draggableviews-table-' . $vars['view']->name . '-' . $vars['view']->current_display);

  // Add JavaScript.
  drupal_add_tabledrag($vars['attributes_array']['id'], 'order', 'sibling', 'draggableviews-weight');

  // Add JavaScript for auto-save functionality.
  if ($vars['view']->field['draggableviews']->options['draggableviews']['ajax']) {
    drupal_add_js(drupal_get_path('module', 'draggableviews') . '/js/draggableviews_table.js');
  }

  // Parent JavaScripts.
  if (!empty($vars['view']->field['draggableviews']->options['draggableviews']['hierarchy_handler'])) {
    drupal_add_tabledrag($vars['attributes_array']['id'], 'match', 'parent', 'draggableviews-parent', 'draggableviews-parent', 'draggableviews-id', FALSE);
    drupal_add_tabledrag($vars['attributes_array']['id'], 'depth', 'group', 'draggableviews-depth', NULL, NULL, FALSE);
  }
}

/**
 * Implements hook_preprocess_views_view_list().
 */
function draggableviews_preprocess_views_view_list(&$vars) {

  // Check whether this table view has draggableview field.
  if (!isset($vars['view']->field['draggableviews'])) {
    return;
  }

  // Add class to ul item of the view.
  $class = 'draggableviews-grid-' . $vars['view']->name . '-' . $vars['view']->current_display;
  $vars['list_type_prefix'] = str_replace('>', ' class="' . $class . '">', $vars['list_type_prefix']);

  // Add JavaScript.
  drupal_add_library('system', 'ui.sortable');

  // Add setting of the row class.
  $js_setting['draggableviews_row_class'][] = $class;

  // Add setting whether ajax enabled or not.
  $js_setting['draggableviews_ajax'] = $vars['view']->field['draggableviews']->options['draggableviews']['ajax'];
  drupal_add_js($js_setting, 'setting');

  // Add custom js and css.
  drupal_add_js(drupal_get_path('module', 'draggableviews') . '/js/draggableviews_list.js');
  drupal_add_css(drupal_get_path('module', 'draggableviews') . '/css/draggableviews_list.css');
}

/**
 * Implements hook_permission().
 */
function draggableviews_permission() {
  return array(
    'access draggableviews' => array(
      'title' => t('Access draggable views'),
      'description' => t('Give users the right to sort their views'),
    ),
  );
}

/**
 * Implements hook_ctools_plugin_type().
 */
function draggableviews_ctools_plugin_type() {
  return array(
    'handler' => array(
      'use hooks' => FALSE,
    ),
    'hierarchy_handler' => array(
      'use hooks' => FALSE,
    ),
  );
}

/**
 * Implements hook_ctools_plugin_directory().
 */
function draggableviews_ctools_plugin_directory($module, $plugin) {
  if ($module == 'draggableviews' && ($plugin == 'handler' || $plugin == 'hierarchy_handler')) {
    return 'handlers';
  }
}

/**
 * Implements hook_contextual_links_view_alter().
 *
 * Adds "Order view" contextual link.
 */
function draggableviews_contextual_links_view_alter(&$element, $items) {

  // Check permission to use draggable.
  if (!user_access('access draggableviews')) {
    return;
  }

  // Add Draggableviews contextual link "Order view".
  $views_ui_element = array();
  if (isset($element['#element']['#views_contextual_links_info']['views_ui'])) {
    $views_ui_element = $element['#element']['#views_contextual_links_info']['views_ui'];
  }

  // In case of block #views_contextual_links_info element is inside of
  // 'content' and not '#element' directly.
  // @see http://drupal.org/node/1413596#comment-5912688
  if (empty($views_ui_element) && isset($element['#element']['content']['#views_contextual_links_info']['views_ui'])) {
    $views_ui_element = $element['#element']['content']['#views_contextual_links_info']['views_ui'];
  }
  if (!empty($views_ui_element['view_display_id']) && isset($views_ui_element['view'])) {
    $display_id = $views_ui_element['view_display_id'];
    $view = $views_ui_element['view'];
    $view
      ->build($display_id);

    // Get the order view's path. Don't include itself.
    if ($path = _draggableviews_get_order_path($view, FALSE)) {

      // Only add if current user has access to the order view.
      if ($router_item = menu_get_item($path)) {
        if ($router_item['access']) {
          $element['#links']['draggableviews-order'] = array(
            'title' => t('Order view'),
            'href' => $path,
            'query' => array(
              'destination' => current_path(),
            ),
          );
        }
      }
    }
  }
}

/**
 * Get class of handler.
 *
 * @param object
 *   Handler's class object.
 */
function draggableviews_get_handler_class($handler_name, $handler_type = 'handler') {
  $objects =& drupal_static(__FUNCTION__);
  if (!isset($objects[$handler_name])) {
    ctools_include('plugins');
    if ($class = ctools_plugin_load_class('draggableviews', $handler_type, $handler_name, 'handler')) {
      $objects[$handler_name] = new $class();
    }
  }
  return $objects[$handler_name];
}

/**
 * Retrieve all sort plugins.
 *
 * Check whether handler class inherits draggableviews_handler.
 *
 * @return array
 *   Array of proper draggableviews handlers.
 */
function draggableviews_get_handlers() {
  ctools_include('plugins');
  $handlers = ctools_get_plugins('draggableviews', 'handler');
  $return = array();
  foreach ($handlers as $handler_id => $handler) {
    $handler_object = draggableviews_get_handler_class($handler_id);
    if (in_array('draggableviews_handler', class_parents(get_class($handler_object)))) {
      $return[$handler_id] = $handler_object;
    }
  }
  return $return;
}

/**
 * Retrieve all hierarchy plugins.
 *
 * Check whether handler class inherits draggableviews_hierarchy_handler.
 *
 * @return array
 *   Array of proper draggableviews handlers.
 */
function draggableviews_get_hierarchy_handlers() {
  ctools_include('plugins');
  $handlers = ctools_get_plugins('draggableviews', 'hierarchy_handler');
  $return = array();
  foreach ($handlers as $handler_id => $handler) {
    $handler_object = draggableviews_get_handler_class($handler_id);
    if (in_array('draggableviews_hierarchy_handler', class_parents(get_class($handler_object)))) {
      $return[$handler_id] = $handler_object;
    }
  }
  return $return;
}

/**
 * Ajax draggabletable submit handler.
 */
function draggableviews_view_draggabletable_form_ajax($form, $form_state) {

  // Find the form element
  $form_element = "form:has(input[name=form_build_id][value='{$form['form_build_id']['#value']}'])";

  // Remove warning and asterisk.
  return array(
    '#type' => 'ajax',
    '#commands' => array(
      ajax_command_remove("{$form_element} div.tabledrag-changed-warning"),
      ajax_command_remove("{$form_element} span.tabledrag-changed"),
      ajax_command_remove("{$form_element} div.draggableviews-changed-warning"),
      ajax_command_invoke("{$form_element} ul.draggableviews-changed", 'removeClass', array(
        'draggableviews-changed',
      )),
    ),
  );
}

/**
 * Get the draggable views weight sort of a view if there is one and return its
 * ID. If there are multiple of these sorts, either the one matching a specified
 * table alias or the first one is returned.
 *
 * @param $view
 *   The view object.
 * @param string $preferred_table_alias
 *   (optional) In case there are multiple sorts, return the ID of one which has
 *   this table alias, if it exists. If it doesn't exist, return the first sort.
 *
 * @return string
 *   The ID of the sort or empty string if there isn't one.
 */
function _draggableviews_get_draggable_sort($view, $preferred_table_alias = '') {
  $default_sort_id = '';
  foreach ($view->sort as $id => $sort) {
    if ($sort->definition['handler'] == 'draggableviews_handler_sort') {
      if (!$preferred_table_alias || $sort->table_alias == $preferred_table_alias) {
        return $id;
      }
      if (!$default_sort_id) {

        // Return this ID if no sort matching $preferred_table_alias is found.
        $default_sort_id = $id;
      }
    }
  }
  return $default_sort_id;
}

/**
 * Evaluates the given PHP code, with the given variables defined.
 *
 * @param $code
 *   The PHP code to run, without <?php ?>
 * @param $arguments
 *   Views arguments including values of exposed filters.
 * @param $view
 *   The view being sorted.
 *
 * @return
 *   The return value of the evaled code.
 */
function _draggableviews_eval_return($code, $arguments, $view) {
  return eval($code);
}

/**
 * Load and/or built the order view with the correct view display.
 *
 * @param $view
 *   The view object
 *
 * @return
 *   The view object with the sort display
 */
function _draggableviews_load_order_view($view, $include_self = TRUE) {
  if ($order_view_name_display = _draggableviews_get_order_view_display($view, $include_self)) {
    if ($order_view_name_display == 'self' && $include_self) {
      $order_view = $view;
    }
    else {
      list($order_view_name, $order_view_display) = explode(':', $order_view_name_display);
      if ($order_view_name == $view->name) {
        if ($order_view_display == $view->current_display) {
          $order_view = $view;
        }
        else {

          // The current $view object does not have the information of the
          // display view that sorts it. An example would be the draggableviews
          // field, which is only in the sort order display.
          $order_view = views_get_view($order_view_name);

          // Error gets thrown if the order_view_display has not been saved yet.
          if (!isset($order_view)) {

            // TODO: Error message can get repeated 3-4 times.  Should only happen once.
            drupal_set_message(t('The order view is unavailable. Try saving or re-saving the view'), 'error');
            return;
          }
          $order_view
            ->set_display($order_view_display);
          $order_view
            ->init_handlers();
        }
      }
      else {

        // Need to get the order view, as order is not part of this one.
        $order_view = views_get_view($order_view_name);

        // Checks if view/display exists and user have access.
        if ($order_view && $order_view
          ->access($order_view_display)) {
          $order_view
            ->set_display($order_view_display);
          $order_view
            ->init_handlers();
        }
      }
    }
    return $order_view;
  }
}

/**
 * Get the view display identifier.
 *
 * @param $view
 *   The view object
 *
 * @return
 *   A string with the view name and display id separated by ':'.
 */
function _draggableviews_get_order_view_display($view, $include_self = TRUE) {

  // Proceed only if weight sort criteria is available.
  if (!($sort_key = _draggableviews_get_draggable_sort($view))) {
    return FALSE;
  }
  $order_view_display = $view->sort[$sort_key]->options['draggableviews_setting_view'];
  if (empty($order_view_display)) {
    return FALSE;
  }
  if (!$include_self) {
    if ($order_view_display == 'self' || $order_view_display == $view->name . ':' . $view->current_display) {
      return FALSE;
    }
  }
  return $order_view_display;
}

/**
 * Get the path to the order view.
 *
 * @param $view
 *   The view object.
 *
 * @return
 *   The path of the page or FALSE if there isn't one.
 */
function _draggableviews_get_order_path($view, $include_self = TRUE) {
  $path = FALSE;
  if ($order_view = _draggableviews_load_order_view($view, $include_self)) {
    if (isset($order_view->display[$order_view->current_display]->display_options['path'])) {
      $path = $order_view->display[$order_view->current_display]->display_options['path'];
    }
  }
  elseif ($include_self) {
    if (isset($view->display[$view->current_display]->display_options['path'])) {
      $path = $view->display[$view->current_display]->display_options['path'];
    }
  }
  else {
    return FALSE;
  }

  // If page expects arguments, we provide arguments set to current view.
  $args = $view->args;
  if (strpos($path, '%') !== FALSE && !empty($args)) {
    $new_path_array = array();
    foreach (explode('/', $path) as $path_part) {
      if (strpos($path_part, '%') !== FALSE) {
        $new_path_array[] = !empty($args) ? array_shift($args) : '';
      }
      else {
        $new_path_array[] = $path_part;
      }
    }
    $path = implode('/', $new_path_array);
  }

  // If page path doesn't have % in the path or we still have some argument
  // remain, simply append them to the end of the path.
  if (!empty($args)) {
    $path .= '/' . implode('/', $args);
  }
  return $path;
}

/**
 * Helper function that returns an option list of all draggable views or let
 * you inspect a specific view to see if it's a draggable view itself and
 * returns the appropriate option for that.
 *
 * @param $view
 *   The view object to inspect. Optional.
 *
 * @return
 *   An option array of draggable views.
 */
function _draggableviews_get_views_options($view = NULL) {
  if (!empty($view)) {
    $view_clone = clone $view;
    $view_clone
      ->set_display($view_clone->current_display);
    $view_clone
      ->init_handlers();
    if (isset($view_clone->field['draggableviews'])) {
      return $view_clone->name . ':' . $view_clone->current_display;
    }
  }

  // Check whether field exists for all enabled views. We only want the
  // 'setting' views.
  $views = views_get_enabled_views();
  $options = array();

  // Convert list of objects to options for the form.
  foreach ($views as $view_name => $view) {
    foreach ($view->display as $display_name => $display) {
      if ($display_name == 'default') {
        continue;
      }

      // Clone view and build it so we can see all the fields.
      $view_clone = clone $view;
      $view_clone
        ->set_display($display_name);
      $view_clone
        ->init_handlers();

      // If draggableviews field attached, show this view in options.
      if (isset($view_clone->field['draggableviews'])) {
        $options[$view_name . ':' . $display_name] = $view->human_name . ' (' . $display->display_title . ')';
      }
    }
  }
  return $options;
}

/**
 * Implements hook_node_delete().
 */
function draggableviews_node_delete($node) {

  // Code to check if entry present in draggableviews_structure table.
  $nid = $node->nid;
  $query = db_select('draggableviews_structure', 'ds')
    ->condition('entity_id', $nid)
    ->fields('ds', array(
    'view_name',
  ));
  $result = $query
    ->execute()
    ->rowCount();

  // Code to remove entries from draggableviews_structure table.
  if ($result > 0) {
    db_delete('draggableviews_structure')
      ->condition('entity_id', $nid)
      ->execute();
  }
}

Functions

Namesort descending Description
draggableviews_contextual_links_view_alter Implements hook_contextual_links_view_alter().
draggableviews_ctools_plugin_directory Implements hook_ctools_plugin_directory().
draggableviews_ctools_plugin_type Implements hook_ctools_plugin_type().
draggableviews_form_alter Implements hook_form_alter().
draggableviews_get_handlers Retrieve all sort plugins.
draggableviews_get_handler_class Get class of handler.
draggableviews_get_hierarchy_handlers Retrieve all hierarchy plugins.
draggableviews_node_delete Implements hook_node_delete().
draggableviews_permission Implements hook_permission().
draggableviews_preprocess_views_view_list Implements hook_preprocess_views_view_list().
draggableviews_preprocess_views_view_table Implements hook_preprocess_views_view_table().
draggableviews_views_api Implements hook_views_api().
draggableviews_views_pre_view Implements hook_views_pre_view().
draggableviews_views_submit Save weight records after form submit.
draggableviews_view_draggabletable_form_ajax Ajax draggabletable submit handler.
_draggableviews_eval_return Evaluates the given PHP code, with the given variables defined.
_draggableviews_get_draggable_sort Get the draggable views weight sort of a view if there is one and return its ID. If there are multiple of these sorts, either the one matching a specified table alias or the first one is returned.
_draggableviews_get_field Gets the Draggableviews field if it exists on the passed-in view.
_draggableviews_get_order_path Get the path to the order view.
_draggableviews_get_order_view_display Get the view display identifier.
_draggableviews_get_views_options Helper function that returns an option list of all draggable views or let you inspect a specific view to see if it's a draggable view itself and returns the appropriate option for that.
_draggableviews_load_order_view Load and/or built the order view with the correct view display.