You are here

gmap_cck.module in GMap Addons 7

Same filename and directory in other branches
  1. 5 gmap_cck.module
  2. 6 gmap_cck.module

display of a google map as cck field

File

gmap_cck.module
View source
<?php

/**
 * @file
 * display of a google map as cck field
 */

/**
 * @addtogroup hooks
 * @{
 */

/**
 * Declare information about a field type.
 *
 * @return
 *   An array keyed by field type name. Each element of the array is an associative
 *   array with these keys and values:
 *   - "label": The human-readable label for the field type.
 */
function gmap_cck_field_info() {
  return array(
    'gmap_cck' => array(
      'label' => 'Google map view',
    ),
  );
}

/**
 * Handle the parameters for a field.
 *
 * @param $op
 *   The operation to be performed. Possible values:
 *   - "form": Display the field settings form.
 *   - "validate": Check the field settings form for errors.
 *   - "save": Declare which fields to save back to the database.
 *   - "database columns": Declare the columns that content.module should create
 *     and manage on behalf of the field. If the field module wishes to handle
 *     its own database storage, this should be omitted.
 *   - "callbacks": Describe the field's behaviour regarding hook_field operations.
 *   - "tables" : Declare the Views tables informations for the field.
 *     Use this operator only if you need to override CCK's default general-purpose
 *     implementation.
 *     In this case, it is probably a good idea to use the default definitions
 *     returned by content_views_field_tables($field) as a start point for your own
 *     definitions.
 *   - "arguments" : Declare the Views arguments informations for the field.
 *     Use this operator only if you need to override CCK's default general-purpose
 *     implementation.
 *     In this case, it is probably a good idea to use the default definitions
 *     returned by content_views_field_arguments($field) as a start point for your own
 *     definitions.
 *   - "filters": Declare the Views filters available for the field.
 *     (this is used in CCK's default Views tables definition)
 *     They always apply to the first column listed in the "database columns"
 *     array.
 * @param $field
 *   The field on which the operation is to be performed.
 * @return
 *   This varies depending on the operation.
 *   - "form": an array of form elements to add to
 *     the settings page.
 *   - "validate": no return value. Use form_set_error().
 *   - "save": an array of names of form elements to
 *     be saved in the database.
 *   - "database columns": an array keyed by column name, with arrays of column
 *     information as values. This column information must include "type", the
 *     MySQL data type of the column, and may also include a "sortable" parameter
 *     to indicate to views.module that the column contains ordered information.
 *     Details of other information that can be passed to the database layer can
 *     be found at content_db_add_column().
 *   - "callbacks": an array describing the field's behaviour regarding hook_field
 *     operations. The array is keyed by hook_field operations ('view', 'validate'...)
 *     and has the following possible values :
 *       CONTENT_CALLBACK_NONE     : do nothing for this operation
 *       CONTENT_CALLBACK_CUSTOM   : use the behaviour in hook_field(operation)
 *       CONTENT_CALLBACK_DEFAULT  : use content.module's default bahaviour
 *     Note : currently only the 'view' operation implements this feature.
 *     All other field operation implemented by the module _will_ be executed
 *     no matter what.
 *   - "tables": an array of 'tables' definitions as expected by views.module
 *     (see Views Documentation).
 *   - "arguments": an array of 'arguments' definitions as expected by views.module
 *     (see Views Documentation).
 *   - "filters": an array of 'filters' definitions as expected by views.module
 *     (see Views Documentation).
 *     When providing several filters, it is recommended to use the 'name'
 *     attribute in order to let the user distinguish between them. If no 'name'
 *     is specified for a filter, the key of the filter will be used instead.
 */
