From d4c27a68d76a5557c5dc29341261a25c9e449747 Mon Sep 17 00:00:00 2001
From: Pol Dell'Aiera <Pol@47194.no-reply.drupal.org>
Date: Fri, 10 Aug 2012 21:08:48 +0200
Subject: [PATCH] Issue #814220: CTools Export UI.

---
 .../includes/openlayers_ui.layers.inc              |  257 +++++---------------
 .../openlayers_ui/includes/openlayers_ui.maps.inc  |  146 +----------
 .../includes/openlayers_ui.styles.inc              |  144 ++++--------
 modules/openlayers_ui/openlayers_ui.module         |  214 +----------------
 openlayers.install                                 |    2 +-
 openlayers.module                                  |    3 +
 .../openlayers_layers_ctools_export_ui.inc         |   57 +++++
 plugins/export_ui/openlayers_layers_ui.class.php   |  100 ++++++++
 .../export_ui/openlayers_maps_ctools_export_ui.inc |   62 +++++
 plugins/export_ui/openlayers_maps_ui.class.php     |  108 ++++++++
 .../openlayers_styles_ctools_export_ui.inc         |   57 +++++
 plugins/export_ui/openlayers_styles_ui.class.php   |  144 +++++++++++
 tests/openlayers.test                              |   51 ++--
 13 files changed, 674 insertions(+), 671 deletions(-)
 create mode 100644 plugins/export_ui/openlayers_layers_ctools_export_ui.inc
 create mode 100644 plugins/export_ui/openlayers_layers_ui.class.php
 create mode 100644 plugins/export_ui/openlayers_maps_ctools_export_ui.inc
 create mode 100644 plugins/export_ui/openlayers_maps_ui.class.php
 create mode 100644 plugins/export_ui/openlayers_styles_ctools_export_ui.inc
 create mode 100644 plugins/export_ui/openlayers_styles_ui.class.php

diff --git a/modules/openlayers_ui/includes/openlayers_ui.layers.inc b/modules/openlayers_ui/includes/openlayers_ui.layers.inc
index 2255bc0..a661510 100644
--- a/modules/openlayers_ui/includes/openlayers_ui.layers.inc
+++ b/modules/openlayers_ui/includes/openlayers_ui.layers.inc
@@ -11,111 +11,9 @@
 /**
  * Layer add/edit form.
  */
