You are here

class views_plugin_style_geojson in Views GeoJSON 7

Same name and namespace in other branches
  1. 6 views/views_plugin_style_geojson.inc \views_plugin_style_geojson

Implementation of views_plugin_style.

Hierarchy

Expanded class hierarchy of views_plugin_style_geojson

1 string reference to 'views_plugin_style_geojson'
views_geojson_views_plugins in views/views_geojson.views.inc
Implements hook_views_plugins().

File

views/views_plugin_style_geojson.inc, line 14
Views style plugin to render nodes in the GeoJSON format.

View source
class views_plugin_style_geojson extends views_plugin_style {

  /**
   * Store the entity info array.
   *
   * @var array
   */
  protected $entity_info;

  /**
   * Store the entity type.
   *
   * @var string
   */
  protected $entity_type;

  /**
   * If this view is displaying an entity, save the entity type and info.
   */
  public function init(&$view, &$display, $options = NULL) {
    parent::init($view, $display, $options);

    // Pretty-print the JSON.
    module_load_include('inc', 'views_geojson', 'views_geojson.helpers');

    // Search api indexes store the entity metadata in the views data array.
    if (mb_strpos($view->base_table, 'search_api_index_') === 0) {
      $views_data = views_fetch_data();
      if (isset($views_data[$view->base_table]['table']['entity type'])) {
        $this->entity_type = $views_data[$view->base_table]['table']['entity type'];
        $this->entity_info = entity_get_info($this->entity_type);
      }
    }
    else {
      foreach (entity_get_info() as $key => $info) {
        if ($view->base_table === $info['base table']) {
          $this->entity_type = $key;
          $this->entity_info = $info;
          break;
        }
      }
    }
  }

  /**
   * Checks if a field handler class is handled by the entity module.
   *
   * @param object $field_handler_instance
   *   The field handler instance to check.
   *
   * @return bool
   *   TRUE if the field is handled by the entity module views integration.
   */
  public function is_entity_views_handler($field_handler_instance) {
    if (!module_exists('entity')) {
      return FALSE;
    }
    $static_cache =& drupal_static(__METHOD__, array());
    $handler_class = get_class($field_handler_instance);
    if (!isset($static_cache[$handler_class])) {
      $static_cache[$handler_class] = FALSE;
      foreach (entity_views_get_field_handlers() as $field_handler) {
        if ($field_handler_instance instanceof $field_handler) {
          return $static_cache[$handler_class] = TRUE;
        }
      }
    }
    return $static_cache[$handler_class];
  }

  /**
   * Set default options.
   */
  public function option_definition() {
    $options = parent::option_definition();
    $options['data_source'] = array(
      'contains' => array(
        'value' => array(
          'default' => 'asc',
        ),
        'latitude' => array(
          'default' => 0,
        ),
        'longitude' => array(
          'default' => 0,
        ),
        'geofield' => array(
          'default' => 0,
        ),
        'wkt' => array(
          'default' => 0,
        ),
        'name_field' => array(
          'default' => 0,
        ),
        'description_field' => array(
          'default' => 0,
        ),
      ),
    );
    $options['attributes'] = array(
      'default' => NULL,
      'translatable' => FALSE,
    );
    $options['jsonp_prefix'] = array(
      'default' => NULL,
      'translatable' => FALSE,
    );
    $options['content_type'] = array(
      'default' => 'default',
      'translatable' => FALSE,
    );
    $options['typed_data_excluded_fields'] = array(
      'default' => array(),
      'translatable' => FALSE,
    );
    $options['using_views_api_mode'] = array(
      'default' => FALSE,
      'translatable' => FALSE,
    );
    return $options;
  }