function gmap_cck_field_settings($op, $field) {
  switch ($op) {
    case 'form':
      $def = gmap_defaults();
      foreach (array(
        'maptype',
        'controltype',
        'width',
        'height',
        'latlong',
      ) as $key) {
        if (isset($field[$key]) && !empty($field[$key])) {
          $def[$key] = $field[$key];
        }
      }
      $form = gmap_macro_builder_form($def);
      $form['#title'] = t('Google Map settings');

      // keep: mapdiv
      unset($form['macroform']['overlayedit']);
      unset($form['macroform']['mapid']);

      // keep: maptype, controltype
      unset($form['macroform']['address']);

      //unset($form['macroform']['latlong']);

      // keep: width, height, alignment, zoom
      unset($form['macroform']['macro']);

      // GPX file support
      $gpx_opts = array(
        t('Disabled'),
      );
      if (module_exists('filefield')) {
        $gpx_opts['filefield'] = t('CCK Filefield');
      }

      // TODO: attachment to node, uploaded file, url from user, ...
      if (count($gpx_opts) > 1) {
        $form['gpx'] = array(
          '#type' => 'select',
          '#title' => t("GPX File support"),
          '#options' => array(
            0 => t('Disabled'),
            'filefield' => t('CCK Filefield'),
          ),
          '#default_value' => $field['gpx'],
        );

        // if gpx-support is enabled we need a source for the data
        $form['gpx_src'] = array(
          '#type' => 'textfield',
          '#title' => t('GPX data source parameter'),
          '#description' => t('Only used if gpx-support is enabled'),
          '#default_value' => $field['gpx_src'],
        );
      }

      // noderef field(s) to nodes with locations for markers
      if (module_exists('nodereference')) {
        $form['marker_noderef'] = array(
          '#type' => 'textfield',
          '#title' => t('Marker noderef'),
          '#description' => t('Name of noderef field to nodes to show as markers'),
          '#default_value' => $field['marker_noderef'],
        );
      }
      return $form;
    case 'validate':

      // check stuff entered in form-fields
      break;
    case 'save':
      return array(
        'maptype',
        'controltype',
        'width',
        'height',
        'alignment',
        'zoom',
        'latlong',
        'gpx',
        'gpx_src',
        'marker_noderef',
      );
    case 'database columns':
      $columns = array(
        // TODO: ??? type -> longtext ???
        'value' => array(
          'type' => 'varchar',
          'length' => 255,
          'not null' => true,
          'default' => "''",
          'sortable' => false,
        ),
      );
      return $columns;
    case 'callbacks':

      // CHECK: ??? any effect on calling ..._field() ???
      return array(
        'view' => CONTENT_CALLBACK_CUSTOM,
      );
  }
}

/**
 * Define the behavior of a field type.
 *
 * @param $op
 *   What kind of action is being performed. Possible values:
 *   - "load": The node is about to be loaded from the database. This hook
 *     should be used to load the field.
 *   - "view": The node is about to be presented to the user. The module
 *     should prepare and return an HTML string containing a default
 *     representation of the field.
 *     It will be called only if 'view' was set to TRUE in hook_field_settings('callbacks')
 *   - "validate": The user has just finished editing the node and is
 *     trying to preview or submit it. This hook can be used to check or
 *     even modify the node. Errors should be set with form_set_error().
 *   - "submit": The user has just finished editing the node and the node has
 *     passed validation. This hook can be used to modify the node.
 *   - "insert": The node is being created (inserted in the database).
 *   - "update": The node is being updated.
 *   - "delete": The node is being deleted.
 * @param &$node
 *   The node the action is being performed on. This argument is passed by
 *   reference for performance only; do not modify it.
 * @param $field
 *   The field the action is being performed on.
 * @param &$items
 *   The contents of the field in this node. Changes to this variable will
 *   be saved back to the node object.
 * @return
 *   This varies depending on the operation.
 *   - The "load" operation should return an object containing extra values
 *     to be merged into the node object.
 *   - The "view" operation should return a string containing an HTML
 *     representation of the field data.
 *   - The "insert", "update", "delete", "validate", and "submit" operations
 *     have no return value.
 *
 * In most cases, only "validate" operations is relevant ; the rest
 * have default implementations in content_field() that usually suffice.
 */
function gmap_cck_field($op, &$node, $field, &$items, $teaser, $page) {
  switch ($op) {
    case 'view':
      $context = $teaser ? 'teaser' : 'full';
      $formatter = isset($field['display_settings'][$context]['format']) ? $field['display_settings'][$context]['format'] : 'default';
      foreach ($items as $delta => $item) {
        $items[$delta]['view'] = content_format($field, $item, $formatter, $node);
      }
      return theme('field', $node, $field, $items, $teaser, $page);
  }
}

/**
 * Declare information about a formatter.
 *
 * @return
 *   An array keyed by formatter name. Each element of the array is an associative
 *   array with these keys and values:
 *   - "label": The human-readable label for the formatter.
 *   - "field types": An array of field type names that can be displayed using
 *     this formatter.
 */
