From 45062ed4275d3fb07ac9f10fd2797b8969a4594a Mon Sep 17 00:00:00 2001
From: Kyle Browning <kylebrowning@me.com>
Date: Mon, 25 Jul 2011 18:20:57 -0700
Subject: [PATCH] RIch settings per endpoint

---
 auth/services_oauth/services_oauth.inc             |   18 ++-
 js/services.admin.js                               |    7 +-
 .../export_ui/services_ctools_export_ui.class.php  |  182 +++++++++++++-------
 services.admin.inc                                 |   34 +---
 services.resource_build.inc                        |    8 +-
 5 files changed, 153 insertions(+), 96 deletions(-)

diff --git a/auth/services_oauth/services_oauth.inc b/auth/services_oauth/services_oauth.inc
index c5c819f..3e7ee8c 100644
--- a/auth/services_oauth/services_oauth.inc
+++ b/auth/services_oauth/services_oauth.inc
@@ -95,11 +95,20 @@ function _services_oauth_security_settings($settings) {
 function _services_oauth_controller_settings($settings, $controller, $endpoint, $class, $name) {
   $form = array();
 
-  $cc = $controller['endpoint']['services_oauth'];
+  if(isset($controller['settings']) && isset($controller['settings']['services_oauth'])) {
+     $cc = $controller['settings']['services_oauth']; 
+  }
+
   $auth_levels = array();
-  $context = oauth_common_context_load($settings['oauth_context']);
-  foreach ($context->authorization_levels as $name => $level) {
-    $auth_levels[$name] = t($level['title']);
+  if(is_array($settings)) {
+    $context = oauth_common_context_load($settings['oauth_context']); 
+  } else {
+    $context = new StdClass();
+  }
+  if(isset($context->authorization_levels)) {
+    foreach ($context->authorization_levels as $name => $level) {
+      $auth_levels[$name] = t($level['title']);
+    } 
   }
 
   $form['credentials'] = array(
@@ -121,6 +130,5 @@ function _services_oauth_controller_settings($settings, $controller, $endpoint,
     '#default_value' => !empty($cc['authorization']) ? $cc['authorization'] : '*',
     '#title'         => t('Required authorization'),
   );
-
   return $form;
 }
diff --git a/js/services.admin.js b/js/services.admin.js
index 892a232..31e3622 100644
--- a/js/services.admin.js
+++ b/js/services.admin.js
@@ -31,21 +31,23 @@ Drupal.behaviors.resourceMenuCollapse = {
           if (row >= 0) {
             $(trs[row]).hide();
             row--;
+            if(row==-1) {
+              $('td.services-remove-td').remove();
+            }
             timeout = setTimeout(rowToggle, 20);
           }
         }
         else {
           if (row < trs.size()) {
+            $(trs[row]).prepend('<td class="services-remove-td">&nbsp</td>');
             $(trs[row]).removeClass('js-hide').show();
             row++;
             timeout = setTimeout(rowToggle, 20);
           }
         }
       }
-
       // Kick-off the toggling upon a new click.
       rowToggle();
-
       // Toggle the arrow image next to the method group title.
       $(this).html(settings.resource.images[(direction ? 0 : 1)]);
       settings.resource[this.id].imageDirection = !direction;
@@ -63,7 +65,6 @@ Drupal.behaviors.resourceSelectAll = {
     $('td.resource-select-all').each(function () {
       var methodCheckboxes = settings.resource['resource-method-group-' + $(this).attr('id')].methodNames;
       var groupCheckbox = $('<input type="checkbox" class="form-checkbox" id="' + $(this).attr('id') + '-select-all" />');
-
       // Each time a single-method checkbox is checked or unchecked, make sure
       // that the associated group checkbox gets the right state too.
       var updateGroupCheckbox = function () {
diff --git a/plugins/export_ui/services_ctools_export_ui.class.php b/plugins/export_ui/services_ctools_export_ui.class.php
index a574ece..f73eda2 100644
--- a/plugins/export_ui/services_ctools_export_ui.class.php
+++ b/plugins/export_ui/services_ctools_export_ui.class.php
@@ -227,7 +227,6 @@ function services_edit_form_endpoint_resources($form, &$form_state, $endpoint) {
     '#type'  => 'value',
     '#value' => $endpoint,
   );
-
   $form['#attached']['js'] = array(
     'misc/tableselect.js',
     drupal_get_path('module', 'services') . '/js/services.admin.js',
@@ -245,6 +244,12 @@ function services_edit_form_endpoint_resources($form, &$form_state, $endpoint) {
     'index'    => t('Index'),
   );
 
+  $classes = array(
+    'actions'          => t('Action'),
+    'targeted_actions' => t('Targeted Action'),
+    'relationships'    => t('Relationship'),
+  );
+
   // Call _services_build_resources() directly instead of
   // services_get_resources to bypass caching.
   $resources = _services_build_resources();
@@ -262,76 +267,45 @@ function services_edit_form_endpoint_resources($form, &$form_state, $endpoint) {
     '#theme' => 'services_resource_table',
    );
 
-  $ignoreArray = array('actions', 'relationships', 'endpoint', 'name', 'file', 'targeted_actions');
   // Generate the list of methods arranged by resource.
-  foreach ($resources as $resource => $methods) {
-    $form['resources']['table'][$resource] = array(
+  foreach ($resources as $name => $resource) {
+    $form['resources']['table'][$name] = array(
       '#collapsed' => TRUE,
     );
 
     $alias = '';
-    if (isset($form_state['build_info']['args'][0]->resources[$resource]['alias'])) {
-      $alias = $form_state['build_info']['args'][0]->resources[$resource]['alias'];
+    if (isset($form_state['build_info']['args'][0]->resources[$name]['alias'])) {
+      $alias = $form_state['build_info']['args'][0]->resources[$name]['alias'];
     }
-    elseif (isset($form_state['input'][$resource . '/alias'])) {
-      $alias = $form_state['input'][$resource . '/alias'];
+    elseif (isset($form_state['input'][$name . '/alias'])) {
+      $alias = $form_state['input'][$name . '/alias'];
     }
 
-    $form['resources']['table'][$resource]['alias'] = array(
+    $form['resources']['table'][$name]['alias'] = array(
       '#type' => 'textfield',
       '#default_value' => $alias,
-      '#name' => $resource .'/alias',
+      '#name' => $name .'/alias',
       '#size' => 20,
     );
-    foreach ($methods as $class => $info) {
-      if (!in_array($class, $ignoreArray)) {
-        if (!isset($info['help'])) {
-          $description = t('No description is available');
-        } else {
-          $description = $info['help'];
-        }
-        if (isset($form_state['build_info']['args'][0]->resources[$resource]['operations'][$class])) {
-          $default_value = $form_state['build_info']['args'][0]->resources[$resource]['operations'][$class]['enabled'];
-        }
-        else {
-          $default_value = 0;
+    foreach ($ops as $op => $title) {
+      if (isset($resource[$op])) {
+        $form['resources']['table'][$name][$name .'/'. $op] = _services_resource_operation_settings($endpoint, $resource, $title, $op);
+      }
+    }
+    foreach ($classes as $element => $class) {
+      if (!empty($resource[$element])) {
+        foreach ($resource[$element] as $action => $definition) {
+          $form['resources']['table'][$name][$name .'/'. $element .'/'. $action] = _services_resource_operation_settings($endpoint, $resource, $class . ' - ' . $action, $element, $action);
         }
-        $form['resources']['table'][$resource][$resource .'/'. $class] = array(
-          '#type' => 'checkbox',
-          '#title' => $class,
-          '#description' => $description,
-          '#default_value' => $default_value,
-         );
       }
-      elseif($class == 'actions' || $class == 'relationships' || $class == 'targeted_actions') {
-        foreach($info as $key => $action) {
-          if (!isset($action['help'])) {
-            $description = t('No description is available');
-          }
-          else {
-            $description = $action['help'];
-          }
-          if (isset($form_state['build_info']['args'][0]->resources[$resource][$class][$key])) {
-            $default_value = $form_state['build_info']['args'][0]->resources[$resource][$class][$key]['enabled'];
-          }
-          else {
-            $default_value = 0;
-          }
-          $form['resources']['table'][$resource][$resource .'/'. $key .'/'. $class] = array(
-            '#type' => 'checkbox',
-            '#title' => $key,
-            '#description' => $description,
-            '#default_value' => $default_value,
-          );
-         }
-       }
-     }
-   }
-
-   $form['save'] = array(
-     '#type'  => 'submit',
-     '#value' => t('Save'),
-   );
+    }
+  }
+
+  $form['save'] = array(
+   '#type'  => 'submit',
+   '#value' => t('Save'),
+  );
+  
   return $form;
 }
 
@@ -387,13 +361,103 @@ function services_edit_form_endpoint_resources_submit($form, $form_state) {
     }
     // If it is action, relationship, or targeted action.
     if (isset($split_path[2])) {
-      $final_resource[$resource][$split_path[2]][$method]['enabled'] = 1;
+      $final_resource[$resource][$split_path[1]][$split_path[2]] = $state;
       continue;
     }
     // If it is operation.
-    $final_resource[$resource]['operations'][$method]['enabled'] = 1;
+    $final_resource[$resource]['operations'][$method] = $state;
   }
   $endpoint->resources = $final_resource;
   services_endpoint_save($endpoint);
   drupal_set_message('Resources have been saved');
 }
+
+
+/**
+ * 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;
+  $classes = array(
+    'actions'          => t('Action'),
+    'targeted_actions' => t('Targeted Action'),
+    'relationships'    => t('Relationship'),
+  );
+
+  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 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($endpoint, $resource, $title, $class, $name = NULL) {
+  module_load_include('runtime.inc', 'services');
+
+  $form = array(
+    '#tree' => TRUE,
+  );
+
+  if ($rop = services_get_resource_operation_info($resource, $class, $name)) {
+    if (isset($rop['help'])) {
+      $description = $rop['help'];
+    }
+    else {
+      $description = t('No description is available');
+    }
+    $form['enabled'] = array(
+      '#type' => 'checkbox',
+      '#title' => $title,
+      '#description' => $description,
+      '#default_value' => !empty($rop['settings']) && $rop['settings']['enabled'],
+      '#return_value' => 1,
+    );
+    if (!empty($rop['endpoint']['preprocess'])) {
+      $form['preprocess'] = array(
+        '#type' => 'item',
+        '#title' => t('Preprocess function'),
+        '#markup' => $rop['endpoint']['preprocess'],
+      );
+    }
+
+    if (!empty($rop['endpoint']['postprocess'])) {
+      $form['preprocess'] = array(
+        '#type' => 'item',
+        '#title' => t('Postprocess function'),
+        '#markup' => $rop['endpoint']['Postprocess'],
+      );
+    }
+
+    // Let authentication modules add their configuration options
+    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_form['#tree'] = TRUE;
+        $form[$auth_module] = array();
+        $form[$auth_module] += $settings_form;
+      }
+    }
+  }
+
+  return $form;
+}
\ No newline at end of file
diff --git a/services.admin.inc b/services.admin.inc
index 7337ff7..4dc1ed7 100644
--- a/services.admin.inc
+++ b/services.admin.inc
@@ -9,8 +9,8 @@ function theme_services_resource_table($variables) {
   // Create header for resource selection table.
   $header = array(
     array('class' => array('select-all')),
-    array('data' => t('Resource'), 'class' => array('resource_method')),
-    array('data' => t('Description'), 'class' => array('resource_description')),
+    array('data' => t('Resources'), 'class' => array('resource_settings')),
+    array('data' => t('Settings'), 'class' => array('resource_settings')),
     array('data' => t('Alias'), 'class' => array('resource_alias')),
   );
 
@@ -46,10 +46,9 @@ function theme_services_resource_table($variables) {
         '<label for="' . $method_class . '-select-all" class="resource-group-label">' . $key . '</label>',
       'class' => array('resource-group-label'),
     );
-
     $row[] = array(
       'data' => '&nbsp;',
-      'class' => array('resource-group-description'),
+      'class' => array('resource-group-settings'),
     );
     $row[] = array(
       'data' => drupal_render($element['alias']),
@@ -68,38 +67,23 @@ function theme_services_resource_table($variables) {
     // Cycle through each method within the current group.
     foreach (element_children($element) as $method_name) {
       if($method_name != 'alias') {
-        $method = $element[$method_name];
         $row = array();
-
-        $current_js['methodNames'][] = $method['#id'];
-
-        // Store method title and description so that checkbox won't render them.
-        $title = $method['#title'];
-        $description = $method['#description'];
-
-        $method['#title_display'] = 'invisible';
-        unset($method['#description']);
-
-        // Test name is used to determine what methods to run.
-        $method['#name'] = $method_name;
-
+        $current_js['methodNames'][] = $element[$method_name]['enabled']['#id'];
+        
         $row[] = array(
-          'data' => drupal_render($method),
+          'data' => drupal_render($element[$method_name]['enabled']),
           'class' => array('resource-method-select'),
         );
         $row[] = array(
-          'data' => '<label for="' . $method['#id'] . '">' . $title . '</label>',
-          'class' => array('resource-method-label'),
-        );
-        $row[] = array(
-          'data' => '<div class="description">' . $description . '</div>',
-          'class' => array('resource-method-description'),
+          'data' => drupal_render($element[$method_name]),
+          'class' => array('resource-method-setting'),
         );
         $row[] = array(
           'data' => '<div class="alias">&nbsp;</div>',
           'class' => array('resource-method-alias'),
         );
         $rows[] = array('data' => $row, 'class' => array($method_class . '-method', ($collapsed ? 'js-hide' : '')));
+
       }
 
     }
diff --git a/services.resource_build.inc b/services.resource_build.inc
index e375513..a84862a 100644
--- a/services.resource_build.inc
+++ b/services.resource_build.inc
@@ -106,14 +106,14 @@ function _services_apply_endpoint(&$resources, $endpoint, $strict = TRUE) {
       foreach ($crud as $op) {
         if (isset($resource[$op])) {
           $cop = isset($cres['operations'][$op]) ? $cres['operations'][$op] : array();
-          $resource[$op]['endpoint'] = $cop;
+          $resource[$op]['settings'] = $cop;
           if ($strict && (empty($cop) || !$cop['enabled'])) {
             unset($resource[$op]);
           }
         }
       }
-
-      $classes = array('targeted actions', 'actions', 'relationships');
+  
+      $classes = array('targeted_actions', 'actions', 'relationships');
       foreach ($classes as $class) {
         if (!empty($resource[$class])) {
           foreach ($resource[$class] as $op => $def) {
@@ -124,7 +124,7 @@ function _services_apply_endpoint(&$resources, $endpoint, $strict = TRUE) {
               }
             }
             else {
-              $resource[$class][$op]['endpoint'] = $cop;
+              $resource[$class][$op]['settings'] = $cop;
             }
           }
         }
-- 
1.7.4.4

