WIP grouped custom fields
authorSteve Sutton <steve@gaslightmedia.com>
Mon, 23 Jul 2018 20:46:54 +0000 (16:46 -0400)
committerSteve Sutton <steve@gaslightmedia.com>
Mon, 23 Jul 2018 20:46:54 +0000 (16:46 -0400)
Adding groups into the custom fields.
Can add group edit group delete group.
Can add fields to groups
Can position groups
Can position fields within groups.

classes/data/dataCustomFields.php
index.php
models/admin/ajax/glmCFields.php [new file with mode: 0644]
models/admin/management/fields.php
setup/databaseScripts/create_database_V0.0.4.sql [deleted file]
setup/databaseScripts/create_database_V0.0.5.sql [new file with mode: 0644]
setup/databaseScripts/dbVersions.php
setup/databaseScripts/update_database_V0.0.5.sql [new file with mode: 0644]
setup/validActions.php
views/admin/management/fields.html

index 1136351..d5f32b1 100644 (file)
@@ -122,6 +122,12 @@ class GlmDataFieldsCustomFields extends GlmDataAbstract
                 'use'       => 'a'
             ),
 
+            // Group Id
+            'gid' => array(
+                'field'     => 'gid',
+                'type'      => 'integer',
+                'use'       => 'a',
+            ),
             // Event ID
             'field_name' => array(
                 'field'    => 'field_name',
index de5126f..9c42f8c 100644 (file)
--- a/index.php
+++ b/index.php
@@ -38,7 +38,7 @@
  *  version from this plugin.
  */
 define('GLM_MEMBERS_FIELDS_PLUGIN_VERSION', '1.0.4');
-define('GLM_MEMBERS_FIELDS_PLUGIN_DB_VERSION', '0.0.4');
+define('GLM_MEMBERS_FIELDS_PLUGIN_DB_VERSION', '0.0.5');
 
 // This is the minimum version of the GLM Members DB plugin require for this plugin.
 define('GLM_MEMBERS_FIELDS_PLUGIN_MIN_MEMBERS_REQUIRED_VERSION', '2.8.0');
diff --git a/models/admin/ajax/glmCFields.php b/models/admin/ajax/glmCFields.php
new file mode 100644 (file)
index 0000000..c7829a1
--- /dev/null
@@ -0,0 +1,208 @@
+<?php
+/**
+ * Gaslight Media Members Database
+ * Custom Fields Form Management
+ *
+ * PHP version 5.5
+ *
+ * @category glmWordPressPlugin
+ * @package  glmMembersDatabase
+ * @author   Chuck Scott <cscott@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  0.1
+ */
+
+// Load Management Fields data abstract
+require_once GLM_MEMBERS_FIELDS_PLUGIN_CLASS_PATH.'/data/dataCustomFields.php';
+
+
+/*
+ * This class performs the work of handling images passed to it via
+ * an AJAX call that goes through the WorPress AJAX Handler.
+ *
+ */
+class GlmMembersAdmin_ajax_glmCFields extends GlmDataFieldsCustomFields
+{
+
+    /**
+     * WordPress Database Object
+     *
+     * @var $wpdb
+     * @access public
+     */
+    public $wpdb;
+    /**
+     * Plugin Configuration Data
+     *
+     * @var $config
+     * @access public
+     */
+    public $config;
+
+    /*
+     * Constructor
+     *
+     * This contructor sets up this model. At this time that only includes
+     * storing away the WordPress data object.
+     *
+     * @return object Class object
+     *
+     */
+    public function __construct ($wpdb, $config)
+    {
+
+        // Save WordPress Database object
+        $this->wpdb = $wpdb;
+
+        // Save plugin configuration object
+        $this->config = $config;
+
+        // Run constructor for data class
+        parent::__construct(false, false);
+
+    }
+
+    /*
+     * Perform Model Action
+     *
+     * This modelAction takes an AJAX image upload and stores the image in the
+     * media/images directory of the plugin.
+     *
+     * This model action does not return, it simply does it's work then calls die();
+     *
+     * @param $actionData
+     *
+     * @return No return is expected from this model. Content should be output then wp_die();
+     */
+    public function modelAction ($actionData = false)
+    {
+
+        switch($_REQUEST['option']) {
+
+            case 'sortGroupOrder':       // Save sort order when done with drag and drop of field in list
+
+                // Fields should be in correct order in the submitted array and keys should be sequential from 0
+                foreach ($_REQUEST['sortedIds'] as $sortKey=>$sortVal) {
+
+                    // Strip "field_" from field ID number
+                    $fieldId = preg_replace("/[^0-9]/", "", $sortVal);
+
+                    // Update the field with the key as the order field
+                    $this->wpdb->update(
+                        GLM_MEMBERS_FIELDS_PLUGIN_DB_PREFIX . 'custom_field_groups',
+                        array( 'group_order' => $sortKey ),
+                        array( 'id' => $fieldId ),
+                        array( '%d' ),
+                        array( '%d' )
+                    );
+
+                    // No need to reply to this
+
+                }
+                break;
+
+            case 'sortOrder':       // Save sort order when done with drag and drop of field in list
+
+                // Fields should be in correct order in the submitted array and keys should be sequential from 0
+                foreach ($_REQUEST['sortedIds'] as $sortKey=>$sortVal) {
+
+                    // Strip "field_" from field ID number
+                    $fieldId = preg_replace("/[^0-9]/", "", $sortVal);
+
+                    // Update the field with the key as the order field
+                    $this->wpdb->update(
+                        GLM_MEMBERS_FIELDS_PLUGIN_DB_PREFIX . 'custom_fields',
+                        array( 'field_order' => $sortKey ),
+                        array( 'id' => $fieldId ),
+                        array( '%d' ),
+                        array( '%d' )
+                    );
+
+                    // No need to reply to this
+
+                }
+                break;
+
+
+            default:
+
+        }
+
+        wp_die();
+    }
+
+
+    /**
+     * Merge template and data to produce HTML
+     *
+     * Checks the theme's view directories and the view directories for
+     * this plugin for a matching view file.
+     *
+     * Note that $viewFile needs to have the proper view directory path
+     * includes. (i.e. "/views/front/registrations/summary.html")
+     *
+     * @param $data array Array of data to merge with the template
+     * @param $view string Path added to
+     *
+     * @access public
+     * @return void
+     */
+    function generateHTML($data, $viewFile)
+    {
+
+        // If a view file is specified
+        if ($viewFile) {
+
+            // Get the specified view file - check theme first
+            $viewPath = GLM_MEMBERS_PLUGIN_CURRENT_THEME_DIR."/views";
+            $viewPath2 = GLM_MEMBERS_CUSTOMFIELDS_PLUGIN_PATH . "views";       // Save default
+
+            // If the view is not found in the theme, fall back to views in the plugin
+            if (!is_file($viewPath.'/'.$viewFile)) {
+
+                // Next try the plugin/add-on
+                $viewPath = GLM_MEMBERS_CUSTOMFIELDS_PLUGIN_PATH . "/views";
+
+                if (!is_file($viewPath.'/'.$viewFile)) {
+
+                    if (GLM_MEMBERS_PLUGIN_FRONT_DEBUG) {
+                        trigger_error("Bad or missing view file when generating checkout HTML: $viewPath/$viewFile", E_USER_NOTICE);
+                    }
+
+                }
+
+            }
+
+        }
+
+        // Load Smarty Template support
+        $smarty = new smartyTemplateSupport();
+
+        // Add standard parameters
+        require GLM_MEMBERS_PLUGIN_SETUP_PATH.'/standardTemplateParams.php';
+
+        // Add data from model to Smarty template
+        if (is_array($data) && count($data) > 0) {
+            foreach ($data as $k => $d) {
+                $smarty->templateAssign($k, $d);
+            }
+        }
+
+        // Update the Smarty view path
+        $smarty->template->setTemplateDir($viewPath);
+
+        // If the view path doesn't match the default, add the default (using theme view)
+        if ($viewPath2 != $viewPath) {
+            $smarty->template->addTemplateDir($viewPath2);
+        }
+
+        // Generate output from model data and view
+        $out = $smarty->template->fetch($viewFile);
+
+        return $out;
+
+    }
+
+
+
+}
index 39dfdca..d3fa638 100644 (file)
@@ -147,6 +147,76 @@ class GlmMembersAdmin_management_fields extends GlmDataFieldsCustomFields
 
         switch ($option) {
 
+            case 'updateNewGroup':
+                // echo '<pre>$_REQUEST: ' . print_r( $_REQUEST, true ) . '</pre>';
+                $gid = filter_var( $_REQUEST['id'], FILTER_VALIDATE_INT );
+                if ( $gid ) {
+                    $this->wpdb->update(
+                        GLM_MEMBERS_FIELDS_PLUGIN_DB_PREFIX . 'custom_field_groups',
+                        array(
+                            'uid'         => GLM_MEMBERS_CONTACTS_PLUGIN_SLUG,
+                            'group_name'  => filter_var( $_REQUEST['group_name'] ),
+                            'publish'     => filter_var( $_REQUEST['publish'], FILTER_VALIDATE_BOOLEAN ),
+                        ),
+                        array( 'id' => $gid ),
+                        array(
+                            '%s',
+                            '%s',
+                            '%s',
+                        ),
+                        array( '%d' )
+                    );
+                }
+                break;
+
+            case 'addNewGroup':
+                $group_order = 1;
+                // Add a group
+                // When adding a group get the last group_order
+                $max_group_order = $this->wpdb->get_var(
+                    "SELECT max(group_order)
+                       FROM " . GLM_MEMBERS_FIELDS_PLUGIN_DB_PREFIX . "custom_field_groups
+                      WHERE uid = '" . GLM_MEMBERS_CONTACTS_PLUGIN_SLUG . "'"
+                );
+                if ( $max_group_order ) {
+                    // Set the new group to last place
+                    $group_order = $max_group_order + 1;
+                }
+                $this->wpdb->insert(
+                    GLM_MEMBERS_FIELDS_PLUGIN_DB_PREFIX . 'custom_field_groups',
+                    array(
+                        'uid'         => GLM_MEMBERS_CONTACTS_PLUGIN_SLUG,
+                        'group_name'  => filter_var( $_REQUEST['group_name'] ),
+                        'group_order' => $group_order,
+                        'publish'     => filter_var( $_REQUEST['publish'], FILTER_VALIDATE_BOOLEAN ),
+                    ),
+                    array(
+                        '%s',
+                        '%s',
+                        '%d',
+                        '%s',
+                    )
+                );
+                break;
+
+            case 'deleteGroup':
+                // echo '<pre>$_REQUEST: ' . print_r( $_REQUEST, true ) . '</pre>';
+                $gid = filter_var( $_REQUEST['id'], FILTER_VALIDATE_INT );
+                if ( $gid ) {
+                    // Need to delete the group and the fields in the group
+                    $this->wpdb->delete(
+                        GLM_MEMBERS_FIELDS_PLUGIN_DB_PREFIX . 'custom_field_groups',
+                        array( 'id' => $gid ),
+                        array( '%d' )
+                    );
+                    $this->wpdb->delete(
+                        GLM_MEMBERS_FIELDS_PLUGIN_DB_PREFIX . 'custom_fields',
+                        array( 'gid' => $gid ),
+                        array( '%d' )
+                    );
+                }
+                break;
+
             case 'addNew':
                 $this->insertEntry();
                 break;
@@ -194,6 +264,23 @@ class GlmMembersAdmin_management_fields extends GlmDataFieldsCustomFields
 
         // Get list of Custom Fields
         $custom_fields = $this->getList( $where );
+
+        // Get all groups
+        $groups = $this->wpdb->get_results(
+            "SELECT *
+               FROM " . GLM_MEMBERS_FIELDS_PLUGIN_DB_PREFIX . "custom_field_groups
+              WHERE uid = '" . GLM_MEMBERS_CONTACTS_PLUGIN_SLUG . "'
+            ORDER BY group_order",
+            ARRAY_A
+        );
+
+        if ( $groups ) {
+            foreach ( $groups as $key => $group ) {
+                $groups[$key]['custom_fields'] = $this->getList( "uid = '$uid' AND gid = {$group['id']}", 'field_order' );
+            }
+            // echo '<pre>$groups: ' . print_r( $groups, true ) . '</pre>';
+        }
+
         //  echo "<pre>REQUEST " . print_r($_REQUEST, true) . "</pre>";
         //  echo "<pre>GET " . print_r($_GET, true) . "</pre>";
         if ( isset($custom_fields) && $custom_fields && count( $custom_fields ) > 0 ) {
@@ -205,11 +292,12 @@ class GlmMembersAdmin_management_fields extends GlmDataFieldsCustomFields
             'option2'             => $option,
             'settingsUpdated'     => $settings_updated,
             'settingsUpdateError' => $settings_update_error,
-            'custom_fields'       => $custom_fields,
+            'custom_fields'       => false,//$custom_fields,
             'field_types'         => $this->config['custom_field_types'],
             'haveCustomFields'    => $haveCustomFields,
             'uid'                 => $uid,
-            'glm_action'          => $glm_action
+            'glm_action'          => $glm_action,
+            'groups'              => $groups,
         );
         // echo "<pre>Template data:" . print_r($template_data, true) . "</pre>";
 
diff --git a/setup/databaseScripts/create_database_V0.0.4.sql b/setup/databaseScripts/create_database_V0.0.4.sql
deleted file mode 100644 (file)
index 9deb7c2..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
--- Gaslight Media Members Database - Fields Add-On
--- File Created: 2017-03-27
--- Database Version: 0.0.4
--- Database Creation Script
---
--- This file is called to create a new set of tables for this
--- add-on for the most recent database version for this add-on.
---
--- There should only be one such file in this directory
---
--- To permit each query below to be executed separately,
--- all queries must be separated by a line with four dashes
-
-
--- Field Setup Table
-CREATE TABLE {prefix}custom_fields (
-  id INT NOT NULL AUTO_INCREMENT,
-  fid TEXT NOT NULL DEFAULT '',
-  field_name TINYTEXT NOT NULL DEFAULT '',      -- Field Name
-  field_type TINYTEXT NOT NULL DEFAULT '',      -- Field Type
-  field_order SMALLINT NOT NULL DEFAULT 0,      -- Order for Field
-  admin_search BOOLEAN NOT NULL DEFAULT '0',    -- If the field is added to member list filters.
-  required BOOLEAN NOT NULL DEFAULT '0',        -- If the field is required.
-  uid varchar(255) NOT NULL DEFAULT '',         -- id for the series of custom fields associated with an entity
-  PRIMARY KEY (id),
-  INDEX(field_name(20))
-);
-
-----
-
--- Data Table
-CREATE TABLE {prefix}custom_field_data (
-  id INT NOT NULL AUTO_INCREMENT,
-  field_id INT NOT NULL DEFAULT 0,              -- Field Id             -- Member Info Id
-  field_data TEXT NOT NULL DEFAULT '',          -- Data for the field
-  entity_id INT NOT NULL DEFAULT 0,             -- id for the specific field associated with an entity
-  PRIMARY KEY (id),
-  INDEX(field_id),
-  INDEX(entity_id)
-);
-
-----
-
--- Picklist Field Options
-CREATE TABLE {prefix}custom_field_options (
-  id INT NOT NULL AUTO_INCREMENT,
-  field_id INT NOT NULL DEFAULT 0,                  -- Pointer to ID of fields in custom_fields table
-  option_text TINYTEXT NOT NULL DEFAULT '',         -- Option's Displayed text
-  option_value TINYTEXT NOT NULL DEFAULT '',        -- Option's value
-  option_default BOOLEAN NOT NULL DEFAULT false,    -- Flag indicating that this option is the default
-  PRIMARY KEY (id),
-  INDEX (field_id)
-);
diff --git a/setup/databaseScripts/create_database_V0.0.5.sql b/setup/databaseScripts/create_database_V0.0.5.sql
new file mode 100644 (file)
index 0000000..2c08da2
--- /dev/null
@@ -0,0 +1,65 @@
+-- Gaslight Media Members Database - Fields Add-On
+-- File Created: 2017-03-27
+-- Database Version: 0.0.4
+-- Database Creation Script
+--
+-- This file is called to create a new set of tables for this
+-- add-on for the most recent database version for this add-on.
+--
+-- There should only be one such file in this directory
+--
+-- To permit each query below to be executed separately,
+-- all queries must be separated by a line with four dashes
+
+
+-- Field Setup Table
+CREATE TABLE {prefix}custom_fields (
+  id INT NOT NULL AUTO_INCREMENT,
+  gid TEXT NOT NULL DEFAULT '',                 -- Field Id (for groups)
+  field_name TINYTEXT NOT NULL DEFAULT '',      -- Field Name
+  field_type TINYTEXT NOT NULL DEFAULT '',      -- Field Type
+  field_order SMALLINT NOT NULL DEFAULT 0,      -- Order for Field
+  admin_search BOOLEAN NOT NULL DEFAULT '0',    -- If the field is added to member list filters.
+  required BOOLEAN NOT NULL DEFAULT '0',        -- If the field is required.
+  uid varchar(255) NOT NULL DEFAULT '',         -- id for the series of custom fields associated with an entity
+  PRIMARY KEY (id),
+  INDEX(field_name(20))
+);
+
+----
+
+-- Data Table
+CREATE TABLE {prefix}custom_field_data (
+  id INT NOT NULL AUTO_INCREMENT,
+  field_id INT NOT NULL DEFAULT 0,              -- Field Id             -- Member Info Id
+  field_data TEXT NOT NULL DEFAULT '',          -- Data for the field
+  entity_id INT NOT NULL DEFAULT 0,             -- id for the specific field associated with an entity
+  PRIMARY KEY (id),
+  INDEX(field_id),
+  INDEX(entity_id)
+);
+
+----
+
+-- Picklist Field Options
+CREATE TABLE {prefix}custom_field_options (
+  id INT NOT NULL AUTO_INCREMENT,
+  field_id INT NOT NULL DEFAULT 0,                  -- Pointer to ID of fields in custom_fields table
+  option_text TINYTEXT NOT NULL DEFAULT '',         -- Option's Displayed text
+  option_value TINYTEXT NOT NULL DEFAULT '',        -- Option's value
+  option_default BOOLEAN NOT NULL DEFAULT false,    -- Flag indicating that this option is the default
+  PRIMARY KEY (id),
+  INDEX (field_id)
+);
+
+----
+
+-- Group Table
+CREATE TABLE {prefix}custom_field_groups (
+  id INT NOT NULL AUTO_INCREMENT,
+  uid TINYTEXT NOT NULL DEFAULT '',                 -- id for the series of custom fields associated with an entity
+  group_name TINYTEXT NOT NULL,                     -- Name of Section/Group
+  group_order SMALLINT NOT NULL DEFAULT 0,          -- Group Order/Position
+  publish BOOLEAN NULL DEFAULT false,               -- Bool to publish group to directory
+  PRIMARY KEY (id)
+);
index dc0810e..5d2bfa8 100644 (file)
@@ -18,5 +18,6 @@ $glmMembersFieldsDbVersions = array(
     '0.0.2' => array('version' => '0.0.2', 'tables' => 2, 'date' => '04/14/2017'),
     '0.0.3' => array('version' => '0.0.3', 'tables' => 2, 'date' => '04/24/2017'),
     '0.0.4' => array('version' => '0.0.4', 'tables' => 2, 'date' => '10/18/2017'),
+    '0.0.5' => array('version' => '0.0.5', 'tables' => 4, 'date' => '07/23/2018'),
 );
 
diff --git a/setup/databaseScripts/update_database_V0.0.5.sql b/setup/databaseScripts/update_database_V0.0.5.sql
new file mode 100644 (file)
index 0000000..837a5dd
--- /dev/null
@@ -0,0 +1,35 @@
+-- Gaslight Media Members Database
+-- File Created: 2017-04-14
+-- Database Version: 0.0.2
+-- Database Update From Previous Version Script
+--
+-- To permit each query below to be executed separately,
+-- all queries must be separated by a line with four dashes
+
+-- Add fid to custom_fields
+ALTER TABLE {prefix}custom_fields ADD gid TEXT NOT NULL DEFAULT '';    -- Field Id (for groups)
+
+----
+
+-- Picklist Field Options
+CREATE TABLE {prefix}custom_field_options (
+  id INT NOT NULL AUTO_INCREMENT,
+  field_id INT NOT NULL DEFAULT 0,                  -- Pointer to ID of fields in custom_fields table
+  option_text TINYTEXT NOT NULL DEFAULT '',         -- Option's Displayed text
+  option_value TINYTEXT NOT NULL DEFAULT '',        -- Option's value
+  option_default BOOLEAN NOT NULL DEFAULT false,    -- Flag indicating that this option is the default
+  PRIMARY KEY (id),
+  INDEX (field_id)
+);
+
+----
+
+-- Group Table
+CREATE TABLE {prefix}custom_field_groups (
+  id INT NOT NULL AUTO_INCREMENT,
+  uid TINYTEXT NOT NULL DEFAULT '',                 -- id for the series of custom fields associated with an entity
+  group_name TINYTEXT NOT NULL,                     -- Name of Section/Group
+  group_order SMALLINT NOT NULL DEFAULT 0,          -- Group Order/Position
+  publish BOOLEAN NULL DEFAULT false,               -- Bool to publish group to directory
+  PRIMARY KEY (id)
+);
index db6208d..63feb8f 100644 (file)
@@ -61,6 +61,7 @@ $glmMembersFieldsAddOnValidActions = array(
     'adminActions' => array(
         'ajax' => array(
           'filterSearch' => GLM_MEMBERS_FIELDS_PLUGIN_SLUG,
+          'glmCFields'   => GLM_MEMBERS_FIELDS_PLUGIN_SLUG,
         ),
         'entity' => array(
             'fields' => GLM_MEMBERS_FIELDS_PLUGIN_SLUG,
index 13a6e2e..bc3e8b3 100644 (file)
@@ -1,52 +1,32 @@
-<!-- Add Custom Field Button and Dialog Box -->
-<div id="newFieldButton" class="button button-primary glm-right">Add a Custom Field</div>
-<div id="newFieldDialog" class="glm-dialog-box" title="Enter a New Custom Field">
-    <form action="{$thisUrl}?page={$thisPage}&glm_action={$glm_action}&option=customfields" method="post" enctype="multipart/form-data">
-        <input type="hidden" name="glm_action" value="{$glm_action}">
-        <input type="hidden" name="option" value="customfields">
-        <input type="hidden" name="option2" value="addNew">
+{* Add Custom Field Group Button and Dialog Box *}
+<div id="newGroupButton" class="button button-primary right">Add A Custom Field Group</div>
+
+<div id="cfGroupDialog" class="glm-dialog-box" title="Add/Edit Custom Field Group">
+    <form action="" method="post" enctype="multipart/form-data">
+        <input type="hidden" name="glm_action" value="{$glm_action}" />
+        <input type="hidden" name="option" value="customfields" />
+        <input id="editGroupOption2" type="hidden" name="option2" value="addNewGroup" />
+        <input id="groupId" type="hidden" name="id" value="" />
 
         <table class="glm-admin-table">
             <tr>
-                <th class="glm-required">Field Name:</th>
-                <td>
-                    <input type="text" name="field_name" class="glm-form-text-input">
-                </td>
-            </tr>
-            <tr>
-                <th class="glm-required">Field Type:</th>
-                <td>
-                    <select name="field_type">
-                        {foreach $field_types as $val => $label}
-                        <option value="{$val}">{$label}</option>
-                        {/foreach}
-                    </select>
-                </td>
-            </tr>
-            <tr>
-                <th>Admin Searchable</th>
-                <td>
-                    <input type="hidden" name="admin_search" value="0" />
-                    <input type="checkbox" name="admin_search" value="1" />
-                    (text or checkbox only)
-                </td>
+                <th class="glm-required">Group Name:</th>
+                <td><input id="groupName" type="text" name="group_name" class="glm-form-text-input"></td>
             </tr>
             <tr>
-            <th>Required?</th>
+                <th>Publish to Front End</th>
                 <td>
-                    <input type="hidden" name="required" value="0" />
-                    <input type="checkbox" name="required" value="1" />
+                    <input type="hidden" name="publish" value="0" />
+                    <input id="groupPublish" type="checkbox" name="publish" value="1" />
                 </td>
             </tr>
-            <input type="hidden" name="uid" value="{$uid}">
         </table>
-        <p><span class="glm-required">*</span> Required</p>
-        <a id="newFieldCancel" class="button button-primary glm-right">Cancel</a>
+        <a id="newGroupCancel" class="button button-primary glm-right">Cancel</a>
         <input type="submit" value="Add new Custom Field" class="button button-primary">
     </form>
 </div>
 
-<!-- Delete Field Button and Dialog Box -->
+{* Delete Field Dialog Box *}
 <div id="deleteFieldDialog" class="glm-dialog-box" title="Delete Field">
     <center>
         <p>Are you sure you want to delete this field?</p>
     </center>
 </div>
 
-<!-- Edit Field Dialog Box -->
+{* Delete Group Dialog Box *}
+<div id="deleteGroupDialog" class="glm-dialog-box" title="Delete Group">
+    <center>
+        <p>Are you sure you want to delete this Group?</p>
+        <p>All Fields in this group will be deleted.</p>
+        <p><div id="deleteGroupConfirm" class="button button-primary">Yes, delete this Group</div></p>
+        <p><div id="deleteGroupCancel" class="button button-primary">Cancel</div></p>
+    </center>
+</div>
+
+
+{* Edit Field Dialog Box *}
 <div id="editFieldDialog" class="glm-dialog-box" title="Edit this Field">
     <form action="{$thisUrl}?page={$thisPage}&glm_action={$glm_action}&option=customfields" method="post" enctype="multipart/form-data">
-        <input type="hidden" name="glm_action" value="{$glm_action}">
-        <input type="hidden" name="option" value="customfields">
-        <input type="hidden" name="option2" value="update">
-        <input id="editFieldID" type="hidden" name="id" value="">
+        <input type="hidden" name="glm_action" value="{$glm_action}" />
+        <input type="hidden" name="option" value="customfields" />
+        <input type="hidden" id="editFieldOption2" name="option2" value="update" />
+        <input id="editFieldID" type="hidden" name="id" value="" />
+        <input id="editGroupID" type="hidden" name="gid" value="" />
         <table class="glm-admin-table">
             <tr>
                 <th class="glm-required">Field Name:</th>
@@ -79,8 +71,6 @@
                     </select>
                 </td>
             </tr>
-            <!-- The UID check below is just a TEMPORARY measure until we have search filters implemented on plugins beyond glm-member-db -->
-            {if $uid == 'glm-member-db'}
             <tr>
                 <th>Admin Searchable</th>
                 <td>
@@ -89,7 +79,6 @@
                     (text or checkbox only)
                 </td>
             </tr>
-            {/if}
             <tr>
                 <th>Required?</th>
                 <td>
         </table>
         <p><span class="glm-required">*</span> Required</p>
         <a id="editFieldCancel" class="button button-primary glm-right">Cancel</a>
-        <input type="submit" value="Update this Field">
+        <input id="editFieldSubmit" type="submit" value="Update this Field">
     </form>
 </div>
+
+{* Updates / Errors *}
 <table id="glm-table-settings" class="glm-admin-table glm-settings-table{if $option2!='settings'} glm-hidden{/if}">
     <tr>
         <td colspan="2">
         </td>
     </tr>
 </table>
-<table id="glm-table-settings" class="glm-admin-table glm-settings-table">
-    <thead>
-        <tr>
-            <th>ID</th>
-            <th>Field</th>
-            <th>Type</th>
-            <th>UID/Entity</th>
-            <th>Required</th>
-            <th>Admin Searchable</th>
-            <th>&nbsp;</th>
-        </tr>
-    </thead>
-    <tbody>
-        {if $haveCustomFields}
-        {assign var="i" value="0"}
-        {foreach $custom_fields as $t}
-        {if $i++ is odd by 1}
-        <tr>
-            {else}
-            <tr class="alternate">
-                {/if}
-                <td>{$t.id}</td>
-                <td>
-                    <div>
-                        <a
-                        class="editField"
-                        data-fieldID="{$t.id}"
-                        data-fieldType="{$t.field_type.value|escape:'html'}"
-                        data-adminSearch="{$t.admin_search.value}"
-                        data-required="{$t.required.value}">{$t.field_name}</a>
-                    </div>
-                </td>
-                <td id="editFieldType_{$t.id}">
-                    {$t.field_type.name}
-                </td>
-                <td>
-                    {$t.uid}
-                </td>
-                <td>
-                    {$t.required.name}
-                </td>
-                <td>
-                    {$t.admin_search.name}
-                </td>
-                <td>
-                    <div class="deleteFieldButton button button-secondary glm-button-small glm-right" data-fieldID="{$t.id}">Delete</div>
-                </td>
-            </tr>
-            {/foreach}
-            {else}
-            <tr class="alternate"><td colspan="2">(no custom fields listed)</td></tr>
-            {/if}
-    </tbody>
-</table>
-<!-- Tests -->
+
+{* custom fields table *}
+<div id="sortable-groups">
+    {if $groups}
+        {foreach $groups as $group}
+        <div style="margin-bottom: 20px;" id="group_{$group.id}">
+            <div style="padding: 10px;">
+                <strong>{$group.group_name}</strong>
+                ( {if $group.publish}Published{else}Not Published{/if} )
+                <a class="deletGroupButton button button-secondary glm-button-small"
+                data-groupId="{$group.id}">Delete</a>
+                <a class="groupEdit button button-secondary glm-button-small"
+                data-groupId="{$group.id}"
+                data-groupName="{$group.group_name|escape:'html'}"
+                data-publish="{$group.publish}">Edit</a>
+            </div>
+            <table style="width:100%; background-color: #fff; border: 1px solid black;">
+                <thead>
+                    <tr style="background-color: #ddd;">
+                        <th align="left"> Field Name </th>
+                        <th align="left" width="20%"> Field Type </th>
+                        <th align="left" width="10%"> Required </th>
+                        <th align="left" width="10%"> Admin Search </th>
+                        <th align="left" width="10%">
+                            <div data-id="{$group.id}" class="newFieldButton button button-primary glm-button-small">Add a Custom Field</div>
+                        </th>
+                    </tr>
+                </thead>
+                <tbody class="sortable-fields">
+                    {if $group.custom_fields}
+                        {foreach $group.custom_fields as $field}
+                            <tr id="field_{$field.id}">
+                                <td class="glm-cfield-handle">
+                                {$field.field_name}
+                                </td>
+                                <td> {$field.field_type.name} </td>
+                                <td> {$field.required.name} </td>
+                                <td> {$field.admin_search.name} </td>
+                                <td align="right">
+                                    <div class="deleteFieldButton button button-secondary glm-button-small" data-fieldID="{$field.id}">Delete</div>
+
+                                    <div class="editField button button-secondary glm-button-small"
+                                        data-fieldID="{$field.id}"
+                                        data-fieldName="{$field.field_name|escape:'html'}"
+                                        data-fieldType="{$field.field_type.value|escape:'html'}"
+                                        data-adminSearch="{$field.admin_search.value}"
+                                        data-groupid="{$field.gid}"
+                                        data-required="{$field.required.value}"
+                                    >Edit</div>
+                                </td>
+                            </tr>
+                        {/foreach}
+                    {/if}
+                </tbody>
+            </table>
+        </div>
+        {/foreach}
+    {/if}
+</div>
 
 <script type="text/javascript">
 jQuery(document).ready(function($) {
 
+    $('#sortable-groups').sortable({
+        cursor: 'move',
+        update: function( event, ui ){
+            var sortedGroupIds = $(this).sortable('toArray');
+            console.log( 'GroupIds:', sortedGroupIds );
+
+            // Send new order via AJAX
+            var formData = {
+                'action':       'glm_members_admin_ajax',
+                'glm_action':   'glmCFields',
+                'option':       'sortGroupOrder',
+                'sortedIds':    sortedGroupIds
+            };
+            $.ajax({
+                type:       'POST',
+                url:        '{$ajaxUrl}',
+                data:       formData,
+                encode:     true,
+                dataType:   'text'
+            });
+        }
+    });
+    $('.sortable-fields').sortable({
+        cursor: 'move',
+        handle: '.glm-cfield-handle',
+        update: function( event, ui ){
+            var sortedIds = $(this).sortable('toArray');
+
+            // Send new order via AJAX
+            var formData = {
+                'action':       'glm_members_admin_ajax',
+                'glm_action':   'glmCFields',
+                'option':       'sortOrder',
+                'sortedIds':    sortedIds
+            };
+            $.ajax({
+                type:       'POST',
+                url:        '{$ajaxUrl}',
+                data:       formData,
+                encode:     true,
+                dataType:   'text'
+            });
+        }
+    });
     /*
      * Edit area tabs
      */
@@ -199,26 +238,78 @@ jQuery(document).ready(function($) {
         minWidth: 400,
         dialogClass: "glm-dialog-no-close"
     });
+    $('#cfGroupDialog').dialog({
+        autoOpen: false,
+        minWidth: 450,
+        dialogClass: 'glm-dialog-no-close'
+    });
     $("#deleteFieldDialog").dialog({
         autoOpen: false,
         minWidth: 400,
         dialogClass: "glm-dialog-no-close"
     });
+    $('#deleteGroupDialog').dialog({
+        autoOpen: false,
+        minWidth: 400,
+        dialogClass: "glm-dialog-no-close"
+    });
     $('#newFieldButton').click( function() {
         $("#newFieldDialog").dialog("open");
     });
+    $('#newGroupButton').click( function(){
+        $('#groupId').val('');
+        $('#groupName').val('');
+        $('#groupPublish').prop('checked', false);
+        $('#editGroupOption2').val('addNewGroup');
+        $('#cfGroupDialog').dialog('open');
+    });
+    $('.groupEdit').click(function(){
+        var groupId      = $(this).attr('data-groupId');
+        var groupName    = $(this).attr('data-groupName');
+        var groupPublish = $(this).attr('data-publish');
+
+        $('#groupId').val(groupId);
+        $('#groupName').val(groupName);
+        $('#editGroupOption2').val('updateNewGroup');
+        if ( groupPublish == '1' ) {
+            $('#groupPublish').prop('checked', true);
+        } else {
+            $('#groupPublish').prop('checked', false);
+        }
+        $('#cfGroupDialog').dialog('open');
+    });
+    $('.newFieldButton').click(function(){
+        var groupId = $(this).data('id');
+        console.log( 'groupid: ', groupId );
+        $('#editGroupID').val(groupId);
+        $('#editFieldOption2').val('addNew');
+        $('#editFieldSubmit').val('Add Field');
+        $("#editFieldDialog").dialog("open");
+        // Reset field for adding a new one
+        $('#editFieldID').val('');
+        $('#editFieldName').val('');
+        $('#editFieldType').val('');
+        $('#editAdminSearch').prop('checked', false);
+        $('#editRequired').prop('checked', false);
+
+    });
     $('.editField').click( function() {
         var fieldID     = $(this).attr('data-fieldID');
-        var fieldName   = $(this).text();
+        var fieldName   = $(this).attr('data-fieldName');
         var fieldType   = $(this).attr('data-fieldType');
         var adminSearch = $(this).attr('data-adminSearch');
         var required    = $(this).attr('data-required');
+        var groupID     = $(this).attr('data-groupid');
 
+        console.log( 'adminSearch', adminSearch );
         //console.log( fieldType );
 
         $('#editFieldID').val(fieldID);
         $('#editFieldName').val(fieldName.trim());
         $('#editFieldType').val(fieldType);
+        $('#editGroupID').val(groupID);
+        $('#editFieldOption2').val('update');
+        $('#editFieldSubmit').val('Update Field');
 
         if (adminSearch === '1') {
             //console.log('setting the checked to true');
@@ -242,6 +333,9 @@ jQuery(document).ready(function($) {
     $('#newFieldCancel').click( function() {
         $("#newFieldDialog").dialog("close");
     });
+    $('#newGroupCancel').click( function() {
+        $("#cfGroupDialog").dialog("close");
+    });
 
     var id = false;
     $('.deleteFieldButton').click( function() {
@@ -255,6 +349,18 @@ jQuery(document).ready(function($) {
     $('#deleteFieldCancel').click( function() {
         $("#deleteFieldDialog").dialog("close");
     });
+    var groupId = false;
+    $('.deletGroupButton').click( function() {
+        groupId = $(this).attr('data-groupId');
+        $("#deleteGroupDialog").dialog("open");
+    });
+    $('#deleteGroupConfirm').click( function() {
+        $("#deleteGroupDialog").dialog("close");
+        window.location.href = "{$thisUrl}?page={$thisPage}&glm_action={$glm_action}&option=customfields&option2=deleteGroup&id=" + groupId;
+    });
+    $('#deleteGroupCancel').click(function(){
+        $("#deleteGroupDialog").dialog("close");
+    });
 
     // Flash certain elements for a short time after display
     $(".glm-flash-updated").fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500);