From 07d9ffc0f093b2d5f6536897d755640b7747f72e Mon Sep 17 00:00:00 2001
From: Kyle Browning <kylebrowning@me.com>
Date: Thu, 17 Mar 2011 09:28:16 -0700
Subject: [PATCH 1/2] Fixes ctools update

---
 .../export_ui/services_ctools_export_ui.class.php  |  359 ++++++++++++++++++++
 plugins/export_ui/services_ctools_export_ui.inc    |  172 ++++++++++
 services.install                                   |    1 +
 services.module                                    |  108 ++-----
 4 files changed, 560 insertions(+), 80 deletions(-)
 create mode 100644 plugins/export_ui/services_ctools_export_ui.class.php
 create mode 100644 plugins/export_ui/services_ctools_export_ui.inc

diff --git a/plugins/export_ui/services_ctools_export_ui.class.php b/plugins/export_ui/services_ctools_export_ui.class.php
new file mode 100644
index 0000000..2ebf671
--- /dev/null
+++ b/plugins/export_ui/services_ctools_export_ui.class.php
@@ -0,0 +1,359 @@
+<?php
+
+/**
+ * @file
+ * Export-ui handler for the Services module.
+ */
+
+class services_ctools_export_ui extends ctools_export_ui {
+
+  /**
+   * Page callback for the resources page.
+   */
+  function resources_page($js, $input, $item) {
+    drupal_set_title($this->get_page_title('resources', $item));
+    return services_edit_endpoint_resources($item);
+  }
+
+  /**
+   * Page callback for the authentication page.
+   */
+  function authentication_page($js, $input, $item) {
+    drupal_set_title($this->get_page_title('authentication', $item));
+    return drupal_get_form('services_edit_form_endpoint_authentication', $item);
+  }
+}
+
+/**
+ * Endpoint authentication configuration form.
+ */
+function services_edit_form_endpoint_authentication($form, &$form_state) {
+  list($endpoint) = $form_state['build_info']['args'];
+  // Loading runtime include as needed by services_authentication_info().
+  module_load_include('runtime.inc', 'services');
+
+  $auth_modules = module_implements('services_authentication_info');
+
+  $form['endpoint_object'] = array(
+    '#type'  => 'value',
+    '#value' => $endpoint,
+  );
+
+  if (empty($auth_modules)) {
+    $form['message'] = array(
+      '#type'          => 'item',
+      '#title'         => t('No installed authentication modules'),
+      '#description'   => t('No authentication modules are installed, standard ' .
+        'Drupal session based security will be used.'),
+    );
+  }
+  elseif (empty($endpoint->authentication)) {
+    $form['message'] = array(
+      '#type'          => 'item',
+      '#title'         => t('No enabled authentication modules'),
+      '#value'   => t('No authentication modules are enabled, standard ' .
+        'Drupal session based security will be used.'),
+    );
+  }
+  else {
+    // Add configuration fieldsets for the authentication modules
+    foreach ($endpoint->authentication as $module => $settings) {
+      $info = services_authentication_info($module);
+      if ($info) {
+        $form[$module] = array(
+          '#type' => 'fieldset',
+          '#title' => $info['title'],
+          '#tree' => TRUE,
+        ) + services_auth_invoke($module, 'security_settings', $settings);
+      }
+    }
+  }
+
+  $form['submit'] = array(
+    '#type'  => 'submit',
+    '#value' => 'Save',
+  );
+
+  return $form;
+}
+
+function services_edit_form_endpoint_authentication_submit($form, $form_state) {
+  $endpoint = $form_state['values']['endpoint_object'];
+
+  foreach (array_keys($endpoint->authentication) as $module) {
+    $endpoint->authentication[$module] = $form_state['values'][$module];
+  }
+
+  drupal_set_message(t('Your authentication options have been saved.'));
+  services_endpoint_save($endpoint);
+}
+
+
+/**
+ * Add resources to an endpoint.
+ */
+function services_edit_endpoint_resources($endpoint) {
+  if (!is_object($endpoint)) {
+    $endpoint = services_endpoint_load($endpoint);
+  }
+  if ($endpoint && !empty($endpoint->title)) {
+    drupal_set_title(check_plain($endpoint->title));
+  }
+  return drupal_get_form('services_edit_form_endpoint_resources', $endpoint);
+}
+
+/**
+ * Form to add resources to an endpoint.
+ */
+function services_edit_form_endpoint_resources(&$form_state, $endpoint) {
+  module_load_include('resource_build.inc', 'services');
+
+  $form = array();
+
+  $form['endpoint_object'] = array(
+    '#type'  => 'value',
+    '#value' => $endpoint,
+  );
+
+  $ops = array(
+    'create'   => t('Create'),
+    'retrieve' => t('Retrieve'),
+    'update'   => t('Update'),
+    'delete'   => t('Delete'),
+    'index'    => t('Index'),
+  );
+
+  // Call _services_build_resources() directly instead of
+  // services_get_resources to bypass caching.
+  $resources = _services_build_resources();
+  // Apply the endpoint in a non-strict mode, so that the non-active resources
+  // are preserved.
+  _services_apply_endpoint($resources, $endpoint, FALSE);
+
+  $res = array(
+    '#tree' => TRUE,
+  );
+
+  foreach ($resources as $name => $resource) {
+    $rc = $resource['endpoint'];
+    $res_set = array(
+      '#type'        => 'fieldset',
+      '#title'       => t('!name resource', array(
+        '!name' => preg_replace('/[_-]+/', ' ', $name),
+      )),
+      '#collapsible' => TRUE,
+      '#collapsed'   => TRUE,
+      '#tree'        => TRUE,
+      '#attributes'  => array(
+        'class' => array('resource'),
+      ),
+    );
+
+    $res_set['alias'] = array(
+      '#type'          => 'textfield',
+      '#title'         => t('Alias'),
+      '#description'   => t('The alias you enter here will be used instead of the resource name.'),
+      '#size'          => 40,
+      '#maxlength'     => 255,
+      '#default_value' => isset($rc['alias']) ? $rc['alias'] : '',
+    );
+
+    $res_set['operations'] = array(
+      '#tree' => TRUE,
+    );
+    foreach ($ops as $op => $title) {
+      if (isset($resource[$op])) {
+        $res_set['operations'][$op] = array(
+          '#type'        => 'fieldset',
+          '#title'       => $title,
+          '#collapsible' => TRUE,
+          '#collapsed'   => FALSE,
+        );
+        _services_resource_operation_settings($res_set['operations'][$op], $endpoint, $resource, $op);
+      }
+    }
+
+    $classes = array(
+      'actions'          => 'actions',
+      'targeted_actions' => 'targeted actions',
+      'relationships'    => 'relationships',
+    );
+    foreach ($classes as $element => $class) {
+      if (!empty($resource[$class])) {
+        $res_set[$element] = array(
+          '#type'  => 'fieldset',
+          '#title' => t($class),
+          '#tree'  => TRUE,
+        );
+        foreach ($resource[$class] as $action => $definition) {
+          $res_set[$element][$action] = array(
+            '#type'        => 'fieldset',
+            '#title'       => $action,
+            '#collapsible' => TRUE,
+            '#collapsed'   => FALSE,
+          );
+          _services_resource_operation_settings($res_set[$element][$action], $endpoint, $resource, $class, $action);
+        }
+      }
+    }
+
+    drupal_alter('services_resource_settings', $res_set, $resource);
+
+    $res[$name] = $res_set;
+  }
+
+  $form['resources'] = $res;
+
+  $form['save'] = array(
+    '#type'  => 'submit',
+    '#value' => t('Save'),
+  );
+  return $form;
+}
+
+function services_edit_form_endpoint_resources_validate($form, $form_state) {
+  $res = $form_state['values']['resources'];
+
+  // Validate aliases
+  foreach ($res as $name => $resource) {
+    if (!empty($resource['alias'])) {
+      if (!preg_match('/^[a-z-]+$/', $resource['alias'])) {
+        form_set_error("resources][{$name}][alias", t("The alias for the !name may only contain lower case a-z and dashes.", array(
+          '!name' => $form['resources'][$name]['#title'],
+        )));
+      }
+    }
+  }
+}
+
+function services_edit_form_endpoint_resources_submit($form, $form_state) {
+  $resources = $form_state['values']['resources'];
+  $endpoint  = $form_state['values']['endpoint_object'];
+
+  foreach ($resources as $name => $resource) {
+    $used = FALSE;
+    $c = isset($endpoint->resources[$name]) ? $endpoint->resources[$name] : array();
+
+    $c['alias'] = $resource['alias'];
+    if (isset($resource['operations'])) {
+      foreach ($resource['operations'] as $op => $def) {
+        $cop = isset($c['operations'][$op]) ? $c['operations'][$op] : array();
+        $cop = array_merge($cop, $def);
+        if ($cop['enabled']) {
+          $c['operations'][$op] = $cop;
+          $used = $used || TRUE;
+        }
+        else {
+          unset($c['operations'][$op]);
+        }
+      }
+    }
+
+    $classes = array(
+      'actions' => 'actions',
+      'targeted_actions' => 'targeted actions',
+      'relationships' => 'relationships',
+    );
+    foreach ($classes as $element => $class) {
+      $class_used = FALSE;
+      if (!empty($resource[$element])) {
+        foreach ($resource[$element] as $act => $def) {
+          $cop = isset($c[$class][$act]) ? $c[$class][$act] : array();
+          $cop = array_merge($cop, $def);
+          if ($cop['enabled']) {
+            $c[$class][$act] = $cop;
+            $class_used = $class_used || TRUE;
+          }
+          else {
+            unset($c[$class][$act]);
+          }
+        }
+        if (!$class_used) {
+          unset($c[$class]);
+        }
+        $used = $class_used || $used;
+      }
+    }
+
+    if ($used) {
+      $endpoint->resources[$name] = $c;
+    }
+    else {
+      unset($endpoint->resources[$name]);
+    }
+  }
+
+  drupal_set_message(t('Your resources have been saved.'));
+  services_endpoint_save($endpoint);
+}
+
+/**
+ * Returns information about a resource operation given it's class and name.
+ *
+ * @return array
+ *  Information about the operation, or NULL if no matching
+ *  operation was found.
+ */
+function services_get_resource_operation_info($resource, $class, $name = NULL) {
+  $op = NULL;
+
+  if (isset($resource[$class])) {
+    $op = $resource[$class];
+    if (!empty($name)) {
+      $op = isset($op[$name]) ? $op[$name] : NULL;
+    }
+  }
+
+  return $op;
+}
+/**
+ * Constructs the settings form for resource operation.
+ *
+ * @param array $settings
+ *  The root element for the settings form.
+ * @param string $resource
+ *  The resource information array.
+ * @param string $class
+ *  The class of the operation. Can be 'create', 'retrieve', 'update',
+ *  'delete', 'index', 'actions' or 'targeted actions' or 'relationships'.
+ * @param string $name
+ *  Optional. The name parameter is only used for actions, targeted actions
+ *  and relationship.
+ */
+function _services_resource_operation_settings(&$settings, $endpoint, $resource, $class, $name = NULL) {
+  module_load_include('runtime.inc', 'services');
+  if ($rop = services_get_resource_operation_info($resource, $class, $name)) {
+    $settings['enabled'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Enabled'),
+      '#default_value' => !empty($rop['endpoint']) && $rop['endpoint']['enabled'],
+    );
+
+    if (!empty($rop['endpoint']['preprocess'])) {
+      $settings['preprocess'] = array(
+        '#type' => 'item',
+        '#title' => t('Preprocess function'),
+        '#value' => $rop['endpoint']['preprocess'],
+      );
+    }
+
+    if (!empty($rop['endpoint']['postprocess'])) {
+      $settings['preprocess'] = array(
+        '#type' => 'item',
+        '#title' => t('Postprocess function'),
+        '#value' => $rop['endpoint']['Postprocess'],
+      );
+    }
+
+    // Let authentication modules add their configuration options
+    if(isset($endpoint->authentication))
+    foreach ($endpoint->authentication as $auth_module => $auth_settings) {
+      $settings_form = services_auth_invoke($auth_module, 'controller_settings', $auth_settings, $rop, $endpoint, $class, $name);
+      if (!empty($settings_form)) {
+        $settings[$auth_module] = $settings_form;
+      }
+    }
+
+    drupal_alter('services_resource_operation_settings', $settings, $endpoint, $resource, $class, $name);
+  }
+}
diff --git a/plugins/export_ui/services_ctools_export_ui.inc b/plugins/export_ui/services_ctools_export_ui.inc
new file mode 100644
index 0000000..3d0c894
--- /dev/null
+++ b/plugins/export_ui/services_ctools_export_ui.inc
@@ -0,0 +1,172 @@
+<?php
+
+$plugin = array(
+  'schema' => 'services_endpoint',
+  'menu' => array(
+    'menu item' => 'services',
+    // Add services specific own menu callbacks.
+    'items' => array(
+      'resources' => array(
+        'path' => 'list/%ctools_export_ui/resources',
+        'title' => 'Resources',
+        'page callback' => 'ctools_export_ui_switcher_page',
+        'page arguments' => array('services_ctools_export_ui', 'resources', 4),
+        'load arguments' => array('services_ctools_export_ui'),
+        'access arguments' => array('administer services'),
+        'type' => MENU_LOCAL_TASK,
+      ),
+      'authentication' => array(
+        'path' => 'list/%ctools_export_ui/authentication',
+        'title' => 'Resources',
+        'page callback' => 'ctools_export_ui_switcher_page',
+        'page arguments' => array('services_ctools_export_ui', 'authentication', 4),
+        'load arguments' => array('services_ctools_export_ui'),
+        'access arguments' => array('administer services'),
+        'type' => MENU_LOCAL_TASK,
+      ),
+    ),
+  ),
+  // Add our custom operations.
+  'allowed operations' => array(
+    'resources'  => array('title' => t('Resources')),
+    'authentication' => array('title' => t('Authentication')),
+  ),
+
+  'handler' => array(
+    'class' => 'services_ctools_export_ui',
+    'parent' => 'ctools_export_ui',
+  ),
+
+  'title' => t('Services'),
+
+  'title singular' => t('service'),
+  'title plural' => t('services'),
+  'title singular proper' => t('Service'),
+  'title plural proper' => t('Services'),
+);
+
+/**
+ * Form to edit the settings of an endpoint.
+ */
+function services_ctools_export_ui_form(&$form, &$form_state) {
+  // Loading runtime include as needed by services_auth_info().
+  module_load_include('runtime.inc', 'services');
+
+  $endpoint = $form_state['item'];
+  // Set dfefault values.
+
+  $form['eid'] = array(
+    '#type'  => 'value',
+    '#value' => isset($endpoint->eid) ? $endpoint->eid : '',
+  );
+
+  $form['title'] = array(
+    '#type'          => 'textfield',
+    '#size'          => 24,
+    '#maxlength'     => 255,
+    '#default_value' => $endpoint->title,
+    '#title'         => t('Endpoint title'),
+    '#required'      => TRUE,
+  );
+
+  $servers = services_get_servers();
+  $server_opts = array(
+    '' => t('-- Select a server'),
+  );
+  foreach ($servers as $server => $info) {
+    $server_opts[$server] = $info['name'];
+  }
+  $form['server'] = array(
+    '#type'          => 'select',
+    '#options'       => $server_opts,
+    '#default_value' => $endpoint->server,
+    '#title'         => t('Server'),
+    '#description'   => t('Select a the server that should be used to handle requests to this endpoint.'),
+    '#required'      => TRUE,
+  );
+
+  $form['path'] = array(
+    '#type'          => 'textfield',
+    '#size'          => 24,
+    '#maxlength'     => 255,
+    '#default_value' => $endpoint->path,
+    '#title'         => t('Path to endpoint'),
+    '#required'      => TRUE,
+  );
+
+  $auth_modules = module_implements('services_authentication_info');
+
+  if (!empty($auth_modules)) {
+    $auth_options = array();
+    foreach ($auth_modules as $module) {
+      $info = services_authentication_info($module);
+      $auth_options[$module] = $info['title'];
+    }
+
+    $form['authentication'] = array(
+      '#type'          => 'checkboxes',
+      '#options'       => $auth_options,
+      '#default_value' => array_keys($endpoint->authentication),
+      '#title'         => t('Authentication'),
+      '#description'   => t('Choose which authentication schemes that should  be used with your endpoint. If no authentication method is selected  the standard Drupal session security is used.'),
+    );
+  }
+  else {
+    $form['authentication'] = array(
+      '#type'          => 'item',
+      '#title'         => t('Authentication'),
+      '#description'   => t('No d modules are installed, standard Drupal session based security will be used.'),
+    );
+  }
+
+  $form['services_use_content_permissions'] = array(
+    '#type'           => 'checkbox',
+    '#title'          => t('Apply content permissions'),
+    '#default_value'  => variable_get('services_use_content_permissions', TRUE),
+    '#description'    => t('CCK includes the optional Content Permissions module, which allows administrators to restrict access to content at the field level. By default, node services do NOT apply these permissions, causing all fields to be returned in all cases. Checking this box causes content permissions to be applied to node services.'),
+  );
+}
+
+/**
+ * Validate submission of the preset edit form.
+ */
+function services_ctools_export_ui_form_validate(&$form, &$form_state) {
+  // Test uniqueness of name:
+  if (preg_match("/[^a-z_]/", $form_state['values']['name'])) {
+    form_error($form['name'], t('Endpoint name may only contain lowercase alpha characters and underscores.'));
+  }
+  else {
+    $query = "SELECT eid FROM {services_endpoint} WHERE name = '%s'";
+    $args  = array($form_state['values']['name']);
+    if (!empty($form_state['values']['eid']) && is_numeric($form_state['values']['eid'])) {
+      $query .= ' AND eid != %d';
+      $args[] = $form_state['values']['eid'];
+    }
+    if (db_result(db_query($query, $args))) {
+      form_error($form['name'], t('Endpoint name must be unique.'));
+    }
+  }
+  //TODO: More validation? Eg. validate path? Transliteration etc?
+}
+
+/**
+ * Submit handler for endpoint.
+ */
+function services_ctools_export_ui_form_submit(&$form, &$form_state) {
+  // Test uniqueness of name:
+  if (preg_match("/[^a-z_]/", $form_state['values']['name'])) {
+    form_error($form['name'], t('Endpoint name may only contain lowercase alpha characters and underscores.'));
+  }
+  else {
+    $query = "SELECT eid FROM {services_endpoint} WHERE name = '%s'";
+    $args  = array($form_state['values']['name']);
+    if (!empty($form_state['values']['eid']) && is_numeric($form_state['values']['eid'])) {
+      $query .= ' AND eid != %d';
+      $args[] = $form_state['values']['eid'];
+    }
+    if (db_result(db_query($query, $args))) {
+      form_error($form['name'], t('Endpoint name must be unique.'));
+    }
+  }
+  //TODO: More validation? Eg. validate path? Transliteration etc?
+}
\ No newline at end of file
diff --git a/services.install b/services.install
index 9798802..c1372b6 100644
--- a/services.install
+++ b/services.install
@@ -75,6 +75,7 @@ function services_schema() {
     'export' => array(
       'key' => 'name',
       'identifier' => 'endpoint',
+      'primary key' => 'name',
       'api' => array(
         'owner' => 'services',
         'api' => 'services',
diff --git a/services.module b/services.module
index f0355e1..ede584b 100644
--- a/services.module
+++ b/services.module
@@ -6,6 +6,11 @@
  */
 
 /**
+ * Minimum CTools version needed.
+ */
+define('SERVICES_REQUIRED_CTOOLS_API', '1.7');
+
+/**
  * Implements hook_help().
  */
 function services_help($path, $arg) {
@@ -71,88 +76,10 @@ function services_hook_info() {
 
 /**
  * Implements hook_menu().
+ *
+ * Services UI is defined in the export-ui plugin.
  */
 function services_menu() {
-  $base = array(
-    'access arguments' => array('administer services'),
-    'file'             => 'services.admin.inc',
-  );
-
-  $items['admin/config/services/services'] = array(
-    'title'          => 'Services',
-    'description'    => 'Manage how external applications communicates with Drupal.',
-    'page callback'  => 'services_list_endpoint',
-  ) + $base;
-
-  $items['admin/config/services/services/list'] = array(
-    'title'          => 'List',
-    'page callback'  => 'services_list_endpoint',
-    'type'           => MENU_DEFAULT_LOCAL_TASK,
-    'weight'         => -10,
-  ) + $base;
-  $items['admin/config/services/services/add'] = array(
-    'title'          => 'Add endpoint',
-    'page callback'  => 'services_add_endpoint',
-    'type'           => MENU_LOCAL_TASK,
-  ) + $base;
-  $items['admin/config/services/services/%services_endpoint/edit'] = array(
-    'title'          => 'Edit endpoint',
-    'page callback'  => 'services_edit_endpoint',
-    'page arguments' => array(4),
-    'type'           => MENU_LOCAL_TASK,
-  ) + $base;
-  $items['admin/config/services/services/%services_endpoint/authentication'] = array(
-    'title'          => 'Authentication',
-    'page callback'  => 'services_edit_endpoint_authentication',
-    'page arguments' => array(4),
-    'type'           => MENU_LOCAL_TASK,
-    'weight'         => 5,
-  ) + $base;
-  $items['admin/config/services/services/%services_endpoint/resources'] = array(
-    'title'          => 'Resources',
-    'page callback'  => 'services_edit_endpoint_resources',
-    'page arguments' => array(4),
-    'type'           => MENU_LOCAL_TASK,
-    'weight'         => 10,
-  ) + $base;
-  $items['admin/config/services/services/%services_endpoint/export'] = array(
-    'title'          => 'Export endpoint',
-    'page callback'  => 'drupal_get_form',
-    'page arguments' => array('services_export_endpoint', 4),
-    'type'           => MENU_LOCAL_TASK,
-    'weight'         => 20,
-  ) + $base;
-  $items['admin/config/services/services/%services_endpoint/delete'] = array(
-    'title'          => 'Delete endpoint',
-    'page callback'  => 'drupal_get_form',
-    'page arguments' => array('services_delete_confirm_endpoint', 4),
-    'type'           => MENU_CALLBACK,
-  ) + $base;
-  $items['admin/config/services/services/%services_endpoint/disabledebug'] = array(
-     'page callback'  => 'services_disable_debug_mode',
-     'page arguments' => array(4),
-     'type'           => MENU_CALLBACK,
-  ) + $base;
-  $items['admin/config/services/services/%services_endpoint/enabledebug'] = array(
-     'page callback'  => 'services_enable_debug_mode',
-     'page arguments' => array(4),
-     'type'           => MENU_CALLBACK,
-  ) + $base;
-  $items['admin/config/services/services/%services_endpoint/disable'] = array(
-    'page callback'  => 'services_disable_endpoint',
-    'page arguments' => array(4),
-    'type'           => MENU_CALLBACK,
-  ) + $base;
-  $items['admin/config/services/services/%services_endpoint/enable'] = array(
-    'page callback'  => 'services_enable_endpoint',
-    'page arguments' => array(4),
-    'type'           => MENU_CALLBACK,
-  ) + $base;
-  $items['admin/config/services/services/ahah/security-options'] = array(
-    'page callback' => '_services_ahah_security_options',
-    'type'          => MENU_CALLBACK,
-  ) + $base;
-
   $endpoints = services_endpoint_load_all();
   foreach ($endpoints as $endpoint) {
     if (empty($endpoint->disabled)) {
@@ -167,6 +94,27 @@ function services_menu() {
   }
   return $items;
 }
+/**
+ * Implements of hook_ctools_plugin_api().
+ */
+function services_ctools_plugin_api($module, $api) {
+  if ($module == 'services' && $api == 'plugins') {
+    return array('version' => 3);
+  }
+}
+
+/**
+ * Implement of hook_ctools_plugin_directory().
+ */
+function services_ctools_plugin_directory($module, $type) {
+  // Safety: go away if CTools is not at an appropriate version.
+  if (!module_invoke('ctools', 'api_version', SERVICES_REQUIRED_CTOOLS_API)) {
+    return;
+  }
+  if ($type =='export_ui') {
+    return 'plugins/export_ui';
+  }
+}
 
 /**
  * Access callback that always returns TRUE.
-- 
1.7.3.4


From 652c6ef0de1085a519f2aa1f45bbf594079644d4 Mon Sep 17 00:00:00 2001
From: Kyle Browning <kylebrowning@me.com>
Date: Thu, 17 Mar 2011 09:34:00 -0700
Subject: [PATCH 2/2] Fixes to titles and menu descriptions

---
 plugins/export_ui/services_ctools_export_ui.inc |   98 +++++++++++++++-------
 1 files changed, 67 insertions(+), 31 deletions(-)

diff --git a/plugins/export_ui/services_ctools_export_ui.inc b/plugins/export_ui/services_ctools_export_ui.inc
index 3d0c894..e591d33 100644
--- a/plugins/export_ui/services_ctools_export_ui.inc
+++ b/plugins/export_ui/services_ctools_export_ui.inc
@@ -4,6 +4,7 @@ $plugin = array(
   'schema' => 'services_endpoint',
   'menu' => array(
     'menu item' => 'services',
+    'menu description' => 'Manage Services',
     // Add services specific own menu callbacks.
     'items' => array(
       'resources' => array(
@@ -17,7 +18,7 @@ $plugin = array(
       ),
       'authentication' => array(
         'path' => 'list/%ctools_export_ui/authentication',
-        'title' => 'Resources',
+        'title' => 'Authentication',
         'page callback' => 'ctools_export_ui_switcher_page',
         'page arguments' => array('services_ctools_export_ui', 'authentication', 4),
         'load arguments' => array('services_ctools_export_ui'),
@@ -51,15 +52,27 @@ $plugin = array(
 function services_ctools_export_ui_form(&$form, &$form_state) {
   // Loading runtime include as needed by services_auth_info().
   module_load_include('runtime.inc', 'services');
-
   $endpoint = $form_state['item'];
-  // Set dfefault values.
-
   $form['eid'] = array(
     '#type'  => 'value',
     '#value' => isset($endpoint->eid) ? $endpoint->eid : '',
   );
 
+  $form['endpoint_object'] = array(
+    '#type'  => 'value',
+    '#value' => $endpoint,
+  );
+
+  $form['name'] = array(
+    '#type'          => 'textfield',
+    '#size'          => 24,
+    '#maxlength'     => 255,
+    '#default_value' => $endpoint->name,
+    '#title'         => t('Endpoint name'),
+    '#description'   => t('A unique name used to identify this preset internally. It must be only be alpha characters and underscores. No spaces, numbers or uppercase characters.'),
+    '#required'      => TRUE,
+  );
+
   $form['title'] = array(
     '#type'          => 'textfield',
     '#size'          => 24,
@@ -108,23 +121,28 @@ function services_ctools_export_ui_form(&$form, &$form_state) {
       '#options'       => $auth_options,
       '#default_value' => array_keys($endpoint->authentication),
       '#title'         => t('Authentication'),
-      '#description'   => t('Choose which authentication schemes that should  be used with your endpoint. If no authentication method is selected  the standard Drupal session security is used.'),
+      '#description'   => t('Choose which authentication schemes that should ' .
+        'be used with your endpoint. If no authentication method is selected ' .
+        'the standard Drupal session security is used.'),
     );
   }
   else {
     $form['authentication'] = array(
       '#type'          => 'item',
       '#title'         => t('Authentication'),
-      '#description'   => t('No d modules are installed, standard Drupal session based security will be used.'),
+      '#description'   => t('No authentication modules are installed, standard ' .
+        'Drupal session based security will be used.'),
     );
   }
 
-  $form['services_use_content_permissions'] = array(
-    '#type'           => 'checkbox',
-    '#title'          => t('Apply content permissions'),
-    '#default_value'  => variable_get('services_use_content_permissions', TRUE),
-    '#description'    => t('CCK includes the optional Content Permissions module, which allows administrators to restrict access to content at the field level. By default, node services do NOT apply these permissions, causing all fields to be returned in all cases. Checking this box causes content permissions to be applied to node services.'),
+  $label = (empty($endpoint->eid) && $endpoint->export_type != EXPORT_IN_CODE) ? t('Save and proceed') : t('Save');
+  $form['submit'] = array(
+    '#type'  => 'submit',
+    '#value' => $label,
   );
+
+  return $form;
+
 }
 
 /**
@@ -132,17 +150,20 @@ function services_ctools_export_ui_form(&$form, &$form_state) {
  */
 function services_ctools_export_ui_form_validate(&$form, &$form_state) {
   // Test uniqueness of name:
-  if (preg_match("/[^a-z_]/", $form_state['values']['name'])) {
-    form_error($form['name'], t('Endpoint name may only contain lowercase alpha characters and underscores.'));
+  if (preg_match("/[^A-Za-z0-9_]/", $form_state['values']['name'])) {
+    form_error($form['name'], t('Endpoint name must be alphanumeric or underscores only.'));
   }
   else {
-    $query = "SELECT eid FROM {services_endpoint} WHERE name = '%s'";
-    $args  = array($form_state['values']['name']);
+    $query = db_select('services_endpoint', 'e');
+    $query->addField('e', 'eid');
+    $query->condition('name', $form_state['values']['name']);
+
     if (!empty($form_state['values']['eid']) && is_numeric($form_state['values']['eid'])) {
-      $query .= ' AND eid != %d';
-      $args[] = $form_state['values']['eid'];
+      $query->condition('eid', $form_state['values']['eid'], '!=');
     }
-    if (db_result(db_query($query, $args))) {
+
+    $res = $query->execute()->fetchField();
+    if (!empty($res)) {
       form_error($form['name'], t('Endpoint name must be unique.'));
     }
   }
@@ -153,20 +174,35 @@ function services_ctools_export_ui_form_validate(&$form, &$form_state) {
  * Submit handler for endpoint.
  */
 function services_ctools_export_ui_form_submit(&$form, &$form_state) {
-  // Test uniqueness of name:
-  if (preg_match("/[^a-z_]/", $form_state['values']['name'])) {
-    form_error($form['name'], t('Endpoint name may only contain lowercase alpha characters and underscores.'));
+  $endpoint = $form_state['values']['endpoint_object'];
+
+  $endpoint->name           = $form_state['values']['name'];
+  $endpoint->title          = $form_state['values']['title'];
+  $endpoint->server         = $form_state['values']['server'];
+  $endpoint->path           = $form_state['values']['path'];
+
+  // Set the authentication modules, and preserve the settings for modules
+  // that already exist.
+  $auth = array();
+  if (isset($form_state['values']['authentication'])) {
+    foreach (array_keys($form_state['values']['authentication']) as $module) {
+      if (isset($endpoint->authentication[$module])) {
+        $auth[$module] = $endpoint->authentication[$module];
+      }
+      else {
+        $auth[$module] = array();
+      }
+    }
+  }
+  $endpoint->authentication = $auth;
+
+  if (empty($endpoint->eid)) {
+    drupal_set_message(t('Your new endpoint %title has been saved.', array('%title' => $endpoint->title)));
+    services_endpoint_save($endpoint);
+    $form_state['values']['eid'] = $endpoint->eid;
   }
   else {
-    $query = "SELECT eid FROM {services_endpoint} WHERE name = '%s'";
-    $args  = array($form_state['values']['name']);
-    if (!empty($form_state['values']['eid']) && is_numeric($form_state['values']['eid'])) {
-      $query .= ' AND eid != %d';
-      $args[] = $form_state['values']['eid'];
-    }
-    if (db_result(db_query($query, $args))) {
-      form_error($form['name'], t('Endpoint name must be unique.'));
-    }
+    drupal_set_message(t('Your changes have been saved.'));
+    services_endpoint_save($endpoint);
   }
-  //TODO: More validation? Eg. validate path? Transliteration etc?
 }
\ No newline at end of file
-- 
1.7.3.4