-function openlayers_ui_layers_types($form_state, $layer = NULL, $edit = FALSE) {
-  $form = array();
-  $form['layer_types'] = openlayers_ui_get_layer_type_options('map');
-  return $form;
-}
-
-
-/**
- * Layer add/edit form.
- */
 function openlayers_ui_layers_form($form_state, $layer = NULL) {
   $form = array();
 
-  $form['layer'] = openlayers_ui_get_layer_form($layer);
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  return $form;
-}
-
-/**
- * Submit handler for layers.
- */
-function openlayers_ui_layers_form_submit(&$form, &$form_state) {
-  // Save layer
-  $layer_class = $form_state['values']['data']['layer_type'];
-  $layer = openlayers_layer_type_load($layer_class);
-
-  $layer->name = $form_state['values']['name'];
-  $layer->title = $form_state['values']['title'];
-  $layer->description = $form_state['values']['description'];
-  $layer->data = $form_state['values']['data'];
-
-  if (isset($layer->data['resolutions'])) {
-    // resolutions are stored as strings because floats are not handled
-    // well by formapi
-    $layer->data['resolutions'] =
-      array_map("floatval", array_keys($layer->data['resolutions']));
-    rsort($layer->data['resolutions']);
-  }
-
-  $success = $layer->save();
-
-  // Redirect to edit page
-  if ($success) {
-    drupal_set_message(t('Layer saved.'));
-    $form_state['redirect'] = 'admin/structure/openlayers/layers/' . $layer->name . '/edit';
-  }
-  else {
-    form_set_error('openlayers', t('Error trying to save layer.'));
-  }
-}
-
-/**
- * Layer-wide-settings form
- */
-function openlayers_ui_layers_settings($form_state, $layer = NULL) {
-  $form = array();
-  $layer_types = openlayers_layer_types();
-
-  foreach ($layer_types as $name => $type_def) {
-    $type = openlayers_layer_type_load($name);
-    if (method_exists($type, 'settings_form')) {
-      $form[$name] = array(
-        '#type' => 'fieldset',
-        '#tree' => TRUE,
-        '#title' => $type_def['title']);
-      $form[$name]['settings'] = $type->settings_form();
-    }
-  }
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  return $form;
-}
-
-/**
- * Submit handler for openlayers_ui_layers_settings
- */
-function openlayers_ui_layers_settings_submit(&$form, &$form_state) {
-  $layer_types = openlayers_layer_types();
-  foreach ($form_state['values'] as $key => $value) {
-    if (in_array($key, array_keys($layer_types))) {
-      foreach ($value['settings'] as $k => $v) {
-        variable_set($k, $v);
-      }
-    }
-  }
-}
-
-/**
- * Get an actual layer form
- */
-function openlayers_ui_get_layer_form($args, $layer = FALSE) {
-  $form = array();
-
-  // TODO: this shouldn't be necessary. this is ugly code.
-  $type = $args['build_info']['args'][0];
-
-
   $form['basics'] = array(
     '#tree' => FALSE,
     '#type' => 'fieldset',
@@ -123,131 +21,96 @@ function openlayers_ui_get_layer_form($args, $layer = FALSE) {
     '#description' => t('The following settings are for all layers.'),
   );
   $form['basics']['name'] = array(
-    '#type' => 'textfield',
-    '#default_value' => isset($type->name) ? $type->name : '',
-    '#title' => t('Layer Machine Name'),
-    '#description' => t('This is the unique name of the layer. It must contain only alphanumeric characters and underscores.'),
+    '#id' => 'layer_machine_name',
+    '#type' => 'machine_name',
+    '#default_value' => isset($layer->name) ? $layer->name : '',
+    '#maxlength' => 32,
+    '#machine_name' => array(
+      'exists' => 'openlayers_layer_type_load',
+      'source' => array('basics', 'title'),
+    ),
+    '#description' => t('A unique machine-readable name for this block type. It must only contain lowercase letters, numbers, and underscores.'),
+    '#disabled' => !!$layer,
   );
   $form['basics']['title'] = array(
+    '#id' => 'layer_machine_title',
     '#type' => 'textfield',
     '#title' => t('Layer Title'),
-    '#default_value' => isset($type->title) ? $type->title : '',
+    '#default_value' => isset($layer->title) ? $layer->title : '',
     '#description' => t('The friendly name of your layer, which will appear in the administration interface as well on the map interface if it is exposed.'),
   );
   $form['basics']['description'] = array(
     '#type' => 'textfield',
     '#title' => t('Layer Description'),
-    '#default_value' => isset($type->description) ? $type->description : '',
+    '#default_value' => isset($layer->description) ? $layer->description : '',
     '#description' => t('The description of your layer, which will appear in the administration interface.'),
   );
 
-  // @TODO: clarify use of data, and of layer_type in overriding it
-  $form['data'] = array(
-    '#tree' => TRUE,
-    '#type' => 'fieldset',
-    '#title' => t('Layer Specifics'),
-    '#description' => t('The following settings are specific to the layer type.'),
-  );
+  $layers_options = array();
+  $layers = openlayers_layer_types();
+  $options = array('' => t('Select the layer type'));
 
-  // Arrays merged in order so that options_form can override defaults
-  $form['data'] = $type->options_form() + $form['data'];
-
-  return $form;
-}
-
-/**
- * Get layer type options.
- */
-function openlayers_ui_get_layer_type_options($type) {
-  $form = array();
-  $form['layer_type_help'] = array(
-    '#type' => 'item',
-    '#markup' => t('Select a layer type to create a new layer.')
-  );
-
-  foreach (openlayers_layer_types() as $key => $plugin) {
-    // Hack to keep Views layers out
-    if (in_array($key, array('openlayers_views_vector', 'openlayers_layer_type_raw'))) {
+  foreach ($layers as $id => $data) {
+    if (!($obj = openlayers_layer_type_load($data['name']))) {
+      continue;
+    };
+    if (in_array($data['name'], array('openlayers_views_vector', 'openlayers_layer_type_raw'))) {
       continue;
     }
 
-    $class = ctools_plugin_get_class($plugin, 'layer_type');
-    if ($class) {
-      // Build form.
-      $form[$key] = array(
-        '#type' => 'item',
-        '#title' => l($plugin['title'], "admin/structure/openlayers/layers/add/{$key}"),
-        '#description' => $plugin['description'],
+
+    $layers_option = array(
+      '#type' => 'fieldset',
+      '#tree' => TRUE,
+      '#title' => t('Layer specific options for @layer_title', array('@layer_title' => $data['title'])),
+    );
+    if (method_exists($obj, 'options_form')) {
+      $layers_option += $obj->options_form();
+      $layers_option['#states'] = array(
+        'visible' => array(
+          ':input[name="layer_type"]' => array('value' => $id),
+        ),
       );
+      $layers_options[$id] = $layers_option;
     }
+    $options[$id] = $data['title'];
   }
-  return $form;
-}
 
-/**
- * Import a layer from cut & paste
- */
-function openlayers_ui_layers_import_form($form_state) {
-  $form['name'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Layers name'),
-    '#description' => t('Enter the name to use for this layer if it is different from the source layer. Leave blank to use the name of the layer.'),
+  $form['layer_type'] = array(
+    '#type' => 'select',
+    '#title' => t('Layer Type'),
+    '#default_value' => isset($layer->data['layer_type']) ? $layer->data['layer_type']: '',
+    '#description' => t('Select the type of layer.'),
+    '#options' => $options,
   );
 
-  $form['layer'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Paste layer code here'),
-    '#required' => TRUE,
-  );
+  $form += $layers_options;
 
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Import'),
-    '#submit' =>   array('openlayers_ui_layers_import_submit'),
-    '#validate' => array('openlayers_ui_layers_import_validate'),
-  );
   return $form;
 }
 
 /**
- * Validate handler to import a layer
+ * Layer-wide-settings form
  */
-function openlayers_ui_layers_import_validate($form, &$form_state) {
-
-  $items = '';
-  ob_start();
-  eval($form_state['values']['layer']);
-  ob_end_clean();
-
-  if (!is_array($items)) {
-    form_error($form['layer'], t('Unable to interpret layer code.'));
-    return;
-  }
+function openlayers_ui_layers_settings($form_state, $layer = NULL) {
+  $form = array();
+  $layer_types = openlayers_layer_types();
 
-  // View name must be alphanumeric or underscores, no other punctuation.
-  if (!empty($form_state['values']['name']) &&
-    preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
-      form_error($form['name'],
-        t('Layer name must be alphanumeric or underscores only.'));
+  foreach ($layer_types as $name => $type_def) {
+    $type = openlayers_layer_type_load($name);
+    if (method_exists($type, 'settings_form')) {
+      $form[$name] = array(
+        '#type' => 'fieldset',
+        '#tree' => TRUE,
+        '#title' => $type_def['title']);
+      $form[$name]['settings'] = $type->settings_form();
+    }
   }
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+  );
 
-  $layer = current($items);
-  $layer_type_object = openlayers_layer_type_load($layer->data['layer_type']);
-
-  if ($form_state['values']['name']) {
-    $layer->name = $form_state['values']['name'];
-  }
+  return $form;
 
-  if ($exists = openlayers_layer_load($layer->name)) {
-    drupal_set_message(t('An existing layer of this name already exists'), 'error');
-    $form_state['redirect'] = 'admin/structure/openlayers/layers';
-  }
-  else {
-    drupal_set_title(t('Add Layer'));
-    $layer_object = new $layer->data['layer_type']($layer);
-    $output = drupal_get_form('openlayers_ui_layers_form',
-      $layer_object);
-    print theme('page', $output);
-    exit;
-  }
 }
diff --git a/modules/openlayers_ui/includes/openlayers_ui.maps.inc b/modules/openlayers_ui/includes/openlayers_ui.maps.inc
index 5879c1d..a9f4f88 100644
--- a/modules/openlayers_ui/includes/openlayers_ui.maps.inc
+++ b/modules/openlayers_ui/includes/openlayers_ui.maps.inc
@@ -8,16 +8,6 @@
  * @ingroup openlayers
  */
 
-function openlayers_ui_maps_clone($map) {
-  $form_state = array();
-  drupal_set_title(
-    t('Clone map %map',
-      array('%map' => $map->name)
-    ), PASS_THROUGH
-  );
-  return drupal_get_form('openlayers_ui_maps_form', $map);
-}
-
 /**
  * Menu Callback for Add Map
  */
@@ -93,21 +83,20 @@ function openlayers_ui_maps_form($form, &$form_state, $map = FALSE, $edit = FALS
     '#group' => 'ui',
     '#type' => 'fieldset'
   );
-
   $form['info']['name'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Map Machine Name'),
-    '#description' => t('This is the machine readable identifier.
-      This should be all lowercase characters, numbers, or underscores (_).'),
-    '#maxlength' => 255,
-    '#default_value' => !empty($map->name) ? $map->name : '',
-    '#disabled' => $edit,
-    '#required' => TRUE,
+    '#id' => 'map_machine_name',
+    '#type' => 'machine_name',
+    '#default_value' => isset($map->name) ? $map->name : '',
+    '#maxlength' => 32,
+    '#machine_name' => array(
+      'exists' => 'openlayers_map_load',
+      'source' => array('info', 'title'),
+    ),
+    '#description' => t('A unique machine-readable name for this block type. It must only contain lowercase letters, numbers, and underscores.'),
+    '#disabled' => !!$map,
   );
-  if ($edit) {
-    $form['info']['name']['#value'] = $map->name;
-  }
   $form['info']['title'] = array(
+    '#id' => 'map_title',
     '#type' => 'textfield',
     '#title' => t('Map Title'),
     '#description' => t('This is the descriptive title of the map and will show up most often in the interface.'),
@@ -120,7 +109,7 @@ function openlayers_ui_maps_form($form, &$form_state, $map = FALSE, $edit = FALS
     '#title' => t('Map Description'),
     '#description' => t('This is full description of the map and is mostly used on the map overview list page.'),
     '#default_value' => !empty($map->description) ? $map->description : '',
-    '#required' => TRUE,
+    '#required' => FALSE,
   );
   $form['info']['width'] = array(
     '#type' => 'textfield',
@@ -570,83 +559,10 @@ function openlayers_ui_maps_form($form, &$form_state, $map = FALSE, $edit = FALS
     );
   }
 
-  // Buttons
-  $form['buttons'] = array('#tree' => FALSE);
-  $form['buttons']['openlayers_save'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save')
-  );
-  $form['buttons']['openlayers_preview'] = array(
-    '#type' => 'submit',
-    '#value' => t('Preview')
-  );
-  $form['buttons']['openlayers_cancel'] = array(
-    '#type' => 'submit',
-    '#value' => t('Cancel')
-  );
-
   return $form;
 }
 
 /**
- * OpenLayers Map Form Validate
- *
- * Validates a map form submission.
- *
- * @param $map_form
- *   Array of values to validate
- * @return
- *   Does not return anything. Uses form_set_error() to communicate errors.
- */
-function openlayers_ui_maps_form_validate($form, &$form_state) {
-  $values = $form_state['values'];
-  $found_error = FALSE;
-
-  // Check if ahah submitting
-  if ($form_state['clicked_button']['#id'] == 'edit-openlayers-projection-ahah' ||
-      $form_state['clicked_button']['#id'] == 'edit-openlayers-cancel') {
-    return TRUE;
-  }
-
-  // Check map name first
-  if (!preg_match('!^[a-z0-9_]+$!', $values['name'])) {
-    form_set_error('name', t('Map Name must contain only lowercase letters, numbers, and underscores.'));
-    $found_error = TRUE;
-  }
-
-  // Check if adding and name already exists
-  $existing = openlayers_map_load($values['name'], TRUE);
-  if (!empty($existing) && (!$form_state['values']['map_edit']) &&
-      ($existing->export_type & EXPORT_IN_DATABASE)) {
-    form_set_error('name', t('The Map Name already exists.'));
-    $found_error = TRUE;
-  }
-
-  // Attempt to render map to find any errors
-  $map = openlayers_ui_maps_form_process($form_state['values']);
-  $map = openlayers_build_map($map);
-  // Check if any errors found
-  if (is_array($map['errors']) && count($map['errors']) > 0) {
-    foreach ($map['errors'] as $error) {
-      form_set_error('openlayers',
-        t('OpenLayers Map Rendering Error: !error', array('!error' => $error)));
-      $found_error = TRUE;
-    }
-  }
-
-  // If found error, rebuild form
-  if ($found_error) {
-    // Add OpenLayers CSS & Javascript, because the form function will not be run on rebuild
-    openlayers_include();
-    drupal_add_js(drupal_get_path('module', 'openlayers_ui') .
-      '/js/openlayers_ui.maps.js', 'module');
-    drupal_add_css(drupal_get_path('module', 'openlayers_ui') .
-      '/openlayers_ui.css');
-    $form_state['rebuild'] = TRUE;
-  }
-}
-
-/**
  * Form submit for map add form, for the projection add ahah
  */
 function openlayers_ui_maps_add_projection_submit($form, &$form_state) {
@@ -655,44 +571,6 @@ function openlayers_ui_maps_add_projection_submit($form, &$form_state) {
   $form_state['rebuild'] = TRUE;
 }
 
-/**
- * Form submit for map add form
- */
-function openlayers_ui_maps_form_submit($form, &$form_state) {
-  // Check for cancel
-  if ($form_state['clicked_button']['#id'] == 'edit-openlayers-cancel') {
-    $form_state['redirect'] = 'admin/structure/openlayers/maps/list';
-  }
-
-  // On preview, process, but don't save, rebuild.
-  if ($form_state['clicked_button']['#id'] == 'edit-openlayers-preview') {
-    $map_data = openlayers_ui_maps_form_process($form_state['values']);
-    $form_state['values']['preview_map'] = $map_data;
-    $form_state['rebuild'] = TRUE;
-  }
-
-  // Only save if save button is pressed
-  if ($form_state['clicked_button']['#id'] == 'edit-openlayers-save') {
-    $map_data = openlayers_ui_maps_form_process($form_state['values']);
-
-    // Save map
-    $map_object = new stdClass();
-    $map_object->name = $form_state['values']['name'];
-    $map_object->title = $form_state['values']['title'];
-    $map_object->description = $form_state['values']['description'];
-    $map_object->data = $map_data;
-    $success = openlayers_map_save($map_object);
-
-    // Redirect to edit page
-    if ($success) {
-      drupal_set_message(t('Map saved.'));
-      $form_state['redirect'] = "admin/structure/openlayers/maps/{$map_object->name}/edit";
-    }
-    else {
-      form_set_error('openlayers', t('Error trying to save map'));
-    }
-  }
-}
 
 /**
  * OpenLayers AHAH
diff --git a/modules/openlayers_ui/includes/openlayers_ui.styles.inc b/modules/openlayers_ui/includes/openlayers_ui.styles.inc
index 3d727cf..9a38b54 100755
--- a/modules/openlayers_ui/includes/openlayers_ui.styles.inc
+++ b/modules/openlayers_ui/includes/openlayers_ui.styles.inc
@@ -30,7 +30,7 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
         an icon. Note that, when using icons, this value should be half the
         width of the icon image.'),
       'type' => 'integer',
-      'element_validate' => array('element_validate_integer')
+      'element_validate' => array('element_validate_integer_positive'),
     ),
     'fillColor' => array(
       'default' => '#EE9900',
@@ -54,7 +54,7 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
         the feature. On lines, this is the representation of the
         feature.  This is a value in pixels.'),
       'type' => 'integer',
-      'element_validate' => array('element_validate_integer')
+      'element_validate' => array('element_validate_integer_positive'),
     ),
     'fillOpacity' => array(
       'default' => 1,
@@ -64,7 +64,7 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
         externalGraphic is applied to a point.  This should be a value
         between 0 and 1.'),
       'type' => 'float',
-      'element_validate' => array('_element_validate_between_zero_and_one', 'element_validate_number')
+      'element_validate' => array('_element_validate_between_zero_and_one', 'element_validate_number'),
     ),
     'strokeOpacity' => array(
       'default' => 1,
@@ -73,7 +73,7 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
         feature. On lines, this is the representation of the feature.
         This should be a value between 0 and 1.'),
       'type' => 'float',
-      'element_validate' => array('_element_validate_between_zero_and_one', 'element_validate_number')
+      'element_validate' => array('_element_validate_between_zero_and_one', 'element_validate_number'),
     ),
     'strokeLinecap' => array(
       'default' => 'round',
@@ -114,7 +114,7 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
         to be used when your graphic has different sizes in the X and
         Y direction.  This should be in pixels.'),
       'type' => 'integer',
-      'element_validate' => array('element_validate_integer')
+      'element_validate' => array('element_validate_integer_positive'),
     ),
     'graphicHeight' => array(
       'default' => '',
@@ -123,30 +123,30 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
         to be used when your graphic has different sizes in the X and
         Y direction.  This should be in pixels.'),
       'type' => 'integer',
-      'element_validate' => array('element_validate_integer')
+      'element_validate' => array('element_validate_integer_positive'),
     ),
     'graphicOpacity' => array(
       'default' => '1',
       'desc' => t('Opacity of an external graphic.  This should be a
-        value between 0 and 1. Grahics that are already semitransparent,
+        value between 0 and 1. Graphics that are already semitransparent,
         like alpha PNGs, should have this set to 1, or rendering problems in
         Internet Explorer will ensue.'),
       'type' => 'float',
-      'element_validate' => array('_element_validate_between_zero_and_one', 'element_validate_number')
+      'element_validate' => array('_element_validate_between_zero_and_one', 'element_validate_number'),
     ),
     'graphicXOffset' => array(
       'default' => '',
       'desc' => t('Where the X value of the "center" of an
       externalGraphic should be.  This should be in pixels.'),
       'type' => 'integer',
-      'element_validate' => array('element_validate_integer')
+      'element_validate' => array('element_validate_integer'),
     ),
     'graphicYOffset' => array(
       'default' => '',
       'desc' => t('Where the Y value of the "center" of an
       externalGraphic should be.  This should be in pixels.'),
       'type' => 'integer',
-      'element_validate' => array('element_validate_integer')
+      'element_validate' => array('element_validate_integer'),
     ),
     'graphicName' => array(
       'default' => '',
@@ -170,7 +170,7 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
     'label' => array(
       'default' => '',
       'desc' => t('A label to show for features. '
-        . 'Tipically used with ${attribute} syntax.')
+        . 'Typically used with ${attribute} syntax.')
     ),
     'labelAlign' => array(
       'default' => 'cm',
@@ -190,12 +190,14 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
     'labelXOffset' => array(
       'default' => '',
       'desc' => t('Label X offset. Positive numbers move label right.'),
-      'element_validate' => array('element_validate_number')
+      'type' => 'integer',
+      'element_validate' => array('element_validate_integer'),
     ),
     'labelYOffset' => array(
       'default' => '',
       'desc' => t('Label Y offset. Positive numbers move label up.'),
-      'element_validate' => array('element_validate_number')
+      'type' => 'integer',
+      'element_validate' => array('element_validate_integer'),
     ),
     'fontColor' => array(
       'default' => '',
@@ -230,9 +232,16 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
   );
 
   // Provide a preview of the style
-  $preview_style = (isset($form_state['values']) && $form_state['values']['preview_style']) ?
-    $form_state['values']['preview_style'] : $style;
-  if ($preview_style) {
+  if (isset($form_state['clicked_button']['#id']) && ($form_state['clicked_button']['#id'] == 'edit-preview')) {
+    $preview_style = new stdClass();
+    $preview_style->name = preg_replace('/_+/', '_',
+      preg_replace('/[^a-z0-9]/', '_',
+        drupal_strtolower($form_state['input']['title'])
+      ));
+    $preview_style->title = $form_state['input']['title'];
+    $preview_style->description = $form_state['input']['description'];
+    $preview_style->data = $form_state['input']['data'];
+
     $form['info']['preview_style_container'] = array(
       '#type' => 'item',
       '#title' => t('Preview'),
@@ -241,13 +250,6 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
     );
   }
 
-  $form['info']['name'] = array(
-    '#title' => t('Style Machine Name'),
-    '#type' => 'textfield',
-    '#required' => TRUE,
-    '#default_value' => !empty($style->name) ? $style->name : '',
-    '#description' => t('This is the unique name of the style. It must contain only alphanumeric characters and underscores.'),
-  );
   $form['info']['title'] = array(
     '#title' => t('Style Title'),
     '#type' => 'textfield',
@@ -255,6 +257,17 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
     '#default_value' => !empty($style->title) ? $style->title : '',
     '#description' => t('The title of your style, which will appear in the administration interface.'),
   );
+  $form['info']['name'] = array(
+    '#type' => 'machine_name',
+    '#default_value' => isset($style->name) ? $style->name : '',
+    '#maxlength' => 32,
+    '#machine_name' => array(
+      'exists' => 'openlayers_style_load',
+      'source' => array('info', 'title'),
+    ),
+    '#description' => t('A unique machine-readable name for this block type. It must only contain lowercase letters, numbers, and underscores.'),
+    '#disabled' => !!$style,
+  );
   $form['info']['description'] = array(
     '#title' => t(' Style Description'),
     '#type' => 'textfield',
@@ -278,8 +291,9 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
       '#default_value' => isset($style->data[$key]) ?
         $style->data[$key] : $prop['default'],
       '#element_validate' => isset($prop['element_validate']) ?
-        $prop['element_validate'] : array()
-    );
+        $prop['element_validate'] : array(),
+      '#element_validate' => isset($prop['element_validate']) ?
+        $prop['element_validate'] : array()    );
 
     // Add options if needed
     if (isset($prop['options']) && is_array($prop['options'])) {
@@ -287,83 +301,11 @@ function openlayers_ui_styles_form($form, &$form_state, $style = NULL, $edit = F
     }
   }
 
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-  $form['preview'] = array(
-    '#type' => 'submit',
+  // Add preview button.
+  $form['buttons']['preview'] = array(
+    '#type' => 'button',
     '#value' => t('Preview'),
   );
-  return $form;
-}
-
-/**
- * Validation handler for styles.
- */
-function openlayers_ui_styles_form_validate($form, &$form_state) {
-
-}
 
-
-/**
- * Submit handler for styles.
- */
-function openlayers_ui_styles_form_submit(&$form, &$form_state) {
-  $style_data = $form_state['values']['style_data'];
-
-  // Cast and unset values so JS can handle them better,
-  // Unless values are in the form ${attribute}
-  foreach ($form_state['values']['data'] as $key => $value) {
-    if ($form_state['values']['data'][$key] === '') {
-      unset($form_state['values']['data'][$key]);
-    }
-    elseif (isset($style_data[$key]['type']) &&
-      strpos($form_state['values']['data'][$key], '${') !== 0) {
-      if ($style_data[$key]['type'] == 'integer') {
-        $form_state['values']['data'][$key] =
-          (int) $form_state['values']['data'][$key];
-      }
-      elseif ($style_data[$key]['type'] == 'float') {
-        $form_state['values']['data'][$key] =
-          (float) $form_state['values']['data'][$key];
-      }
-    }
-  }
-
-  // Create style object
-  $style = new stdClass();
-  $style->name = $form_state['values']['name'];
-  $style->title = $form_state['values']['title'];
-  $style->description = $form_state['values']['description'];
-  $style->data = $form_state['values']['data'];
-
-  // On preview, process, but don't save, rebuild.
-  if ($form_state['clicked_button']['#id'] == 'edit-preview') {
-    $form_state['values']['preview_style'] = $style;
-    $form_state['rebuild'] = TRUE;
-    return;
-  }
-
-  // Save
-  $success = openlayers_style_save($style);
-
-  // Redirect to edit page
-  if ($success) {
-    drupal_set_message(t('Style saved.'));
-    $form_state['redirect'] = 'admin/structure/openlayers/styles/' . $style->name . '/edit';
-  }
-  else {
-    form_set_error('openlayers', t('Error trying to save style.'));
-  }
-}
-
-/**
- * Custom validation function.
- * See #element_validate in Form API.
- */
-function _element_validate_between_zero_and_one($element, &$form_state, $form) {
-  if ($element['#value'] < 0 || $element['#value'] > 1) {
-    form_error($element, t('The @property property must be between 0 and 1.', array('@property' => $element['#title'])));
-  }
+  return $form;
 }
diff --git a/modules/openlayers_ui/openlayers_ui.module b/modules/openlayers_ui/openlayers_ui.module
index 36ecba2..56cfd6d 100644
--- a/modules/openlayers_ui/openlayers_ui.module
+++ b/modules/openlayers_ui/openlayers_ui.module
@@ -7,7 +7,7 @@
  */
 
 /**
- * Implements hook_init
+ * Implements hook_init().
  */
 function openlayers_ui_init() {
   // For backwards compability for the change from maps to presets
@@ -127,77 +127,6 @@ function openlayers_ui_menu() {
     'weight' => -20,
   );
 
-  // Layers.
-  $items['admin/structure/openlayers/layers'] = array(
-    'title' => 'Layers',
-    'page callback' => 'openlayers_ui_object_list',
-    'page arguments' => array(3),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.layers.inc',
-    'type' => MENU_LOCAL_TASK,
-    'weight' => -10,
-  );
-  $items['admin/structure/openlayers/layers/list'] = array(
-    'title' => 'List',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-    'weight' => -20,
-  );
-  // Layer type drilldown
-  $items['admin/structure/openlayers/layers/add'] = array(
-    'title' => 'Add',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_layers_types'),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.layers.inc',
-    'type' => MENU_LOCAL_TASK,
-    'weight' => -10,
-  );
-  // Actual add-layer form
-  $items['admin/structure/openlayers/layers/add/%openlayers_layer_type'] = array(
-    'title' => 'Add',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_layers_form', 5),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.layers.inc',
-    'type' => MENU_LOCAL_TASK,
-    'weight' => -15,
-  );
-  $items['admin/structure/openlayers/layers/clone/%openlayers_layer'] = array(
-    'title' => 'Clone layer',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_layers_form', 5),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.layers.inc',
-    'type' => MENU_NORMAL_ITEM,
-    'tab_parent' => 'admin/structure/openlayers/layers',
-  );
-  // Edit-layer form
-  $items['admin/structure/openlayers/layers/%openlayers_layer/edit'] = array(
-    'title' => 'Edit layer',
-    'description' => 'Edit OpenLayers layer.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_layers_form', 4),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.layers.inc',
-    'type' => MENU_LOCAL_TASK,
-    'tab_parent' => 'admin/structure/openlayers/layers',
-  );
-  $items['admin/structure/openlayers/layers/%openlayers_layer_export/delete'] = array(
-    'title' => 'Delete layer',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_object_delete', 3, 4),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.layers.inc',
-    'type' => MENU_NORMAL_ITEM
-  );
-  $items['admin/structure/openlayers/layers/%openlayers_layer_export/export'] = array(
-    'title' => 'Export layer',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_object_export', 3, 4),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.layers.inc',
-    'type' => MENU_NORMAL_ITEM
-  );
   // Layer-type-wide settings form
   $items['admin/structure/openlayers/layers/settings'] = array(
     'title' => 'API Keys',
@@ -209,147 +138,6 @@ function openlayers_ui_menu() {
     'weight' => -10,
   );
 
-  // Styles.
-  $items['admin/structure/openlayers/styles'] = array(
-    'title' => 'Styles',
-    'page callback' => 'openlayers_ui_object_list',
-    'page arguments' => array(3),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.styles.inc',
-    'type' => MENU_LOCAL_TASK,
-    'weight' => -10,
-  );
-  $items['admin/structure/openlayers/styles/list'] = array(
-    'title' => 'List',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-    'weight' => -20,
-  );
-  $items['admin/structure/openlayers/styles/add'] = array(
-    'title' => 'Add',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_styles_form'),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.styles.inc',
-    'type' => MENU_LOCAL_TASK,
-    'weight' => -10,
-  );
-  $items['admin/structure/openlayers/styles/add/%openlayers_style'] = array(
-    'title' => 'Add',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_styles_form', 5),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.styles.inc',
-    'type' => MENU_NORMAL_ITEM,
-    'weight' => -10,
-  );
-  $items['admin/structure/openlayers/styles/clone/%openlayers_style'] = array(
-    'title' => 'Clone style',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_styles_form', 5),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.styles.inc',
-    'type' => MENU_LOCAL_TASK,
-  );
-  $items['admin/structure/openlayers/styles/%openlayers_style/edit'] = array(
-    'title' => 'Edit style',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_styles_form', 4, TRUE),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.styles.inc',
-    'type' => MENU_LOCAL_TASK
-  );
-  $items['admin/structure/openlayers/styles/%openlayers_style/delete'] = array(
-    'title' => 'Delete style',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_object_delete', 3, 4),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.styles.inc',
-    'type' => MENU_LOCAL_TASK
-  );
-  $items['admin/structure/openlayers/styles/%openlayers_style/export'] = array(
-    'title' => 'Export style',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_object_export', 3, 4),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.styles.inc',
-    'type' => MENU_LOCAL_TASK
-  );
-
-  // Maps.
-  $items['admin/structure/openlayers/maps'] = array(
-    'title' => 'Maps',
-    'description' => 'Create OpenLayers maps.',
-    'page callback' => 'openlayers_ui_object_list',
-    'page arguments' => array(3),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.maps.inc',
-    'type' => MENU_LOCAL_TASK,
-    'weight' => 10,
-  );
-  $items['admin/structure/openlayers/maps/list'] = array(
-    'title' => 'List',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-    'weight' => -20,
-  );
-  $items['admin/structure/openlayers/maps/add'] = array(
-    'title' => 'Add',
-    'description' => 'Add OpenLayers maps.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_maps_form'),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.maps.inc',
-    'type' => MENU_LOCAL_TASK,
-    'weight' => -10,
-  );
-  $items['admin/structure/openlayers/maps/add/%openlayers_map'] = array(
-    'title' => 'Clone Map',
-    'description' => 'Add OpenLayers maps.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_maps_form', 5),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.maps.inc',
-    'type' => MENU_NORMAL_ITEM,
-    'weight' => -10,
-  );
-  $items['admin/structure/openlayers/maps/clone/%openlayers_map'] = array(
-    'title' => 'Clone Map',
-    'description' => 'Clone OpenLayers maps.',
-    'page callback' => 'openlayers_ui_maps_clone',
-    'page arguments' => array(5),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.maps.inc',
-    'type' => MENU_LOCAL_TASK,
-  );
-  $items['admin/structure/openlayers/maps/%openlayers_map/edit'] = array(
-    'title' => 'Edit map',
-    'description' => 'Edit OpenLayers maps.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_maps_form', 4, TRUE),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.maps.inc',
-    'type' => MENU_LOCAL_TASK,
-    'tab_parent' => 'admin/structure/openlayers/maps',
-  );
-  $items['admin/structure/openlayers/maps/%openlayers_map/delete'] = array(
-    'title' => 'Delete map',
-    'description' => 'Delete OpenLayers map.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_object_delete', 3, 4),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.maps.inc',
-    'type' => MENU_LOCAL_TASK,
-    'tab_parent' => 'admin/structure/openlayers/maps',
-  );
-  $items['admin/structure/openlayers/maps/%openlayers_map/export'] = array(
-    'title' => 'Export map',
-    'description' => 'Export OpenLayers map.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('openlayers_ui_object_export', 3, 4),
-    'access arguments' => array('administer openlayers'),
-    'file' => 'includes/openlayers_ui.maps.inc',
-    'type' => MENU_LOCAL_TASK,
-    'tab_parent' => 'admin/structure/openlayers/maps',
-  );
   $items['admin/structure/openlayers/callbacks/version_check/%'] = array(
     'title' => 'OpenLayers Version Check',
     'page callback' => 'openlayers_ui_version_check',
diff --git a/openlayers.install b/openlayers.install
index ece5fd4..fecff3d 100644
--- a/openlayers.install
+++ b/openlayers.install
@@ -296,4 +296,4 @@ function openlayers_update_7201() {
   // Set default map variable
   variable_set('openlayers_default_map', variable_get('openlayers_default_preset', 'default'));
   variable_del('openlayers_default_preset');
-}
\ No newline at end of file
+}
diff --git a/openlayers.module b/openlayers.module
index 95d5ddf..0672eca 100644
--- a/openlayers.module
+++ b/openlayers.module
@@ -72,6 +72,9 @@ function openlayers_ctools_plugin_directory($module, $plugin) {
   if ($plugin == 'content_types' && !empty($plugin)) {
     return 'includes/' . $plugin;
   }
+  if ($module == 'ctools' && $plugin == 'export_ui') {
+    return 'plugins/' . $plugin;
+  }
   if ($module == 'openlayers' && !empty($plugin)) {
     return 'plugins/' . $plugin;
   }
diff --git a/plugins/export_ui/openlayers_layers_ctools_export_ui.inc b/plugins/export_ui/openlayers_layers_ctools_export_ui.inc
new file mode 100644
index 0000000..335d3c4
--- /dev/null
+++ b/plugins/export_ui/openlayers_layers_ctools_export_ui.inc
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Define this Export UI plugin.
+ */
+$plugin = array(
+  'schema' => 'openlayers_layers',
+  'access' => 'administer openlayers',
+  'menu' => array(
+    'menu prefix' => 'admin/structure/openlayers',
+    'menu item' => 'layers',
+    'menu title' => 'Layers',
+    'menu description' => 'Administer OpenLayers Layers presets.',
+  ),
+
+  'handler' => array(
+    'class' => 'openlayers_layers_ui',
+  ),
+
+  'title singular' => t('layer'),
+  'title plural' => t('layers'),
+  'title singular proper' => t('OpenLayers Layer preset'),
+  'title plural proper' => t('OpenLayers Layers presets'),
+
+  'strings' => array(
+    'confirmation' => array(
+      'add' => array(
+        'success' => t('Layer saved.'),
+      ),
+      'delete' => array(
+        'success' => t('Layer was deleted.'),
+      )
+    )
+  ),
+
+  'form' => array(
+    'settings' => 'openlayers_layers_ctools_export_ui_form_settings',
+    'validate' => 'openlayers_layers_ctools_export_ui_form_validate',
+  ),
+);
+
+function openlayers_layers_ctools_export_ui_form_settings(&$form, &$form_state) {
+  $layer = $form_state['item'];
+  if (empty($layer->name)) {
+    $layer = NULL;
+  }
+  module_load_include('inc', 'openlayers_ui', 'includes/openlayers_ui.layers');
+  $form = openlayers_ui_layers_form($form_state, $layer);
+}
+
+function openlayers_layers_ctools_export_ui_form_validate(&$form, &$form_state) {
+  $values = $form_state['values'];
+  if (empty($values['layer_type'])) {
+    form_set_error('layer_type', t('Layer type must be set.'));
+  }
+}
+
+?>
diff --git a/plugins/export_ui/openlayers_layers_ui.class.php b/plugins/export_ui/openlayers_layers_ui.class.php
new file mode 100644
index 0000000..97fc97a
--- /dev/null
+++ b/plugins/export_ui/openlayers_layers_ui.class.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * @file
+ */
+
+class openlayers_layers_ui extends ctools_export_ui {
+
+  /**
+   * Prepare the tag values before they are added to the database.
+   */
+  function edit_form_submit(&$form, &$form_state) {
+    $form_state['values']['data'] = $form_state['values'][$form_state['values']['layer_type']];
+    parent::edit_form_submit($form, $form_state);
+  }
+
+  /**
+   * hook_menu() entry point.
+   *
+   * Child implementations that need to add or modify menu items should
+   * probably call parent::hook_menu($items) and then modify as needed.
+   */
+  function hook_menu(&$items) {
+    parent::hook_menu($items);
+    $items['admin/structure/openlayers/layers']['type'] = MENU_LOCAL_TASK;
+  }
+
+  /**
+   * Build a row based on the item.
+   *
+   * By default all of the rows are placed into a table by the render
+   * method, so this is building up a row suitable for theme('table').
+   * This doesn't have to be true if you override both.
+   */
+  function list_build_row($item, &$form_state, $operations) {
+    // Set up sorting
+    $name = $item->{$this->plugin['export']['key']};
+    $schema = ctools_export_get_schema($this->plugin['schema']);
+
+    // Note: $item->{$schema['export']['export type string']} should have already been set up by export.inc so
+    // we can use it safely.
+    switch ($form_state['values']['order']) {
+      case 'disabled':
+        $this->sorts[$name] = empty($item->disabled) . $name;
+        break;
+      case 'title':
+        $this->sorts[$name] = $item->{$this->plugin['export']['admin_title']};
+        break;
+      case 'name':
+        $this->sorts[$name] = $name;
+        break;
+      case 'storage':
+        $this->sorts[$name] = $item->{$schema['export']['export type string']} . $name;
+        break;
+    }
+
+    $this->rows[$name]['data'] = array();
+    $this->rows[$name]['class'] = !empty($item->disabled) ? array('ctools-export-ui-disabled') : array('ctools-export-ui-enabled');
+
+    // If we have an admin title, make it the first row.
+    if (!empty($this->plugin['export']['admin_title'])) {
+      $this->rows[$name]['data'][] = array('data' => check_plain($item->{$this->plugin['export']['admin_title']}), 'class' => array('ctools-export-ui-title'));
+    }
+
+    $layers_types = openlayers_layer_types();
+    $this->rows[$name]['data'][] = array('data' => $item->title, 'class' => array('ctools-export-ui-title'));
+    $this->rows[$name]['data'][] = array('data' => $layers_types[$item->data['layer_type']]['title'], 'class' => array('ctools-export-ui-layer-type'));
+    $this->rows[$name]['data'][] = array('data' => $item->description, 'class' => array('ctools-export-ui-description'));
+    $this->rows[$name]['data'][] = array('data' => check_plain($item->{$schema['export']['export type string']}), 'class' => array('ctools-export-ui-storage'));
+
+    $ops = theme('links__ctools_dropbutton', array('links' => $operations, 'attributes' => array('class' => array('links', 'inline'))));
+
+    $this->rows[$name]['data'][] = array('data' => $ops, 'class' => array('ctools-export-ui-operations'));
+
+    // Add an automatic mouseover of the description if one exists.
+    if (!empty($this->plugin['export']['admin_description'])) {
+      $this->rows[$name]['title'] = $item->{$this->plugin['export']['admin_description']};
+    }
+  }
+
+  /**
+   * Provide the table header.
+   *
+   * If you've added columns via list_build_row() but are still using a
+   * table, override this method to set up the table header.
+   */
+  function list_table_header() {
+    $header = array();
+    if (!empty($this->plugin['export']['admin_title'])) {
+      $header[] = array('data' => t('Title'), 'class' => array('ctools-export-ui-title'));
+    }
+
+    $header[] = array('data' => t('Title'), 'class' => array('ctools-export-ui-title'));
+    $header[] = array('data' => t('Type'), 'class' => array('ctools-export-ui-layer-type'));
+    $header[] = array('data' => t('Description'), 'class' => array('ctools-export-ui-description'));
+    $header[] = array('data' => t('Storage'), 'class' => array('ctools-export-ui-storage'));
+    $header[] = array('data' => t('Operations'), 'class' => array('ctools-export-ui-operations'));
+
+    return $header;
+  }
+}
diff --git a/plugins/export_ui/openlayers_maps_ctools_export_ui.inc b/plugins/export_ui/openlayers_maps_ctools_export_ui.inc
new file mode 100644
index 0000000..bb6a823
--- /dev/null
+++ b/plugins/export_ui/openlayers_maps_ctools_export_ui.inc
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Define this Export UI plugin.
+ */
+$plugin = array(
+  'schema' => 'openlayers_maps',
+  'access' => 'administer openlayers',
+  'menu' => array(
+    'menu prefix' => 'admin/structure/openlayers',
+    'menu item' => 'maps',
+    'menu title' => 'Maps',
+    'menu description' => 'Administer OpenLayers Maps presets.',
+  ),
+
+  'handler' => array(
+    'class' => 'openlayers_maps_ui'
+  ),
+
+  'title singular' => t('map'),
+  'title plural' => t('maps'),
+  'title singular proper' => t('Openlayers Map preset'),
+  'title plural proper' => t('Openlayers Maps presets'),
+
+  'strings' => array(
+    'confirmation' => array(
+      'add' => array(
+        'success' => t('Map saved.'),
+      ),
+      'delete' => array(
+        'success' => t('Map was deleted.'),
+      )
+    )
+  ),
+
+  'form' => array(
+    'settings' => 'openlayers_maps_ctools_export_ui_form_settings',
+    'validate' => 'openlayers_maps_ctools_export_ui_form_validate',
+  ),
+);
+
+function openlayers_maps_ctools_export_ui_form_settings(&$form, &$form_state) {
+  $map = $form_state['item'];
+  if (empty($map->name)) {
+    $map = NULL;
+  }
+
+  module_load_include('inc', 'openlayers_ui', 'includes/openlayers_ui.maps');
+
+  if ($form_state['op'] == 'add') {
+    $form = openlayers_ui_maps_form($form, $form_state, $map, FALSE);
+  }
+
+  if ($form_state['op'] == 'edit') {
+    $form = openlayers_ui_maps_form($form, $form_state, $map, TRUE);
+  }
+}
+
+function openlayers_maps_ctools_export_ui_form_validate($form, $form_state) {
+
+}
+
+?>
diff --git a/plugins/export_ui/openlayers_maps_ui.class.php b/plugins/export_ui/openlayers_maps_ui.class.php
new file mode 100644
index 0000000..932c178
--- /dev/null
+++ b/plugins/export_ui/openlayers_maps_ui.class.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * @file
+ */
+
+class openlayers_maps_ui extends ctools_export_ui {
+
+  /**
+   * Prepare the tag values before they are added to the database.
+   */
+  function edit_form_submit(&$form, &$form_state) {
+    $map_data = openlayers_ui_maps_form_process($form_state['values']);
+    $form_state['values']['data'] = $map_data;
+    parent::edit_form_submit($form, $form_state);
+  }
+
+  /**
+   * Build a row based on the item.
+   *
+   * By default all of the rows are placed into a table by the render
+   * method, so this is building up a row suitable for theme('table').
+   * This doesn't have to be true if you override both.
+   */
+  function list_build_row($item, &$form_state, $operations) {
+    // Set up sorting
+    $name = $item->{$this->plugin['export']['key']};
+    $schema = ctools_export_get_schema($this->plugin['schema']);
+
+    // Note: $item->{$schema['export']['export type string']} should have already been set up by export.inc so
+    // we can use it safely.
+    switch ($form_state['values']['order']) {
+      case 'disabled':
+        $this->sorts[$name] = empty($item->disabled) . $name;
+        break;
+      case 'title':
+        $this->sorts[$name] = $item->{$this->plugin['export']['admin_title']};
+        break;
+      case 'name':
+        $this->sorts[$name] = $name;
+        break;
+      case 'storage':
+        $this->sorts[$name] = $item->{$schema['export']['export type string']} . $name;
+        break;
+    }
+
+    $this->rows[$name]['data'] = array();
+    $this->rows[$name]['class'] = !empty($item->disabled) ? array('ctools-export-ui-disabled') : array('ctools-export-ui-enabled');
+
+    // If we have an admin title, make it the first row.
+    if (!empty($this->plugin['export']['admin_title'])) {
+      $this->rows[$name]['data'][] = array('data' => check_plain($item->{$this->plugin['export']['admin_title']}), 'class' => array('ctools-export-ui-title'));
+    }
+
+    $layers = openlayers_layers_load();
+    foreach ($item->data['layers'] as $layer) {
+      $layers_names[] = empty($layers[$layer]->title) ? $layer : filter_xss_admin($layers[$layer]->title);
+    }
+    sort($layers_names);
+    $layers_names = implode(',', $layers_names);
+
+    $this->rows[$name]['data'][] = array('data' => $item->title, 'class' => array('ctools-export-ui-title'));
+    $this->rows[$name]['data'][] = array('data' => $layers_names, 'class' => array('ctools-export-ui-layers'));
+    $this->rows[$name]['data'][] = array('data' => $item->description, 'class' => array('ctools-export-ui-description'));
+    $this->rows[$name]['data'][] = array('data' => check_plain($item->{$schema['export']['export type string']}), 'class' => array('ctools-export-ui-storage'));
+
+    $ops = theme('links__ctools_dropbutton', array('links' => $operations, 'attributes' => array('class' => array('links', 'inline'))));
+
+    $this->rows[$name]['data'][] = array('data' => $ops, 'class' => array('ctools-export-ui-operations'));
+
+    // Add an automatic mouseover of the description if one exists.
+    if (!empty($this->plugin['export']['admin_description'])) {
+      $this->rows[$name]['title'] = $item->{$this->plugin['export']['admin_description']};
+    }
+  }
+
+  /**
+   * Provide the table header.
+   *
+   * If you've added columns via list_build_row() but are still using a
+   * table, override this method to set up the table header.
+   */
+  function list_table_header() {
+    $header = array();
+    if (!empty($this->plugin['export']['admin_title'])) {
+      $header[] = array('data' => t('Title'), 'class' => array('ctools-export-ui-title'));
+    }
+
+    $header[] = array('data' => t('Title'), 'class' => array('ctools-export-ui-title'));
+    $header[] = array('data' => t('Layers'), 'class' => array('ctools-export-ui-layers'));
+    $header[] = array('data' => t('Description'), 'class' => array('ctools-export-ui-description'));
+    $header[] = array('data' => t('Storage'), 'class' => array('ctools-export-ui-storage'));
+    $header[] = array('data' => t('Operations'), 'class' => array('ctools-export-ui-operations'));
+
+    return $header;
+  }
+
+  /**
+   * hook_menu() entry point.
+   *
+   * Child implementations that need to add or modify menu items should
+   * probably call parent::hook_menu($items) and then modify as needed.
+   */
+  function hook_menu(&$items) {
+    parent::hook_menu($items);
+    $items['admin/structure/openlayers/maps']['type'] = MENU_LOCAL_TASK;
+  }
+
+}
diff --git a/plugins/export_ui/openlayers_styles_ctools_export_ui.inc b/plugins/export_ui/openlayers_styles_ctools_export_ui.inc
new file mode 100644
index 0000000..bc3f207
--- /dev/null
+++ b/plugins/export_ui/openlayers_styles_ctools_export_ui.inc
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Define this Export UI plugin.
+ */
+$plugin = array(
+  'schema' => 'openlayers_styles',
+  'access' => 'administer openlayers',
+  'menu' => array(
+    'menu prefix' => 'admin/structure/openlayers',
+    'menu item' => 'styles',
+    'menu title' => 'Styles',
+    'menu description' => 'Administer OpenLayers Styles presets.',
+  ),
+
+  'handler' => array(
+    'class' => 'openlayers_styles_ui',
+  ),
+
+  'title singular' => t('style'),
+  'title plural' => t('styles'),
+  'title singular proper' => t('OpenLayers Style preset'),
+  'title plural proper' => t('OpenLayers Styles presets'),
+
+  'strings' => array(
+    'confirmation' => array(
+      'add' => array(
+        'success' => t('Style saved.'),
+      ),
+      'delete' => array(
+        'success' => t('Style was deleted.'),
+      )
+    )
+  ),
+
+  'form' => array(
+    'settings' => 'openlayers_styles_ctools_export_ui_form_settings',
+  ),
+);
+
+function openlayers_styles_ctools_export_ui_form_settings(&$form, &$form_state) {
+  $style = $form_state['item'];
+  if (empty($style->name)) {
+    $style = NULL;
+  }
+
+  module_load_include('inc', 'openlayers_ui', 'includes/openlayers_ui.styles');
+
+  if ($form_state['op'] == 'add') {
+    $form = openlayers_ui_styles_form($form, $form_state, $style, FALSE);
+  }
+
+  if ($form_state['op'] == 'edit') {
+    $form = openlayers_ui_styles_form($form, $form_state, $style, TRUE);
+  }
+}
+
+?>
diff --git a/plugins/export_ui/openlayers_styles_ui.class.php b/plugins/export_ui/openlayers_styles_ui.class.php
new file mode 100644
index 0000000..09ff05d
--- /dev/null
+++ b/plugins/export_ui/openlayers_styles_ui.class.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * @file
+ */
+
+class openlayers_styles_ui extends ctools_export_ui {
+
+  /**
+   * Prepare the tag values before they are added to the database.
+   */
+  function edit_form_submit(&$form, &$form_state) {
+    $style_data = $form_state['values']['style_data'];
+
+    // Cast and unset values so JS can handle them better,
+    // Unless values are in the form ${attribute}
+    foreach ($form_state['values']['data'] as $key => $value) {
+      if ($form_state['values']['data'][$key] === '') {
+        unset($form_state['values']['data'][$key]);
+      }
+      elseif (isset($style_data[$key]['type']) &&
+        strpos($form_state['values']['data'][$key], '${') !== 0) {
+        if ($style_data[$key]['type'] == 'integer') {
+          $form_state['values']['data'][$key] =
+            (int) $form_state['values']['data'][$key];
+        }
+        elseif ($style_data[$key]['type'] == 'float') {
+          $form_state['values']['data'][$key] =
+            (float) $form_state['values']['data'][$key];
+        }
+      }
+    }
+
+    parent::edit_form_submit($form, $form_state);
+  }
+
+  /**
+   * Build a row based on the item.
+   *
+   * By default all of the rows are placed into a table by the render
+   * method, so this is building up a row suitable for theme('table').
+   * This doesn't have to be true if you override both.
+   */
+  function list_build_row($item, &$form_state, $operations) {
+    // Set up sorting
+    $name = $item->{$this->plugin['export']['key']};
+    $schema = ctools_export_get_schema($this->plugin['schema']);
+
+    // Note: $item->{$schema['export']['export type string']} should have already been set up by export.inc so
+    // we can use it safely.
+    switch ($form_state['values']['order']) {
+      case 'disabled':
+        $this->sorts[$name] = empty($item->disabled) . $name;
+        break;
+      case 'title':
+        $this->sorts[$name] = $item->{$this->plugin['export']['admin_title']};
+        break;
+      case 'name':
+        $this->sorts[$name] = $name;
+        break;
+      case 'storage':
+        $this->sorts[$name] = $item->{$schema['export']['export type string']} . $name;
+        break;
+    }
+
+    $this->rows[$name]['data'] = array();
+    $this->rows[$name]['class'] = !empty($item->disabled) ? array('ctools-export-ui-disabled') : array('ctools-export-ui-enabled');
+
+    // If we have an admin title, make it the first row.
+    if (!empty($this->plugin['export']['admin_title'])) {
+      $this->rows[$name]['data'][] = array('data' => check_plain($item->{$this->plugin['export']['admin_title']}), 'class' => array('ctools-export-ui-title'));
+    }
+
+    // Add the icon as an image in the row if it exists.
+    if (isset($item->data['externalGraphic'])) {
+      $style = theme('image', array('path' => $item->data['externalGraphic']));
+    }
+    else {
+      $style = openlayers_ui_style_preview($item);
+    }
+
+    $this->rows[$name]['data'][] = array('data' => $style, 'class' => array('ctools-export-ui-preview'));
+    $this->rows[$name]['data'][] = array('data' => $item->title, 'class' => array('ctools-export-ui-title'));
+    $this->rows[$name]['data'][] = array('data' => $item->description, 'class' => array('ctools-export-ui-description'));
+    $this->rows[$name]['data'][] = array('data' => check_plain($item->{$schema['export']['export type string']}), 'class' => array('ctools-export-ui-storage'));
+
+    $ops = theme('links__ctools_dropbutton', array('links' => $operations, 'attributes' => array('class' => array('links', 'inline'))));
+
+    $this->rows[$name]['data'][] = array('data' => $ops, 'class' => array('ctools-export-ui-operations'));
+
+    // Add an automatic mouseover of the description if one exists.
+    if (!empty($this->plugin['export']['admin_description'])) {
+      $this->rows[$name]['title'] = $item->{$this->plugin['export']['admin_description']};
+    }
+  }
+
+
+  /**
+   * Provide the table header.
+   *
+   * If you've added columns via list_build_row() but are still using a
+   * table, override this method to set up the table header.
+   */
+  function list_table_header() {
+    $header = array();
+    if (!empty($this->plugin['export']['admin_title'])) {
+      $header[] = array('data' => t('Title'), 'class' => array('ctools-export-ui-title'));
+    }
+
+    $header[] = array('data' => t('Preview'), 'class' => array('ctools-export-ui-preview'));
+    $header[] = array('data' => t('Title'), 'class' => array('ctools-export-ui-title'));
+    $header[] = array('data' => t('Description'), 'class' => array('ctools-export-ui-description'));
+    $header[] = array('data' => t('Storage'), 'class' => array('ctools-export-ui-storage'));
+    $header[] = array('data' => t('Operations'), 'class' => array('ctools-export-ui-operations'));
+
+    return $header;
+  }
+
+  /**
+   * hook_menu() entry point.
+   *
+   * Child implementations that need to add or modify menu items should
+   * probably call parent::hook_menu($items) and then modify as needed.
+   */
+  function hook_menu(&$items) {
+    parent::hook_menu($items);
+    $items['admin/structure/openlayers/styles']['type'] = MENU_LOCAL_TASK;
+  }
+}
+
+/**
+ * Custom validations functions.
+ * See #element_validate in Form API.
+ */
+function _element_validate_between_zero_and_one($element, &$form_state, $form) {
+  if ($element['#value'] < 0 || $element['#value'] > 1) {
+    form_error($element, t('The @property property must be between 0 and 1.', array('@property' => $element['#title'])));
+  }
+}
+
+function _element_validate_number_positive($element, &$form_state, $form) {
+  if ($element['#value'] <= 0) {
+    form_error($element, t('The @property property must be a positive number.', array('@property' => $element['#title'])));
+  }
+}
diff --git a/tests/openlayers.test b/tests/openlayers.test
index bf1eaa1..8240351 100644
--- a/tests/openlayers.test
+++ b/tests/openlayers.test
@@ -162,9 +162,9 @@ class OpenLayersMaps extends DrupalWebTestCase {
     $this->assertText(t('Map saved.'), t('The map was successfully saved.'));
 
     // Remove Map
-    $this->drupalPost('admin/structure/openlayers/maps/' . $map_settings['name'] . '/delete',
-      array(), t('Confirm'));
-    $this->assertText($map_settings['name'] .' was deleted.', t('Map removed succesfully.'));
+    $this->drupalPost('admin/structure/openlayers/maps/list/' . $map_settings['name'] . '/delete',
+      array(), t('Delete'));
+    $this->assertText('Map was deleted.', t('Map removed succesfully.'));
   }
 }
 
@@ -191,13 +191,13 @@ class OpenLayersStyles extends DrupalWebTestCase {
 
     // New style.
     $new_style = array(
-      'name' => 'UnitStyle',
+      'name' => 'unitstyle',
       'title' => 'Unit Style',
       'description' => 'Unit Style for Drupal',
     );
 
     // Check basic navigation.
-    $this->drupalGet('admin/structure/openlayers/styles/default/export');
+    $this->drupalGet('admin/structure/openlayers/styles/list/default/export');
     $this->assertResponse(200, t('User can reach default style export page.'));
     $this->drupalGet('admin/structure/openlayers/styles/add');
     $this->assertResponse(200, t('User can reach style add page.'));
@@ -209,13 +209,13 @@ class OpenLayersStyles extends DrupalWebTestCase {
 
     // Test that the style shows up in the style list.
     $this->drupalGet('admin/structure/openlayers/styles');
-    $this->assertRaw(t('UnitStyle'),
+    $this->assertRaw(t('Unit Style'),
       t('A new style has been added by the test'));
 
     // Test removing style.
-    $this->drupalPost('admin/structure/openlayers/styles/UnitStyle/delete',
-      array(), t('Confirm'));
-    $this->assertText($new_style['name'] .' was deleted.',
+    $this->drupalPost('admin/structure/openlayers/styles/list/unitstyle/delete',
+      array(), t('Delete'));
+    $this->assertText('Style was deleted.',
       t('Style removed succesfully.'));
   }
 }
@@ -244,42 +244,42 @@ class OpenLayersLayers extends DrupalWebTestCase {
     // New layers to add
     $layer_inputs = array(
       'openlayers_layer_type_osm' => array(
-        'name' => 'UnitLayerOSM',
+        'name' => 'unitlayerosm',
         'title' => 'Unit Layer',
         'description' => 'Unit Layer for Drupal',
       ),
       'openlayers_layer_type_xyz' => array(
-        'name' => 'UnitLayerXYZ',
+        'name' => 'unitlayerxyz',
         'title' => 'Unit Layer',
         'description' => 'Unit Layer for Drupal',
       ),
       'openlayers_layer_type_kml' => array(
-        'name' => 'UnitLayerKML',
+        'name' => 'unitlayerkml',
         'title' => 'Unit Layer',
         'description' => 'Unit Layer for Drupal',
       ),
       'openlayers_layer_type_wms' => array(
-        'name' => 'UnitLayerWMS',
+        'name' => 'unitlayerwms',
         'title' => 'Unit Layer',
         'description' => 'Unit Layer for Drupal',
       ),
       'openlayers_layer_type_cloudmade' => array(
-        'name' => 'UnitLayerCloudMade',
+        'name' => 'unitlayercloudmade',
         'title' => 'Unit Layer',
         'description' => 'Unit Layer for Drupal',
       ),
       'openlayers_layer_type_yahoo' => array(
-        'name' => 'UnitLayerYahoo',
+        'name' => 'unitlayeryahoo',
         'title' => 'Unit Layer',
         'description' => 'Unit Layer for Drupal',
       ),
       'openlayers_layer_type_virtualearth' => array(
-        'name' => 'UnitLayerVirtualEarth',
+        'name' => 'unitlayervirtualearth',
         'title' => 'Unit Layer',
         'description' => 'Unit Layer for Drupal',
       ),
       'openlayers_layer_type_google' => array(
-        'name' => 'UnitLayerGoogle',
+        'name' => 'unitlayergoogle',
         'title' => 'Unit Layer',
         'description' => 'Unit Layer for Drupal',
       )
@@ -292,21 +292,22 @@ class OpenLayersLayers extends DrupalWebTestCase {
     // Go through each new layer for testing.
     foreach ($layer_inputs as $layer_type => $input_data) {
       // Test saving new layer.
-      $this->drupalPost('admin/structure/openlayers/layers/add/' . $layer_type,
+      $input_data['layer_type'] = $layer_type;
+      $this->drupalPost('admin/structure/openlayers/layers/add',
         $input_data, t('Save'));
       $this->assertText(t('Layer saved.'),
         t('The layer was successfully saved.'));
 
       // Ensure that the layer shows up in the list.
       $this->drupalGet('admin/structure/openlayers/layers');
-      $this->assertRaw($input_data['name'],
+      $this->assertRaw($input_data['title'],
         t('A new layer of layer type %type_name has been added by the test',
           array('%type_name' => $layer_type)));
 
       // Test removing layer.
-      $this->drupalPost('admin/structure/openlayers/layers/' .
-        $input_data['name'] . '/delete', array(), t('Confirm'));
-      $this->assertText($input_data['name'] . ' was deleted.',
+      $this->drupalPost('admin/structure/openlayers/layers/list/' .
+        $input_data['name'] . '/delete', array(), t('Delete'));
+      $this->assertText('Layer was deleted.',
         t('Layer removed succesfully.'));
     }
   }
@@ -364,12 +365,12 @@ class OpenLayersUnitTests extends DrupalUnitTestCase {
   function testOpenLayersStylePath() {
     module_load_include('inc', 'openlayers', 'includes/openlayers.render');
     global $base_url;
-    
+
     // Full URL
     $path = 'http://example.org/sites/all/modules/openlayers/example/${path}.jpg';
     $styled_path = openlayers_style_path($path);
     $this->assertTrue($styled_path === $path, 'OpenLayers Style Path function with full URL.');
-    
+
     // Just replacement value
     $path = '${path}';
     $styled_path = openlayers_style_path($path);
@@ -380,4 +381,4 @@ class OpenLayersUnitTests extends DrupalUnitTestCase {
     $styled_path = openlayers_style_path($path);
     $this->assertTrue($styled_path === $base_url . '/' . $path, 'OpenLayers Style Path function with relative URL.');
   }
-}
\ No newline at end of file
+}
-- 
1.7.3.4