function gmap_cck_field_formatter_info() {
  return array(
    'default' => array(
      'label' => t('Default map'),
      'field types' => array(
        'gmap_cck',
      ),
    ),
  );
}

/**
 * Prepare an individual item for viewing in a browser.
 *
 * @param $field
 *   The field the action is being performed on.
 * @param $item
 *   An array, keyed by column, of the data stored for this item in this field.
 * @param $formatter
 *   The name of the formatter being used to display the field.
 * @param $node
 *   The node object, for context. Will be NULL in some cases.
 *   Warning : when displaying field retrieved by Views, $node will not
 *   be a "full-fledged" node object, but an object containg the data returned
 *   by the Views query (at least nid, vid, changed)
 * @return
 *   An HTML string containing the formatted item.
 *
 * In a multiple-value field scenario, this function will be called once per
 * value currently stored in the field. This function is also used as the handler
 * for viewing a field in a views.module tabular listing.
 *
 * It is important that this function at the minimum perform security
 * transformations such as running check_plain() or check_markup().
 */
function gmap_cck_field_formatter($field, $item, $formatter, $node) {
  if (!isset($item['value'])) {
    return '';
  }
  $m = unserialize($item['value']);
  if (!is_array($m)) {
    $m = array();
  }
  if (!empty($field['gpx']) && _gmap_cck_check_gpx_source($m, $field, $node)) {
    _gmap_cck_get_gpx($m, $field, $node);
  }
  if (!empty($field['marker_noderef'])) {
    _gmap_cck_noderef_markers($m, $field, $node);
  }
  $map = array_merge(gmap_defaults(), $field, $m);
  $map['id'] = 'gmap';
  if (isset($node) && isset($node->nid)) {
    $map['id'] .= '_' . $node->nid;
  }
  $map['id'] .= '_' . $field['field_name'];

  // TODO: correct width, height & zoom if ($formatter != 'default')
  return theme('gmap', array(
    '#settings' => $map,
  ));
}

/**
 * Declare information about a widget.
 *
 * @return
 *   An array keyed by widget name. Each element of the array is an associative
 *   array with these keys and values:
 *   - "label": The human-readable label for the widget.
 *   - "field types": An array of field type names that can be edited using
 *     this widget.
 */
function gmap_cck_widget_info() {
  return array(
    'gmap_cck' => array(
      'label' => 'Google Map',
      'field types' => array(
        'gmap_cck',
      ),
    ),
  );
}

/**
 * Handle the parameters for a widget.
 *
 * @param $op
 *   The operation to be performed. Possible values:
 *   - "form": Display the widget settings form.
 *   - "validate": Check the widget settings form for errors.
 *   - "save": Declare which pieces of information to save back to the database.
 *   - "callbacks": Describe the widget's behaviour regarding hook_widget operations.
 * @param $widget
 *   The widget on which the operation is to be performed.
 * @return
 *   This varies depending on the operation.
 *   - "form": an array of form elements to add to the settings page.
 *   - "validate": no return value. Use form_set_error().
 *   - "save": an array of names of form elements to be saved in the database.
 *   - "callbacks": an array describing the widget's behaviour regarding hook_widget
 *     operations. The array is keyed by hook_widget operations ('form', 'validate'...)
 *     and has the following possible values :
 *       CONTENT_CALLBACK_NONE     : do nothing for this operation
 *       CONTENT_CALLBACK_CUSTOM   : use the behaviour in hook_widget(operation)
 *       CONTENT_CALLBACK_DEFAULT  : use content.module's default bahaviour
 *     Note : currently only the 'default value' operation implements this feature.
 *     All other widget operation implemented by the module _will_ be executed
 *     no matter what.
 */
function gmap_cck_widget_settings($op, $widget) {
  switch ($op) {
    case 'callbacks':

      // CHECK: do we need this ???
      return array(
        'default value' => CONTENT_CALLBACK_CUSTOM,
      );
  }
}