  /**
   * Provide a form for setting options.
   */
  public function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);
    $fields = array();

    // Get list of fields in this view & flag available geodata fields.
    $handlers = $this->display->handler
      ->get_handlers('field');

    // Check for any fields, as the view needs them.
    if (empty($handlers)) {
      $form['error_markup'] = array(
        '#value' => t('You need to enable at least one field before you can configure your field settings'),
        '#prefix' => '<div class="error form-item description">',
        '#suffix' => '</div>',
      );
      return;
    }

    // Go through fields.
    foreach ($handlers as $field_id => $handler) {
      $fields[$field_id] = $handler->definition['title'];
      $field_info = NULL;
      if (!empty($handler->field_info)) {
        $field_info = $handler->field_info;
      }
      elseif ($this
        ->is_entity_views_handler($handler)) {

        // Strip the basic field name from the entity views handler field and
        // fetch the field info for it.
        $property = EntityFieldHandlerHelper::get_selector_field_name($handler->real_field);
        if ($field_name = EntityFieldHandlerHelper::get_selector_field_name(mb_substr($handler->real_field, 0, mb_strpos($handler->real_field, ':' . $property)), ':')) {
          $field_info = field_info_field($field_name);
        }
      }
      $fields_info[$field_id]['type'] = $field_info['type'];
    }

    // Default data source.
    $data_source_options = array(
      'latlon' => t('Other: Lat/Lon Point'),
      'geofield' => t('Geofield'),
      'wkt' => t('WKT'),
    );

    // Data Source options.
    $form['data_source'] = array(
      '#type' => 'fieldset',
      '#tree' => TRUE,
      '#title' => t('Data Source'),
    );
    $form['data_source']['value'] = array(
      '#type' => 'select',
      '#multiple' => FALSE,
      '#title' => t('Map Data Sources'),
      '#description' => t('Choose which sources of data that the map will provide features for.'),
      '#options' => $data_source_options,
      '#default_value' => $this->options['data_source']['value'],
    );

    // Other Lat and Lon data sources.
    if (count($fields) > 0) {
      $form['data_source']['latitude'] = array(
        '#type' => 'select',
        '#title' => t('Latitude Field'),
        '#description' => t('Choose a field for Latitude.  This should be a field that is a decimal or float value.'),
        '#options' => $fields,
        '#default_value' => $this->options['data_source']['latitude'],
        '#process' => array(
          'ctools_dependent_process',
        ),
        '#dependency' => array(
          'edit-style-options-data-source-value' => array(
            'latlon',
          ),
        ),
      );
      $form['data_source']['longitude'] = array(
        '#type' => 'select',
        '#title' => t('Longitude Field'),
        '#description' => t('Choose a field for Longitude.  This should be a field that is a decimal or float value.'),
        '#options' => $fields,
        '#default_value' => $this->options['data_source']['longitude'],
        '#process' => array(
          'ctools_dependent_process',
        ),
        '#dependency' => array(
          'edit-style-options-data-source-value' => array(
            'latlon',
          ),
        ),
      );

      // Get Geofield-type fields.
      $geofield_fields = array();
      foreach ($fields as $field_id => $field) {
        if ($fields_info[$field_id]['type'] === 'geofield') {
          $geofield_fields[$field_id] = $field;
        }
      }

      // Geofield.
      $form['data_source']['geofield'] = array(
        '#type' => 'select',
        '#title' => t('Geofield'),
        '#description' => t("Choose a Geofield field. Any formatter will do; we'll access Geofield's underlying WKT format."),
        '#options' => $geofield_fields,
        '#default_value' => $this->options['data_source']['geofield'],
        '#process' => array(
          'ctools_dependent_process',
        ),
        '#dependency' => array(
          'edit-style-options-data-source-value' => array(
            'geofield',
          ),
        ),
      );

      // WKT.
      $form['data_source']['wkt'] = array(
        '#type' => 'select',
        '#title' => t('WKT'),
        '#description' => t('Choose a WKT format field.'),
        '#options' => $fields,
        '#default_value' => $this->options['data_source']['wkt'],
        '#process' => array(
          'ctools_dependent_process',
        ),
        '#dependency' => array(
          'edit-style-options-data-source-value' => array(
            'wkt',
          ),
        ),
      );
    }
    $form['data_source']['name_field'] = array(
      '#type' => 'select',
      '#title' => t('Title Field'),
      '#description' => t('Choose the field to appear as title on tooltips.'),
      '#options' => array_merge(array(
        '' => '',
      ), $fields),
      '#default_value' => $this->options['data_source']['name_field'],
    );
    $form['data_source']['description_field'] = array(
      '#type' => 'select',
      '#title' => t('Description'),
      '#description' => t('Choose the field or rendering method to appear as
          description on tooltips.'),
      '#required' => FALSE,
      '#options' => array_merge(array(
        '' => '',
      ), $fields),
      '#default_value' => $this->options['data_source']['description_field'],
    );

    // Attributes and variable styling description.
    $form['attributes'] = array(
      '#type' => 'fieldset',
      '#title' => t('Attributes and Styling'),
      '#description' => t('Attributes are field data attached to each feature.  This can be used with styling to create Variable styling.'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $form['jsonp_prefix'] = array(
      '#type' => 'textfield',
      '#title' => t('JSONP prefix'),
      '#default_value' => $this->options['jsonp_prefix'],
      '#description' => t('If used the JSON output will be enclosed with parentheses and prefixed by this label, as in the JSONP format.'),
    );
    $form['content_type'] = array(
      '#type' => 'radios',
      '#title' => t('Content-Type'),
      '#options' => array(
        'default' => t('Default: application/json'),
        'text/json' => t('text/json'),
      ),
      '#default_value' => $this->options['content_type'],
      '#description' => t('The Content-Type header that will be sent with the JSON output.'),
    );
    $form['typed_data_excluded_fields'] = array(
      '#type' => 'select',
      '#multiple' => TRUE,
      '#title' => t('Excluded from typed data handling:'),
      '#description' => t('By default the data processing attempts to properly type data before creating the json. If you receive data that seem the wrong format you can disable the processing here.'),
      '#options' => $fields,
      '#default_value' => $this->options['typed_data_excluded_fields'],
    );

    // Make array of attributes.
    $variable_fields = array();

    // Add name and description.
    if (!empty($this->options['data_source']['name_field'])) {
      $variable_fields['name'] = '${name}';
    }
    if (!empty($this->options['data_source']['description_field'])) {
      $variable_fields['description'] = '${description}';
    }

    // Go through fields.
    foreach ($this->view->display_handler
      ->get_handlers('field') as $field => $handler) {
      if ($field !== $this->options['data_source']['name_field'] && $field !== $this->options['data_source']['description_field']) {
        $variable_fields[$field] = '${' . $field . '}';
      }
    }
    $variables_list = array(
      'items' => $variable_fields,
      'attributes' => array(
        'class' => array(
          'description',
        ),
      ),
    );
    $markup = '<p class="description">' . t('Fields added to this view will be attached to their respective feature, (point, line, polygon,) as attributes. These attributes can then be used to add variable styling to your themes. This is accomplished by using the %syntax syntax in the values for a style.  The following is a list of formatted variables that are currently available; these can be placed right in the style interface.', array(
      '%syntax' => '${field_name}',
    )) . '</p>';
    $markup .= theme('item_list', $variables_list);
    $markup .= '<p class="description">' . t('Please note that this does not apply to Grouped Displays.') . '</p>';
    $form['attributes']['styling'] = array(
      '#type' => 'markup',
      '#markup' => $markup,
    );
  }

  /**
   * Implementation of view_style_plugin::render().
   */
  public function render() {
    $args = func_get_args();
    $results = array_shift($args);
    $features = array(
      'type' => 'FeatureCollection',
      'features' => array(),
    );

    // Render each row.
    foreach ($results as $i => $row) {
      $this->view->row_index = $i;
      if ($feature = _views_geojson_render_fields($this->view, $row, $i)) {
        $features['features'][] = $feature;
      }
    }
    unset($this->view->row_index);

    // Render the collection to JSON.
    $json = drupal_json_encode($features);
    if (!empty($this->options['jsonp_prefix'])) {
      $json = $this->options['jsonp_prefix'] . "({$json})";
    }

    // Replace form field placeholders.
    $json = $this
      ->form_fields_replace($json);
    if (!empty($this->view->preview)) {

      // Pretty-print the JSON. This only works for 'page' displays.
      $json = _views_geojson_encode_formatted($features);
      if (!empty($this->options['jsonp_prefix'])) {
        $json = $this->options['jsonp_prefix'] . "({$json})";
      }
      if ($this->display->display_plugin === 'page') {
        $json = "<pre>{$json}</pre>";
      }

      // Replace form field placeholders.
      $json = $this
        ->form_fields_replace($json, FALSE);
    }
    elseif ($this->display->display_plugin === 'feed') {
      $content_type = $this->options['content_type'] === 'default' ? 'application/json' : $this->options['content_type'];
      drupal_add_http_header('Content-Type', "{$content_type}; charset=utf-8");
    }
    else {
      if ($this->display->display_plugin === 'page' && !$this->options['using_views_api_mode']) {

        // Change the content type header since we're sending a JSON response.
        $content_type = $this->options['content_type'] === 'default' ? 'application/json' : $this->options['content_type'];
        drupal_add_http_header('Content-Type', "{$content_type}; charset=utf-8");

        // Page display prints and exits; this is BAD (prevents caching) but
        // supports legacy configurations.
        print $json;
        drupal_exit();
      }
    }

    // Everything else returns output.
    return $json;
  }

  /**
   * Replaces form field placehoders with their substitutions.
   *
   * @param string $output
   *   The text output of the current display.
   * @param bool $encode
   *   Whether or not to json encode placeholders.
   *
   * @return string
   *   Returns the output of the current display with form field placeholders
   *   replaced.
   *
   * @see template_preprocess_views_view()
   */
  protected function form_fields_replace($output, $encode = TRUE) {

    // If form fields were found in the view.
    if (views_view_has_form_elements($this->view)) {

      // The minimum form elements needed to do the replacement are 'output',
      // '#substitutions' and the form element name of each field.
      $elements = array(
        'output',
        '#substitutions',
      );
      foreach ($this->view->field as $field_name => $field) {
        $form_element_name = $field_name;
        if (method_exists($field, 'form_element_name')) {
          $form_element_name = $field
            ->form_element_name();
        }
        $elements[] = $form_element_name;
      }

      // Wrap the output in a views form.
      $form = drupal_get_form(views_form_id($this->view), $this->view, $output);

      // Remove all but the necessary form elements.
      $form = array_intersect_key($form, array_flip($elements));
      if ($encode) {

        // JSON encode placeholders and fields for output in feeds.
        foreach ($form['#substitutions']['#value'] as &$substitution) {
          $field_name = $substitution['field_name'];
          $row_id = $substitution['row_id'];

          // If the field exists for the row.
          if (isset($form[$field_name][$row_id])) {

            // Render and json encode the the field for the row.
            $form[$field_name][$row_id] = array(
              '#markup' => trim(drupal_json_encode(drupal_render($form[$field_name][$row_id])), '"'),
            );
          }
          $substitution['placeholder'] = trim(drupal_json_encode($substitution['placeholder']), '"');
        }
      }

      // Perform the replacement.
      $output = theme('views_form_views_form', array(
        'form' => $form,
      ));
    }
    return $output;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
views_object::$definition public property Handler's definition.
views_object::$options public property Except for displays, options for the object will be held here. 1
views_object::altered_option_definition function Collect this handler's option definition and alter them, ready for use.
views_object::construct public function Views handlers use a special construct function. 4
views_object::export_option public function 1
views_object::export_options public function
views_object::export_option_always public function Always exports the option, regardless of the default value.
views_object::options Deprecated public function Set default options on this object. 1
views_object::set_default_options public function Set default options.
views_object::set_definition public function Let the handler know what its full definition is.
views_object::unpack_options public function Unpack options over our existing defaults, drilling down into arrays so that defaults don't get totally blown away.
views_object::unpack_translatable public function Unpack a single option definition.
views_object::unpack_translatables public function Unpacks each handler to store translatable texts.
views_object::_set_option_defaults public function
views_plugin::$display public property The current used views display.
views_plugin::$plugin_name public property The plugin name of this plugin, for example table or full.
views_plugin::$plugin_type public property The plugin type of this plugin, for example style or query.
views_plugin::$view public property The top object of a view. Overrides views_object::$view 1
views_plugin::additional_theme_functions public function Provide a list of additional theme functions for the theme info page.
views_plugin::options_submit public function Handle any special handling on the validate form. 9
views_plugin::plugin_title public function Return the human readable name of the display.
views_plugin::summary_title public function Returns the summary of the settings in the display. 8
views_plugin::theme_functions public function Provide a full list of possible theme templates used by this style.
views_plugin_style::$row_plugin public property The row plugin, if it's initialized and the style itself supports it.
views_plugin_style::$row_tokens public property Store all available tokens row rows.
views_plugin_style::build_sort public function Called by the view builder to see if this style handler wants to interfere with the sorts. If so it should build; if it returns any non-TRUE value, normal sorting will NOT be added to the query. 1
views_plugin_style::build_sort_post public function Called by the view builder to let the style build a second set of sorts that will come after any other sorts in the view. 1
views_plugin_style::destroy public function Destructor. Overrides views_object::destroy
views_plugin_style::even_empty public function Should the output of the style plugin be rendered even if it's empty. 1
views_plugin_style::get_field public function Get a rendered field.
views_plugin_style::get_field_value public function Get the raw field value.
views_plugin_style::get_row_class public function Return the token replaced row class for the specified row.
views_plugin_style::options_validate public function Validate the options form. Overrides views_plugin::options_validate
views_plugin_style::pre_render public function Allow the style to do stuff before each row is rendered.
views_plugin_style::query public function Add anything to the query that we might need to. Overrides views_plugin::query 2
views_plugin_style::render_fields public function Render all of the fields for a given style and store them on the object.
views_plugin_style::render_grouping public function Group records as needed for rendering.
views_plugin_style::render_grouping_sets public function Render the grouping sets.
views_plugin_style::tokenize_value public function Take a value and apply token replacement logic to it.
views_plugin_style::uses_fields public function Return TRUE if this style also uses fields.
views_plugin_style::uses_row_class public function Return TRUE if this style also uses a row plugin.
views_plugin_style::uses_row_plugin public function Return TRUE if this style also uses a row plugin.
views_plugin_style::uses_tokens public function Return TRUE if this style uses tokens.
views_plugin_style::validate public function Validate that the plugin is correct and can be saved. Overrides views_plugin::validate
views_plugin_style_geojson::$entity_info protected property Store the entity info array.
views_plugin_style_geojson::$entity_type protected property Store the entity type.
views_plugin_style_geojson::form_fields_replace protected function Replaces form field placehoders with their substitutions.
views_plugin_style_geojson::init public function If this view is displaying an entity, save the entity type and info. Overrides views_plugin_style::init
views_plugin_style_geojson::is_entity_views_handler public function Checks if a field handler class is handled by the entity module.
views_plugin_style_geojson::options_form public function Provide a form for setting options. Overrides views_plugin_style::options_form
views_plugin_style_geojson::option_definition public function Set default options. Overrides views_plugin_style::option_definition
views_plugin_style_geojson::render public function Implementation of view_style_plugin::render(). Overrides views_plugin_style::render