/**
 * Define the behavior of a widget.
 *
 * @param $op
 *   What kind of action is being performed. Possible values:
 *   - "prepare form values": The editing form will be displayed. The widget
 *     should perform any conversion necessary from the field's native storage
 *     format into the storage used for the form. Convention dictates that the
 *     widget's version of the data should be stored beginning with "default".
 *   - "form": The node is being edited, and a form should be prepared for
 *     display to the user.
 *   - "validate": The user has just finished editing the node and is
 *     trying to preview or submit it. This hook can be used to check or
 *     even modify the node. Errors should be set with form_set_error().
 *   - "process form values": The inverse of the prepare operation. The widget
 *     should convert the data back to the field's native format.
 *   - "submit": The user has just finished editing the node and the node has
 *     passed validation. This hook can be used to modify the node.
 * @param &$node
 *   The node the action is being performed on. This argument is passed by
 *   reference for performance only; do not modify it.
 * @param $field
 *   The field the action is being performed on.
 * @param &$items
 *   The contents of the field in this node. Changes to this variable will
 *   be saved back to the node object.
 * @return
 *   This varies depending on the operation.
 *   - The "form" operation should return an array of form elements to display.
 *   - Other operations have no return value.
 */
function gmap_cck_widget($op, &$node, $field, &$items) {
  switch ($op) {
    case 'prepare form values':

      // db-value (serialized array) is in $items[0]['value'];
      $items[0]['raw'] = unserialize($items[0]['value']);

      // show some gpx-data?
      if (!empty($field['gpx'])) {
        _gmap_cck_check_gpx_source($items[0]['raw'], $field, $node);
      }

      // if ($field['multiple']) { ??? }
      break;
    case 'form':
      $rd =& $items[0]['raw'];

      // raw data
      if (empty($rd)) {
        $rd = array();
      }
      $ndefs = array(
        'maptype' => $field['maptype'],
        'width' => $field['width'],
        'height' => $field['height'],
        'zoom' => $field['zoom'],
        'latlong' => $field['latlong'],
      );
      if (isset($rd['gpx'])) {
        $gpx_support = array(
          '#value' => 'GPX support enabled',
        );
        _gmap_cck_get_gpx($rd, $field, $node);
      }
      $ndefs = array_merge(gmap_defaults(), $ndefs, $rd);
      $hide = array(
        'mapid' => 1,
        // type=hidden
        'width' => 1,
        'height' => 1,
        'alignment' => 1,
        // 'zoom' => 2,
        'maptype' => 1,
        'controltype' => 1,
        'macro' => 2,
      );
      $form = gmap_macro_builder_form($ndefs, $hide);

      // remove/hide unwanted parts of form
      $mf =& $form['macroform'];
      $mf['#title'] = t('Select map view');
      unset($mf['overlayedit'], $mf['address']);
      foreach ($mf as $key => $val) {
        if ($key[0] == '#') {
          continue;
        }
        $mf['#tree'] = true;
        $mf['#parents'] = array(
          $field['field_name'],
        );
      }
      $form[$field['field_name']] = $form['macroform'];
      unset($form['macroform']);
      if (isset($gpx_support)) {
        $form['gpx_support'] = $gpx_support;
      }
      return $form;
    case 'process form values':

      // TODO: if ($field['multiple']) ...
      // get the values and build serialized array for db-save

      /*      $ms = '[gmap zoom='. $items['zoom'] .'|center='.
                  $items['latlong'] .'|width='. $field['width'] .'|height='.
                  $field['height'] .'|id=gmap_'. $field['field_name'] .'|control='.
                  $field['controltype'] .'|type='. $field['maptype'] .']';
      */
      $v = array(
        'zoom' => $items['zoom'],
        'latlong' => $items['latlong'],
      );

      // TODO: add more props like 'markers', gpx-file, ...
      foreach ($items as $delta => $item) {
        unset($items[$delta]);
      }

      // show some gpx-data?
      if (!empty($field['gpx'])) {
        _gmap_cck_check_gpx_source($v, $field, $node);
      }
      $items[0]['value'] = serialize($v);

      // $ms;
      break;
  }
}

/**
 * @} End of "addtogroup hooks".
 */
function _gmap_cck_check_gpx_source(&$data, &$field, &$node) {
  $r = true;
  if ($field['gpx'] == 'filefield') {
    $fname = 'field_' . $field['gpx_src'];
    $ff =& $node->{$fname};
    $r = isset($ff) && _gmap_cck_gpxdata_filefield($data, $ff);
  }
  if (!$r) {

    // TODO: ev. try to use $data['gpx']['file']
    unset($data['gpx']);
  }
  return $r;
}

/**
 * try to always get filedata from filefield, even if we're in preview mode
 */
function _gmap_cck_gpxdata_filefield(&$v, &$filefield) {
  foreach ($filefield as $i => $data) {
    if (!isset($data['filepath'])) {
      continue;
    }
    $v['gpx'] = array(
      'type' => 'file',
      'file' => $data['filepath'],
    );
    return true;
  }
  return false;
}

/**
 * process gpx-parameter to show something on the map
 */
function _gmap_cck_get_gpx(&$settings, &$field, &$node) {
  require_once 'gmap_gpx.inc';
  if ($settings['gpx']['type'] == 'file') {
    $f = $settings['gpx']['file'];
    if (!file_exists($f)) {

      // was in temp location last time we looked?
      if (!isset($field) || !isset($node)) {
        return false;
      }
      if (!_gmap_cck_check_gpx_source($settings, $field, $node)) {
        return false;
      }
      $f = $settings['gpx']['file'];
    }
    gmap_gpx_parse_file($f);
    gmap_gpx_data2map($settings);
    gmap_gpx_cleanup();
  }
  unset($settings['gpx']);
  return true;
}

/**
 * load locations from nodes pointed at with noderef field, show markers
 */
function _gmap_cck_noderef_markers(&$settings, &$field, &$node) {
  $fname = 'field_' . $field['marker_noderef'];
  $nrf =& $node->{$fname};
  $nids = array();
  foreach ($nrf as $delta => $item) {
    if (!empty($item['nid']) && is_numeric($item['nid'])) {
      $nids[] = $item['nid'];
      $n = node_load($item['nid']);
      if ($n) {
        $settings['markers'][] = array(
          'label' => $n->title,
          'opts' => array(
            'title' => $n->title,
          ),
          'latitude' => $n->locations[0]['latitude'],
          'longitude' => $n->locations[0]['longitude'],
          'markername' => variable_get('gmap_node_marker_' . $n->type, 'drupal'),
          'text' => theme('gmapnodelabel', $n),
        );
      }
    }
  }

  /* TODO: go this route if we have too many (how many?) nodes
    if (count($nids)) {
      $nidstr = implode(',', $nids);
      //$res = db_query("SELECT * FROM {location} l , {node_revisions} nr WHERE".
      //  " l.type = 'node' AND l.eid = nr.vid AND nr.nid IN('$nidstr')");
      $res = db_query("SELECT nr.title, n.nid, n.type, max(nr.vid) vid,".
        " l.latitude, l.longitude FROM location l, node n, node_revisions nr".
        " WHERE l.type = 'node' AND l.eid = nr.vid AND n.nid = nr.nid AND".
        " nr.nid IN($nidstr) GROUP BY n.nid");
      $locs = array();
      while ($nl = db_fetch_object($res)) {
        if (!isset($locs[$nl->nid]) || ($locs[$nl->nid]->timestamp < $nl->timestamp)) {
          $locs[$nl->nid] = $nl;
        }
      }
      foreach ($locs as $nid => $nl) {
        $settings['markers'][] = array(
          'label' => $nl->title,
          'latitude' => $nl->latitude,
          'longitude' => $nl->longitude,
          'markername' => variable_get('gmap_node_marker_'. $nl->type, 'drupal'),
          //'text' => theme('gmapnodelabel',$nl), // TODO:
          'popuplink' => url('map/query/node/'. $nl->nid),
        );
      }
    }
  */
}

Functions

Namesort descending Description
gmap_cck_field Define the behavior of a field type.
gmap_cck_field_formatter Prepare an individual item for viewing in a browser.
gmap_cck_field_formatter_info Declare information about a formatter.
gmap_cck_field_info Declare information about a field type.
gmap_cck_field_settings Handle the parameters for a field.
gmap_cck_widget Define the behavior of a widget.
gmap_cck_widget_info Declare information about a widget.
gmap_cck_widget_settings Handle the parameters for a widget.
_gmap_cck_check_gpx_source End of "addtogroup hooks".
_gmap_cck_get_gpx process gpx-parameter to show something on the map
_gmap_cck_gpxdata_filefield try to always get filedata from filefield, even if we're in preview mode
_gmap_cck_noderef_markers load locations from nodes pointed at with noderef field, show markers