From 2ca74b90e1640644fc7c7d301c1e82a018fbc066 Mon Sep 17 00:00:00 2001 From: Chuck Scott Date: Wed, 20 Apr 2016 17:03:21 -0400 Subject: [PATCH] Events near done, menu resturctur --- classes/data/dataMembers.php | 28 +- classes/data/settings/dataSettingsGeneral.php | 2 +- classes/data/settings/dataSettingsTerms.php | 2 +- classes/data/settings/dataSettingsTheme.php | 127 +++++ classes/glmPluginSupport.php | 82 ++-- controllers/admin.php | 6 +- css/admin.css | 3 + glm-member-db.php | 441 ------------------ index.php | 441 +++++++++++++++++- lib/GlmDataAbstract/DataAbstract.php | 85 +++- lib/GlmDataAbstract/documentation.odt | Bin 38584 -> 43521 bytes models/admin/management/theme.php | 184 ++++++++ models/admin/member/memberInfo.php | 1 + models/admin/members/list.php | 70 ++- setup/adminMenus.php | 16 +- ..._V1.1.2.sql => create_database_V1.1.4.sql} | 72 +-- setup/databaseScripts/dbVersions.php | 4 +- ...se_V1.1.2.sql => drop_database_V1.1.3.sql} | 2 +- .../update_database_V1.1.3.php | 17 + .../update_database_V1.1.3.sql | 11 + .../update_database_V1.1.4.sql | 31 ++ setup/validActions.php | 1 + views/admin/management/header.html | 3 +- views/admin/management/theme.html | 33 ++ views/admin/members/list.html | 110 +++-- 25 files changed, 1170 insertions(+), 602 deletions(-) create mode 100644 classes/data/settings/dataSettingsTheme.php delete mode 100644 glm-member-db.php create mode 100644 models/admin/management/theme.php rename setup/databaseScripts/{create_database_V1.1.2.sql => create_database_V1.1.4.sql} (86%) rename setup/databaseScripts/{drop_database_V1.1.2.sql => drop_database_V1.1.3.sql} (95%) create mode 100644 setup/databaseScripts/update_database_V1.1.3.php create mode 100644 setup/databaseScripts/update_database_V1.1.3.sql create mode 100644 setup/databaseScripts/update_database_V1.1.4.sql create mode 100644 views/admin/management/theme.html diff --git a/classes/data/dataMembers.php b/classes/data/dataMembers.php index ab547838..925ea7d1 100644 --- a/classes/data/dataMembers.php +++ b/classes/data/dataMembers.php @@ -304,7 +304,7 @@ class GlmDataMembers extends GlmDataAbstract { * @return array Array of Name and ID for all members * @access public */ - public function getSimpleMembersList($where = '') + public function getSimpleMembersList($where = '', $order = '', $fieldVals = true, $idField = 'id', $start = false, $limit = false) { // Save the current fields array and make a copy @@ -320,7 +320,7 @@ class GlmDataMembers extends GlmDataAbstract { 'active_id' => $fSave['active_id'] ); - $memberList = $this->getList($where); + $memberList = $this->getList($where, $order, $fieldVals, $idField, $start, $limit); // Restore the fields list $this->fields = $fSave; @@ -328,6 +328,30 @@ class GlmDataMembers extends GlmDataAbstract { return $memberList; } + /** + * Get ID/Name list + * + * @param string $where + * + * @return array ID/Name pairs + */ + public function getIdName($where = 'true') + { + $savedFields = $this->fields; + + $this->fields = array( + 'id' => $savedFields['id'], + 'name' => $savedFields['name'] + ); + + $r = $this->getList($where); + + $this->fields = $savedFields; + + return $r; + + } + } ?> \ No newline at end of file diff --git a/classes/data/settings/dataSettingsGeneral.php b/classes/data/settings/dataSettingsGeneral.php index c8032953..d90028c8 100644 --- a/classes/data/settings/dataSettingsGeneral.php +++ b/classes/data/settings/dataSettingsGeneral.php @@ -14,7 +14,7 @@ */ /** - * EventManagementDataSettingsGeneral class + * GlmDataSettingsGeneral class * * PHP version 5 * diff --git a/classes/data/settings/dataSettingsTerms.php b/classes/data/settings/dataSettingsTerms.php index 4a05295d..d379f960 100644 --- a/classes/data/settings/dataSettingsTerms.php +++ b/classes/data/settings/dataSettingsTerms.php @@ -14,7 +14,7 @@ */ /** - * EventManagementDataSettingsGeneral class + * GlmDataSettingsTerms class * * PHP version 5 * diff --git a/classes/data/settings/dataSettingsTheme.php b/classes/data/settings/dataSettingsTheme.php new file mode 100644 index 00000000..e9b19a04 --- /dev/null +++ b/classes/data/settings/dataSettingsTheme.php @@ -0,0 +1,127 @@ + + * @license http://www.gaslightmedia.com Gaslightmedia + * @release SVN: $Id: dataSettingsTerms.php,v 1.0 2011/01/25 19:31:47 cscott Exp $ + */ + +/** + * GlmDataSettingsTheme class + * + * PHP version 5 + * + * @category Data + * @package EventManagement + * @author Chuck Scott + * @license http://www.gaslightmedia.com Gaslightmedia + * @release SVN: $Id: dataMembers.php,v 1.0 2011/01/25 19:31:47 cscott + * Exp $ + */ +class GlmDataSettingsTheme extends GlmDataAbstract +{ + + /** + * WordPress Database Object + * + * @var $wpdb + * @access public + */ + public $wpdb; + /** + * Plugin Configuration Data + * + * @var $config + * @access public + */ + public $config; + /** + * Field definitions + * + * @var $ini + * @access public + */ + public $table; + + /** + * Field definitions + * + * 'type' is type of field as defined by the application + * text Regular text field + * pointer Pointer to an entry in another table + * 'filters' is the filter name for a particular filter ID in PHP filter + * functions + * See PHP filter_id() + * + * 'use' is when to use the field + * l = List + * g = Get + * n = New + * i = Insert + * e = Edit + * u = Update + * d = Delete + * a = All + * + * @var $ini + * @access public + */ + public $fields = false; + + /** + * Constructor + * + * @param object $d + * database connection + * + * @return void + * @access public + */ + function __construct ($wpdb, $config) + { + + // If this class is not being extended along with existing $wpdb and $config + if (!$this->wpdb) { + + // Save WordPress Database object + $this->wpdb = $wpdb; + + // Save plugin configuration object + $this->config = $config; + + } + + /* + * Table Name + */ + $this->table = GLM_MEMBERS_PLUGIN_DB_PREFIX . 'settings_theme'; + + /* + * Table Data Fields + */ + $this->fields = array( + + 'id' => array( + 'field' => 'id', + 'type' => 'integer', + 'view_only' => true, + 'use' => 'a' + ) + + ); + + if (is_admin() && GLM_MEMBERS_PLUGIN_ADMIN_DEBUG_VERBOSE) { + glmMembersAdmin::addNotice($this->fields, 'DataBlock', 'Table Fields: '.$this->table); + } + + } +} + +?> \ No newline at end of file diff --git a/classes/glmPluginSupport.php b/classes/glmPluginSupport.php index 34a01d6c..d4fa9866 100644 --- a/classes/glmPluginSupport.php +++ b/classes/glmPluginSupport.php @@ -184,7 +184,15 @@ class GlmPluginSupport // Has this plug-in registered any database versions if ($a['database']) { - $dbVersion = get_option($a['database']['dbActiveVersionOption']); + if (!isset($a['database']['dbActiveVersionOption'])) { + $startupNotices .= '

There is missing database information for plugin/add-on '.$a['name']."." + ."
Incorrect plugin/add-on configuration is suspected. Check add-on registration data array for this add-on.

"; + $db_error = true; + $dbVersion = false; + + } else { + $dbVersion = get_option($a['database']['dbActiveVersionOption']); + } /* // Do a sanity check on the database version @@ -248,40 +256,60 @@ class GlmPluginSupport // Get current database version $dbVersion = $a['database']['dbCurrentVersion']; - // Read in Database creation script - $sqlFile = $a['database']['dbScriptPath'].'/create_database_V'.$dbVersion.'.sql'; - $sql = file_get_contents($sqlFile); + // Check for a version string + if (trim($dbVersion == '')) { - // Replace {prefix} with table name prefix - $sql = str_replace('{prefix}', $a['database']['dbPrefix'], $sql); + $startupNotices .= '

Failure installing database tables for the '.$a['name'] + .'
No database version set:

'; - // Split script into separate queries by looking for lines with only "----" - $queries = preg_split('/^----$/m', $sql); + } else { - // Try executing all queries to build database - do { - $q = current($queries); - $this->wpdb->query($q); - $queryError = $this->wpdb->last_error; - } while ($queryError == '' && next($queries)); + // Create name for Database creation script + $sqlFile = $a['database']['dbScriptPath'].'/create_database_V'.$dbVersion.'.sql'; - // If there were no errors - if (trim($queryError) == '') { + // Make sure it exists + if (!is_file($sqlFile)) { - // Notify the user that the database has been installed - $startupNotices .= '

New database tables installed for the '.$a['name'].' plugin.

'; + $startupNotices .= '

Failure installing database tables for the '.$a['name'] + .'
Database Creation Script not found: '.$sqlFile.'

'; - // Save the version of the installed database - update_option($a['database']['dbActiveVersionOption'], $dbVersion); + } else { + $sql = file_get_contents($sqlFile); - // Indicate that we were successfull - $db_setup_status = true; + // Replace {prefix} with table name prefix + $sql = str_replace('{prefix}', $a['database']['dbPrefix'], $sql); - } else { - $startupNotices .= '

Failure installing database tables for the '.$a['name'] - .'
Database Installation Error: '.print_r($queryError,1) - .'

'.$q.'

'; - } + // Split script into separate queries by looking for lines with only "----" + $queries = preg_split('/^----$/m', $sql); + + // Try executing all queries to build database + do { + $q = current($queries); + $this->wpdb->query($q); + $queryError = $this->wpdb->last_error; + } while ($queryError == '' && next($queries)); + + // If there were no errors + if (trim($queryError) == '') { + + // Notify the user that the database has been installed + $startupNotices .= '

New database tables installed for the '.$a['name'].' plugin.

'; + + // Save the version of the installed database + update_option($a['database']['dbActiveVersionOption'], $dbVersion); + + // Indicate that we were successfull + $db_setup_status = true; + + } else { + $startupNotices .= '

Failure installing database tables for the '.$a['name'] + .'
Database Installation Error: '.print_r($queryError,1) + .'

'.$q.'

'; + } + + } // db creation file check + + } // db version check // Otherwise, check if we need to update the database } elseif ($dbVersion != $a['database']['dbCurrentVersion']) { diff --git a/controllers/admin.php b/controllers/admin.php index f1496584..fab3f2c4 100644 --- a/controllers/admin.php +++ b/controllers/admin.php @@ -696,7 +696,11 @@ class glmMembersAdmin extends GlmPluginSupport do { if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { - $this->addNotice("Requested Action: Menu item = $menuItem, Action = $action", 'Process'); + $option = ''; + if (isset($_REQUEST['option'])) { + $option = ', Option = '.$_REQUEST['option']; + } + $this->addNotice("Requested Action: Menu item = $menuItem, Action = $action".$option, 'Process'); } $modelRedirect = false; diff --git a/css/admin.css b/css/admin.css index 99a94723..5c3d988b 100644 --- a/css/admin.css +++ b/css/admin.css @@ -41,6 +41,9 @@ .glm-clear { clear: both; } +.glm-nowrap { + white-space: nowrap; +} .glm-hidden { display: none !important; } diff --git a/glm-member-db.php b/glm-member-db.php deleted file mode 100644 index 704dab0d..00000000 --- a/glm-member-db.php +++ /dev/null @@ -1,441 +0,0 @@ - - * @license http://www.gaslightmedia.com Gaslightmedia - * @version 1.0.47 - */ - -/* - * Plugin and Database Versions - * - * Database updates for the main plugin and add-ons must also be - * listed in the /setup/databaseScripts/dbVersions.php file of - * the corresponding plugin/add-on. The table in that file - * determines which version is installed for a new activation - * and which update scrips are run to bring the database up to - * the current version. - * - * We check the plugin version stored in the WordPress option below - * so that we're sure the other add-ons see an up to date - * version from this plugin. - * - */ - -define('GLM_MEMBERS_PLUGIN_VERSION', '1.0.58'); -define('GLM_MEMBERS_PLUGIN_DB_VERSION', '1.1.2'); - -// Check if plugin version is not current in WordPress option and if needed updated it -if (GLM_MEMBERS_PLUGIN_VERSION != get_option('glmMembersDatabasePluginVersion')) { - update_option('glmMembersDatabasePluginVersion', GLM_MEMBERS_PLUGIN_VERSION); -} - -/* - * Copyright 2014 Charles Scott (email : cscott@gaslightmedia.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/** - * ******************************************************************************* - * - * *** Directory and File Structure *** - * - * index.php - * - * Index file for this plugin. All plugin processing starts here. (See - * "Process Flow" below.) - * - * controllers - * - * Directory containing any controllers. Typically there - * would be admin and front controllers in this directory. - * These controllers do the general setup for the plugin, - * determine and call the appropriate model, determine the - * appropriate view, then merge any data returned by the model - * with the view and output the result as appropriate. - * - * When executed, a model may determine that it cannot handle - * the current request and return such a notice to the controller - * possibly with a suggested model to execute. Models may also - * return a desired view back to the controller based on the - * result of processing, but should do so in a generic way so - * as to permit multi-lingual output and use of multiple "skins" - * (but not to the exception of appropriate use of WordPress - * Themes). - * - * css - * - * Directory containing any css files specific to this plugin. - * The use of additional styling should be kept to a minimum - * so as to not interfere with the application of WordPress - * default styling and Themes. - * - * js - * - * Directory containing any JAVAscript files specific to this - * Plugin. This directory should be reserved for general script - * files that provide functionality that can be used in various - * views. Any JAVAscript that is specific to a view should be - * located along with the associated view as it is logically - * part of the view. - * - * lib - * - * Directory containing any class or function libraries that - * are used generally by this plugin. Any class or other code - * that is specific to a particular model should be located - * in, or along with, that model since it is logically - * associated only with that model. - * - * misc - * - * Directory containing ancillary directories and files. This - * might be used for things like cach directories. An example - * might be the "smarty" directory for Smaarty Templates. - * - * models - * - * Directory containing model files that execute a specific - * process in this plugin. If this is a simple plugin, then - * the model files can be placed directly in this directory. - * If it's a more complex plugin, then there should be sub- - * directories for various groupings of related model files. - * - * An individual model may consist of a grouping of files, - * such as additional class files, that are specific only to - * that model. In that case, these should be located in a - * subdirectory under where the model file called by the - * controller is located and that directory should be named - * so as to be obviously associated with that model. - * - * There are three special files in the models directory. These - * are activate.php, deactivate.php, and uninstall.php. These - * are called via hooks setup in this file and should always - * be here. If they do not provide any real functionality, they - * should at least be a shell that can be called for those - * situations. - * - * views - * - * Directory containing view files for producing output upon - * request of a model file in the models directory. If this - * is a simply plugin, then the view files can be placed - * directly in this directory. If it's a more complex plugin, - * then there should be sub-directories for the various - * groupings of related view files. If using sub-directories, - * those should generally match the associated model directories. - * It may also be wise to use separate front and admin - * directories under views to keep things organized. - * - * Additionally, views may be grouped in such a way that they - * support the selection of various "skins" that output in - * different ways, although any styling should be provided by - * WordPress Themes or use default WordPress styling. - * - * *** Process Flow *** - * - * WordPress calls the plugin index file. All plugin processing starts here. - * - * The plugin index file performs the following operations ... - * - Sets-up any required plugin-wide defines and data - * - Instatiates any plugin-wide classes and objects - * - Sets-up any plugin-wide WordPress hooks - * - Determines which controller is to be executed - * - Executes the selected controller - * - * The selected controller performs the following operations ... - * - Sets-up any controller specific defines and data - * - Instatiates any controller specific classes and objects - * - Sets-up any controller specific WordPress hooks - * - Determines which model process is to be executed - * - Executes the selected model - * - * The selected model performs the following operations ... - * - Sets-up any model specific defines and data - * - Instatiates any model specific classes and objects - * - Sets-up any model specific WordPress hooks - * - Performs any specific processing required of the model - * - Determines which view is to be used to generate output - * - Generates output based on model data and the selected view - * - * WordPress wraps everything up - * - * ******************************************************************************** - */ - -// Check that we're being called by WordPress. -if (!defined('ABSPATH')) { - die("Please do not call this code directly!"); -} - -/* - * - * Some initial setup and tests - * - */ - -$startupNotices = ''; - -// Get standard defined parameters -require_once('defines.php'); - -// Get configuration -require_once('config.php'); - -// Try to set the DB version option to false (new plugin) - If it's already set this won't do anything. -add_option('glmMembersDatabaseDbVersion', false); - -/* - * - * Hook in add-ons and integrate their actions - * - * Each add-on should add an array element to the supplied array using the following structure. - * - * $addOns[{slug for add-on}] = array( - * 'dir' => {path to add-on directory}, - * 'name' => {text name for add-on}, - * 'short_name' => {an abreviated name for the add-on}, - * 'slug' => {slug for add-on}, - * 'config' => {optional array of config data to be merged into $config}, - * 'actions' => array( - * 'adminActions => array( - * '{menu name}' => array( - * '{action}' => '{add-on slug}', - * ... additional actions ... - * ), - * ... additional menu names ... - * 'frontActions' => array( - * '{page}' => array( - * '{action name}' => '{add-on slug}', - * ... addional actions --- - * ), - * ... additional pages ... - * ) - * ), - * 'shortcodes' => array( - * '{shortcode} => array( - * 'plugin' => {plugin slug}, - * 'menu' => '{menu}, - * 'action' => '{action}', - * 'table' => {table name where default values for 'attributes' are stored}, - * 'adttributes' => array({array of attributes that might be in the shortcode}) - * ), - * ... additional shortcodes ... - * ), - * // if the add-on is adding database tables use this, otherwise FALSE - * 'database' => array( - * 'dbPrefix' => '{database prefix to use}', - * 'dbCurrentVersion' => '{current add-on database version number}', - * 'dbActiveVersionOption' => '{option name used to get current active database version}', - * 'dbScriptPath' => '{full path to add-on database scripts directory}', - * 'dbVersions' => array( - * '{version#}' => array('version' => '{version#}', 'tables' => {# of tables}, - * ... additional versions in order of version number - highest is last --- - * ) - * - * ) - * - * Note that the database prefix must not start with 'glm_members_'. It can however be something - * like 'glm_membersPackaging_', otherwise the tables will be counted along with those of the main - * plugin and database checks will fail. - * - * Also note that the default array includes information on the main member db plugin. This is - * done for consistency, particularly for database maintenance - */ -require_once(GLM_MEMBERS_PLUGIN_SETUP_PATH.'/validActions.php'); -require_once(GLM_MEMBERS_PLUGIN_SETUP_PATH.'/shortcodes.php'); -require_once(GLM_MEMBERS_PLUGIN_DB_SCRIPTS.'/dbVersions.php'); -$config['addOns'] = array( - GLM_MEMBERS_PLUGIN_SLUG => array( - 'dir' => GLM_MEMBERS_PLUGIN_PATH, - 'name' => GLM_MEMBERS_PLUGIN_NAME, - 'short_name' => GLM_MEMBERS_PLUGIN_SHORT_NAME, - 'slug' => GLM_MEMBERS_PLUGIN_SLUG, - 'actions' => $glmMembersValidActions, - 'shortcodes' => $glmMembersShortcodes, - 'shortcodesDescription' => $glmMembersShortcodesDescription, - 'database' => array( - 'dbPrefix' => GLM_MEMBERS_PLUGIN_DB_PREFIX, - 'dbCurrentVersion' => GLM_MEMBERS_PLUGIN_DB_VERSION, - 'dbActiveVersionOption' => GLM_MEMBERS_PLUGIN_ACTIVE_DB_OPTION, - 'dbScriptPath' => GLM_MEMBERS_PLUGIN_DB_SCRIPTS, - 'dbVersions' => $glmMembersDbVersions - ) - ) -); - -// Create hook for registering add-on plugins and their features -$config['addOns'] = apply_filters('glm-member-db-register-addon', $config['addOns']); - -// If any add-ons have registered -$config['validActions'] = array(); -$config['shortcodes'] = array(); -if (count($config['addOns']) > 0) { - - // For each add-on that's registered - foreach ($config['addOns'] as $a) { - - // Add their valid actions to the main validActions config array - if (isset($a['actions'])) { - $config['validActions'] = array_merge_recursive($config['validActions'], $a['actions']); - } - - // Add their shortcodes to the main shortcodes config array - if (isset($a['shortcodes'])) { - $config['shortcodes'] = array_merge_recursive($config['shortcodes'], $a['shortcodes']); - } - - // If the add-on has supplied additional config parameters when registering - if (isset($a['config'])) { - $config = array_merge_recursive($config, $a['config']); - } - - // If the add-on has additional config parameters in a plugin.ini file - $iniFile = GLM_MEMBERS_WORDPRESS_PLUGIN_PATH.'/'.$a['slug'].'/config/plugin.ini'; - if (isset($iniFile)) { - - // Parse the add-on's configuration file - $addOnIni = parse_ini_file($iniFile); - - // Replace parameters that are in the theme ini file - $config = array_replace($config, $addOnIni); - - } - } -} - -/* - * - * Activate and Deactivate hooks - * - */ - -// Activate -function glmMembersPluginActivate () -{ - global $wpdb, $config; - require_once (GLM_MEMBERS_PLUGIN_PATH . '/activate.php'); - new glmMembersPluginActivate($wpdb, $config); -} -register_activation_hook(__FILE__, 'glmMembersPluginActivate'); - -// Deactivate -function glmMembersPluginDeactivate () -{ - global $wpdb, $config; - require_once (GLM_MEMBERS_PLUGIN_PATH . '/deactivate.php'); - $x = new glmMembersPluginDeactivate($wpdb, $config); - return false; -} -register_deactivation_hook(__FILE__, 'glmMembersPluginDeactivate'); - -/* - * - * Load any other common files needed - * - */ - -// Load data abstract -require_once(GLM_MEMBERS_PLUGIN_LIB_PATH.'/GlmDataAbstract/DataAbstract.php'); - -// Load glmPluginSupport class -require_once (GLM_MEMBERS_PLUGIN_PATH . '/classes/glmPluginSupport.php'); - -// Load Smarty Template Support -require_once (GLM_MEMBERS_PLUGIN_PATH . '/lib/smartyTemplateSupport.php'); - -/* - * Hook through which an add-on may supply additional logged in user information and - * have that data stored in the config array. Typically it would be the - * glm-members-db-contacts add-on supplying the information. - * - * This hook provides default data with the current WordPress user 'data' object - * as a 'wpUser' sub-array if a WordPress user is logged in. If not 'wpUser' will - * be false. The supplied data is the basic information on the WordPress user - * provided by the 'data' object from wp_get_current_user(). - * - * To permit more than one routine to access this filter and therefore to supply - * additional information on the logged in user, code may merge it's own data or may - * add another sub-array containing user information specific to an add-on (i.e. - * contacts add-on might supply a 'contactUser' sub-array). - * - */ -if (function_exists('is_user_logged_in')) { - $config['loggedInUser'] = array( - 'wpUser' => false - ); - if (is_user_logged_in()) { - $config['loggedInUser']['wpUser'] = (array) wp_get_current_user()->data; - } - $config['loggedInUser'] = apply_filters('glm_members_current_logged_in_user', $config['loggedInUser']); -} - -/* - * - * Determine which controller to load - * - */ - -// If the call to this code is to display a separate debug window -if (isset($_REQUEST['glmDebugWindow']) && $_REQUEST['glmDebugWindow'] == true) { - glmMembersAdminNotices(true); - exit; - -// Otherwise select appropriate controller -} elseif (is_admin()) { - require_once (GLM_MEMBERS_PLUGIN_PATH . '/controllers/admin.php'); - new glmMembersAdmin($wpdb, $config); -} else { - require_once (GLM_MEMBERS_PLUGIN_PATH . '/controllers/front.php'); - new glmMembersFront($wpdb, $config); -} - -/* - * Display any notices? - */ - -// Display any notices that need to be displayed at the top of the admin content area -function glmMembersWordpressAdminNotices() { - global $startupNotices; - echo '
'.GLM_MEMBERS_PLUGIN_NAME.' Plugin Notice
'.$startupNotices.'
'; -} -if ($startupNotices != '') { - add_action('admin_notices','glmMembersWordpressAdminNotices'); -} - -// If not doing debug, then clear all of the debug messages -if (!GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { - delete_option('glmMembersAdminNoticeAlerts'); - delete_option('glmMembersAdminNotices'); - delete_option('glmMembersAdminNoticeProcess'); - delete_option('glmMembersAdminNoticeDataBlocks'); -} - diff --git a/index.php b/index.php index 7e914158..b9ccc3da 100644 --- a/index.php +++ b/index.php @@ -1,2 +1,441 @@ + * @license http://www.gaslightmedia.com Gaslightmedia + * @version 1.0.47 + */ + +/* + * Plugin and Database Versions + * + * Database updates for the main plugin and add-ons must also be + * listed in the /setup/databaseScripts/dbVersions.php file of + * the corresponding plugin/add-on. The table in that file + * determines which version is installed for a new activation + * and which update scrips are run to bring the database up to + * the current version. + * + * We check the plugin version stored in the WordPress option below + * so that we're sure the other add-ons see an up to date + * version from this plugin. + * + */ + +define('GLM_MEMBERS_PLUGIN_VERSION', '1.0.58'); +define('GLM_MEMBERS_PLUGIN_DB_VERSION', '1.1.4'); + +// Check if plugin version is not current in WordPress option and if needed updated it +if (GLM_MEMBERS_PLUGIN_VERSION != get_option('glmMembersDatabasePluginVersion')) { + update_option('glmMembersDatabasePluginVersion', GLM_MEMBERS_PLUGIN_VERSION); +} + +/* + * Copyright 2014 Charles Scott (email : cscott@gaslightmedia.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * ******************************************************************************* + * + * *** Directory and File Structure *** + * + * index.php + * + * Index file for this plugin. All plugin processing starts here. (See + * "Process Flow" below.) + * + * controllers + * + * Directory containing any controllers. Typically there + * would be admin and front controllers in this directory. + * These controllers do the general setup for the plugin, + * determine and call the appropriate model, determine the + * appropriate view, then merge any data returned by the model + * with the view and output the result as appropriate. + * + * When executed, a model may determine that it cannot handle + * the current request and return such a notice to the controller + * possibly with a suggested model to execute. Models may also + * return a desired view back to the controller based on the + * result of processing, but should do so in a generic way so + * as to permit multi-lingual output and use of multiple "skins" + * (but not to the exception of appropriate use of WordPress + * Themes). + * + * css + * + * Directory containing any css files specific to this plugin. + * The use of additional styling should be kept to a minimum + * so as to not interfere with the application of WordPress + * default styling and Themes. + * + * js + * + * Directory containing any JAVAscript files specific to this + * Plugin. This directory should be reserved for general script + * files that provide functionality that can be used in various + * views. Any JAVAscript that is specific to a view should be + * located along with the associated view as it is logically + * part of the view. + * + * lib + * + * Directory containing any class or function libraries that + * are used generally by this plugin. Any class or other code + * that is specific to a particular model should be located + * in, or along with, that model since it is logically + * associated only with that model. + * + * misc + * + * Directory containing ancillary directories and files. This + * might be used for things like cach directories. An example + * might be the "smarty" directory for Smaarty Templates. + * + * models + * + * Directory containing model files that execute a specific + * process in this plugin. If this is a simple plugin, then + * the model files can be placed directly in this directory. + * If it's a more complex plugin, then there should be sub- + * directories for various groupings of related model files. + * + * An individual model may consist of a grouping of files, + * such as additional class files, that are specific only to + * that model. In that case, these should be located in a + * subdirectory under where the model file called by the + * controller is located and that directory should be named + * so as to be obviously associated with that model. + * + * There are three special files in the models directory. These + * are activate.php, deactivate.php, and uninstall.php. These + * are called via hooks setup in this file and should always + * be here. If they do not provide any real functionality, they + * should at least be a shell that can be called for those + * situations. + * + * views + * + * Directory containing view files for producing output upon + * request of a model file in the models directory. If this + * is a simply plugin, then the view files can be placed + * directly in this directory. If it's a more complex plugin, + * then there should be sub-directories for the various + * groupings of related view files. If using sub-directories, + * those should generally match the associated model directories. + * It may also be wise to use separate front and admin + * directories under views to keep things organized. + * + * Additionally, views may be grouped in such a way that they + * support the selection of various "skins" that output in + * different ways, although any styling should be provided by + * WordPress Themes or use default WordPress styling. + * + * *** Process Flow *** + * + * WordPress calls the plugin index file. All plugin processing starts here. + * + * The plugin index file performs the following operations ... + * - Sets-up any required plugin-wide defines and data + * - Instatiates any plugin-wide classes and objects + * - Sets-up any plugin-wide WordPress hooks + * - Determines which controller is to be executed + * - Executes the selected controller + * + * The selected controller performs the following operations ... + * - Sets-up any controller specific defines and data + * - Instatiates any controller specific classes and objects + * - Sets-up any controller specific WordPress hooks + * - Determines which model process is to be executed + * - Executes the selected model + * + * The selected model performs the following operations ... + * - Sets-up any model specific defines and data + * - Instatiates any model specific classes and objects + * - Sets-up any model specific WordPress hooks + * - Performs any specific processing required of the model + * - Determines which view is to be used to generate output + * - Generates output based on model data and the selected view + * + * WordPress wraps everything up + * + * ******************************************************************************** + */ + +// Check that we're being called by WordPress. +if (!defined('ABSPATH')) { + die("Please do not call this code directly!"); +} + +/* + * + * Some initial setup and tests + * + */ + +$startupNotices = ''; + +// Get standard defined parameters +require_once('defines.php'); + +// Get configuration +require_once('config.php'); + +// Try to set the DB version option to false (new plugin) - If it's already set this won't do anything. +add_option('glmMembersDatabaseDbVersion', false); + +/* + * + * Hook in add-ons and integrate their actions + * + * Each add-on should add an array element to the supplied array using the following structure. + * + * $addOns[{slug for add-on}] = array( + * 'dir' => {path to add-on directory}, + * 'name' => {text name for add-on}, + * 'short_name' => {an abreviated name for the add-on}, + * 'slug' => {slug for add-on}, + * 'config' => {optional array of config data to be merged into $config}, + * 'actions' => array( + * 'adminActions => array( + * '{menu name}' => array( + * '{action}' => '{add-on slug}', + * ... additional actions ... + * ), + * ... additional menu names ... + * 'frontActions' => array( + * '{page}' => array( + * '{action name}' => '{add-on slug}', + * ... addional actions --- + * ), + * ... additional pages ... + * ) + * ), + * 'shortcodes' => array( + * '{shortcode} => array( + * 'plugin' => {plugin slug}, + * 'menu' => '{menu}, + * 'action' => '{action}', + * 'table' => {table name where default values for 'attributes' are stored}, + * 'adttributes' => array({array of attributes that might be in the shortcode}) + * ), + * ... additional shortcodes ... + * ), + * // if the add-on is adding database tables use this, otherwise FALSE + * 'database' => array( + * 'dbPrefix' => '{database prefix to use}', + * 'dbCurrentVersion' => '{current add-on database version number}', + * 'dbActiveVersionOption' => '{option name used to get current active database version}', + * 'dbScriptPath' => '{full path to add-on database scripts directory}', + * 'dbVersions' => array( + * '{version#}' => array('version' => '{version#}', 'tables' => {# of tables}, + * ... additional versions in order of version number - highest is last --- + * ) + * + * ) + * + * Note that the database prefix must not start with 'glm_members_'. It can however be something + * like 'glm_membersPackaging_', otherwise the tables will be counted along with those of the main + * plugin and database checks will fail. + * + * Also note that the default array includes information on the main member db plugin. This is + * done for consistency, particularly for database maintenance + */ +require_once(GLM_MEMBERS_PLUGIN_SETUP_PATH.'/validActions.php'); +require_once(GLM_MEMBERS_PLUGIN_SETUP_PATH.'/shortcodes.php'); +require_once(GLM_MEMBERS_PLUGIN_DB_SCRIPTS.'/dbVersions.php'); +$config['addOns'] = array( + GLM_MEMBERS_PLUGIN_SLUG => array( + 'dir' => GLM_MEMBERS_PLUGIN_PATH, + 'name' => GLM_MEMBERS_PLUGIN_NAME, + 'short_name' => GLM_MEMBERS_PLUGIN_SHORT_NAME, + 'slug' => GLM_MEMBERS_PLUGIN_SLUG, + 'actions' => $glmMembersValidActions, + 'shortcodes' => $glmMembersShortcodes, + 'shortcodesDescription' => $glmMembersShortcodesDescription, + 'database' => array( + 'dbPrefix' => GLM_MEMBERS_PLUGIN_DB_PREFIX, + 'dbCurrentVersion' => GLM_MEMBERS_PLUGIN_DB_VERSION, + 'dbActiveVersionOption' => GLM_MEMBERS_PLUGIN_ACTIVE_DB_OPTION, + 'dbScriptPath' => GLM_MEMBERS_PLUGIN_DB_SCRIPTS, + 'dbVersions' => $glmMembersDbVersions + ) + ) +); + +// Create hook for registering add-on plugins and their features +$config['addOns'] = apply_filters('glm-member-db-register-addon', $config['addOns']); + +// If any add-ons have registered +$config['validActions'] = array(); +$config['shortcodes'] = array(); +if (count($config['addOns']) > 0) { + + // For each add-on that's registered + foreach ($config['addOns'] as $a) { + + // Add their valid actions to the main validActions config array + if (isset($a['actions'])) { + $config['validActions'] = array_merge_recursive($config['validActions'], $a['actions']); + } + + // Add their shortcodes to the main shortcodes config array + if (isset($a['shortcodes'])) { + $config['shortcodes'] = array_merge_recursive($config['shortcodes'], $a['shortcodes']); + } + + // If the add-on has supplied additional config parameters when registering + if (isset($a['config'])) { + $config = array_merge_recursive($config, $a['config']); + } + + // If the add-on has additional config parameters in a plugin.ini file + $iniFile = GLM_MEMBERS_WORDPRESS_PLUGIN_PATH.'/'.$a['slug'].'/config/plugin.ini'; + if (isset($iniFile)) { + + // Parse the add-on's configuration file + $addOnIni = parse_ini_file($iniFile); + + // Replace parameters that are in the theme ini file + $config = array_replace($config, $addOnIni); + + } + } +} + +/* + * + * Activate and Deactivate hooks + * + */ + +// Activate +function glmMembersPluginActivate () +{ + global $wpdb, $config; + require_once (GLM_MEMBERS_PLUGIN_PATH . '/activate.php'); + new glmMembersPluginActivate($wpdb, $config); +} +register_activation_hook(__FILE__, 'glmMembersPluginActivate'); + +// Deactivate +function glmMembersPluginDeactivate () +{ + global $wpdb, $config; + require_once (GLM_MEMBERS_PLUGIN_PATH . '/deactivate.php'); + $x = new glmMembersPluginDeactivate($wpdb, $config); + return false; +} +register_deactivation_hook(__FILE__, 'glmMembersPluginDeactivate'); + +/* + * + * Load any other common files needed + * + */ + +// Load data abstract +require_once(GLM_MEMBERS_PLUGIN_LIB_PATH.'/GlmDataAbstract/DataAbstract.php'); + +// Load glmPluginSupport class +require_once (GLM_MEMBERS_PLUGIN_PATH . '/classes/glmPluginSupport.php'); + +// Load Smarty Template Support +require_once (GLM_MEMBERS_PLUGIN_PATH . '/lib/smartyTemplateSupport.php'); + +/* + * Hook through which an add-on may supply additional logged in user information and + * have that data stored in the config array. Typically it would be the + * glm-members-db-contacts add-on supplying the information. + * + * This hook provides default data with the current WordPress user 'data' object + * as a 'wpUser' sub-array if a WordPress user is logged in. If not 'wpUser' will + * be false. The supplied data is the basic information on the WordPress user + * provided by the 'data' object from wp_get_current_user(). + * + * To permit more than one routine to access this filter and therefore to supply + * additional information on the logged in user, code may merge it's own data or may + * add another sub-array containing user information specific to an add-on (i.e. + * contacts add-on might supply a 'contactUser' sub-array). + * + */ +if (function_exists('is_user_logged_in')) { + $config['loggedInUser'] = array( + 'wpUser' => false + ); + if (is_user_logged_in()) { + $config['loggedInUser']['wpUser'] = (array) wp_get_current_user()->data; + } + $config['loggedInUser'] = apply_filters('glm_members_current_logged_in_user', $config['loggedInUser']); +} + +/* + * + * Determine which controller to load + * + */ + +// If the call to this code is to display a separate debug window +if (isset($_REQUEST['glmDebugWindow']) && $_REQUEST['glmDebugWindow'] == true) { + glmMembersAdminNotices(true); + exit; + +// Otherwise select appropriate controller +} elseif (is_admin()) { + require_once (GLM_MEMBERS_PLUGIN_PATH . '/controllers/admin.php'); + new glmMembersAdmin($wpdb, $config); +} else { + require_once (GLM_MEMBERS_PLUGIN_PATH . '/controllers/front.php'); + new glmMembersFront($wpdb, $config); +} + +/* + * Display any notices? + */ + +// Display any notices that need to be displayed at the top of the admin content area +function glmMembersWordpressAdminNotices() { + global $startupNotices; + echo '
'.GLM_MEMBERS_PLUGIN_NAME.' Plugin Notice
'.$startupNotices.'
'; +} +if ($startupNotices != '') { + add_action('admin_notices','glmMembersWordpressAdminNotices'); +} + +// If not doing debug, then clear all of the debug messages +if (!GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { + delete_option('glmMembersAdminNoticeAlerts'); + delete_option('glmMembersAdminNotices'); + delete_option('glmMembersAdminNoticeProcess'); + delete_option('glmMembersAdminNoticeDataBlocks'); +} + diff --git a/lib/GlmDataAbstract/DataAbstract.php b/lib/GlmDataAbstract/DataAbstract.php index e39de524..d2486e8e 100755 --- a/lib/GlmDataAbstract/DataAbstract.php +++ b/lib/GlmDataAbstract/DataAbstract.php @@ -1879,12 +1879,6 @@ abstract class GlmDataAbstract $time = date( $f['time_format'], strtotime($time)); } -/* NEED TO LOOK AT THIS - if (!$this->optionIncludeSelectListData && !$forEdit) { - $time_list = false; - } -*/ - $r = array( 'time' => $time, 'time_list' => $time_list @@ -1907,8 +1901,26 @@ abstract class GlmDataAbstract $time = ''; + // Get default - Needs to be in format "00:00:00" 24 hour time if (isset($f['default'])) { - $time = $f['default']; + $def = explode(':', $f['default']); + $ampm = 'AM'; + if (isset($def[0])) { + if ($def[0] >= 12) { + $ampm = 'PM'; + } + if ($def[0] > 12) { + $hour = $def[0] -12; + } else { + $hour = $def[0]; + } + } + if (isset($def[1])) { + $min = $def[1]; + } else { + $min = 00; + } + } // Otherwise we should be getting input from the user @@ -3090,7 +3102,7 @@ abstract class GlmDataAbstract * @return array Array containing results * @access public */ - public function getList($where = '', $order = '', $fieldVals = true, $idField = 'id') + public function getList($where = '', $order = '', $fieldVals = true, $idField = 'id', $start = false, $limit = false) { // NOTE: $fieldVals not yet implemented @@ -3111,7 +3123,14 @@ abstract class GlmDataAbstract $sql .= "ORDER BY $order "; } -// echo "
".print_r($sql,1)."
"; + + // If $start and $limit, we're doing paging + $paging = false; + if ($start !== false && $limit > 0) { + $sql .= "limit ".($start-1).", $limit"; + $paging = true; + } + if (is_admin() && GLM_MEMBERS_PLUGIN_ADMIN_DEBUG_VERBOSE && class_exists('glmMembersAdmin')) { $this->addDataAbstractNotice($sql, 'DataBlock', "DataAbstract - getList() query"); } @@ -3121,8 +3140,10 @@ abstract class GlmDataAbstract return false; } - // Process field output data for each result - use ID field as key + // Setup result array $newList = array(); + + // Process field output data for each result - use ID field as key while (list($k, $v) = each($list)) { $newList[$v['id']] = $this->processOutputData($v, 'l'); } @@ -3131,6 +3152,20 @@ abstract class GlmDataAbstract $this->addDataAbstractNotice($newList, 'DataBlock', "getList() data"); } + + // If we're doing paging, return that data along with the list + if ($paging) { + $c = count($list); + return array( + 'start' => $start, + 'limit' => $limit, + 'returned' => $c, + 'last' => $start + count($list) - 1, + 'list' => $newList + ); + } + + // Since we're not doing paging, return the list only return $newList; } @@ -3140,29 +3175,37 @@ abstract class GlmDataAbstract * @return void * @access public */ - public function getEntry($id, $idfield = 'id', $where = '', $fieldVals = false) + public function getEntry($id, $idfield = 'id', $where = 'true', $fieldVals = false) { - if ($id-0 == 0) { -// echo "DataAbstract.php - getEntry() called with invalid ID"; - return false; - } + // If there's an ID supplied, add that to the query + $idWhere = ''; + if ($id-0 > 0) { + $idWhere = "AND $idfield = $id"; + } - $this->buildFieldsList('g'); + // If there's no ID supplied and there's no where clause, what are we doing here? + if ($idWhere == '' and $where == 'true') { + return false; + } + + $this->buildFieldsList('g'); + // Get only the first result $sql = "SELECT $this->select FROM $this->table T - WHERE $idfield = $id - $where + WHERE $where + $idWhere + LIMIT 1 ;"; $detail = $this->wpdb->get_row($sql, ARRAY_A); $this->addDataAbstractNotice($sql, 'DataBlock', "DataAbstract - getEntry() query"); // If nothing was found, simply return false - if ($detail == false) { - return false; - } + if ($detail == false) { + return false; + } // Process individual fields $detail = $this->processOutputData($detail, 'g', $fieldVals); diff --git a/lib/GlmDataAbstract/documentation.odt b/lib/GlmDataAbstract/documentation.odt index 21f30b1fc85e24b2caf5f0a42552124ff5af1982..033ff2c493796a36624091725381f33c0af9d380 100644 GIT binary patch delta 37311 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 Gmj6Et^zhmM delta 32334 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 diff --git a/models/admin/management/theme.php b/models/admin/management/theme.php new file mode 100644 index 00000000..c57a4ff6 --- /dev/null +++ b/models/admin/management/theme.php @@ -0,0 +1,184 @@ + + * @license http://www.gaslightmedia.com Gaslightmedia + * @version 0.1 + */ + +// Load Members data abstract +require_once(GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/settings/dataSettingsTheme.php'); + +/* + * This class performs the work for the default action of the "Members" menu + * option, which is to display the members dashboard. + * + */ +class GlmMembersAdmin_management_theme extends GlmDataSettingsTheme +{ + + /** + * 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 members data class + parent::__construct(false, false); + + } + + /* + * Perform Model Action + * + * This method does the work for this model and returns any resulting data + * + * @return array Status and data array + * + * 'status' + * + * True if successfull and false if there was a fatal failure. + * + * 'menuItemRedirect' + * + * If not false, provides a menu item the controller should + * execute after this one. Normally if this is used, there would also be a + * modelRedirect value supplied as well. + * + * 'modelRedirect' + * + * If not false, provides an action the controller should execute after + * this one. + * + * 'view' + * + * A suggested view name that the contoller should use instead of the + * default view for this model or false to indicate that the default view + * should be used. + * + * 'data' + * + * Data that the model is returning for use in merging with the view to + * produce output. + * + */ + public function modelAction ($actionData = false) + { + // General settings are always stored in a record with ID=1. + $id = 1; + + // Determine if current user can edit configurations + if (!current_user_can('glm_members_settings')) { + return array( + 'status' => false, + 'menuItemRedirect' => 'error', + 'modelRedirect' => 'index', + 'view' => 'admin/error/index.html', + 'data' => array( + 'reason' => 'User does not have rights to make configuration changes.' + ) + ); + } + + // Check for submission option + $option = ''; + if (isset($_REQUEST['option']) && $_REQUEST['option'] == 'submit') { + $option = $_REQUEST['option']; + } + + switch($option) { + + // Update the settings and redisplay the form + case 'submit': + + // Update the general settings + $themeSettings = $this->updateEntry(1); + + // Display admin message that the data has been updated + glmMembersAdmin::addNotice('Theme Settings for the '.GLM_MEMBERS_PLUGIN_NAME.' plugin have been updated.', 'AdminNotice'); + + break; + + // Default is to get the current settings and display the form + default: + + // Try to get the first (should be only) entry for general settings. + $themeSettings = $this->editEntry($id); + + // Check that we actually have the theme + if ($themeSettings === false) { + + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { + glmMembersAdmin::addNotice("/modesl/admin/settings/theme.php: Unable to load Theme Settings.", 'Alert'); + } + + return array( + 'status' => false, + 'menuItemRedirect' => 'error', + 'modelRedirect' => 'index', + 'view' => 'admin/error/index.html', + 'data' => array( + 'reason' => 'Unable to create theme settings entry in database.' + ) + ); + } + + break; + + } + + // Compile template data + $templateData = array( + 'reason' => '', + 'themeSettings' => $themeSettings, +// 'canEdit' => $canEdit + ); + + // Return status, suggested view, and data to controller + return array( + 'status' => true, + 'menuItemRedirect' => false, + 'modelRedirect' => false, + 'view' => 'admin/management/theme.html', + 'data' => $templateData + ); + + } + +} + +?> \ No newline at end of file diff --git a/models/admin/member/memberInfo.php b/models/admin/member/memberInfo.php index 5ab5b792..d23b73f3 100644 --- a/models/admin/member/memberInfo.php +++ b/models/admin/member/memberInfo.php @@ -256,6 +256,7 @@ class GlmMembersAdmin_member_memberInfo extends GlmDataMemberInfo $this->memberData = $Members->getEntry($this->memberID); $this->fields['logo']['i_prefix'] = 'memb_'.$this->memberID.'_'; $this->haveMember = true; + $memberName = $this->memberData['name']; // If no member info record, assume that we need to create a new one diff --git a/models/admin/members/list.php b/models/admin/members/list.php index b46a274e..c2995b1a 100644 --- a/models/admin/members/list.php +++ b/models/admin/members/list.php @@ -108,6 +108,15 @@ class GlmMembersAdmin_members_list extends GlmDataMembers $filterPending = false; $filterArchived = false; $haveFilter = false; + $numbDisplayed = false; + $lastDisplayed = false; + $paging = true; + $prevStart = false; + $nextStart = false; + $start = 1; + $limit = 20; // Set to the number of listings per page + $namesList = false; + // Check if this is a request to show archived members if (isset($_REQUEST['filterArchived'])) { @@ -117,7 +126,7 @@ class GlmMembersAdmin_members_list extends GlmDataMembers // If not, don't show them } else { - $where .= " AND T.access != ".$this->config['access_numb']['Archived']; + $where .= " AND access != ".$this->config['access_numb']['Archived']; } // Get a list of categories for filtering @@ -172,13 +181,60 @@ class GlmMembersAdmin_members_list extends GlmDataMembers $haveFilter = true; } + // Get count of members listed + $memberCount = $this->getStats($where); + + // If the number of members is less than a page, don't do paging + if ($memberCount <= $limit) { + $paging = false; + } + + // Get full list of names matching this where clause for search box + $namesList = $this->getIdName($where); + + // Check if we're doing paging + if (isset($_REQUEST['pageSelect'])) { + + // If request is for Next + if ($_REQUEST['pageSelect'][0] == 'N') { + $newStart = $_REQUEST['nextStart'] - 0; + + // Otherwise it must be Previous + } else { + $newStart = $_REQUEST['prevStart'] - 0; + } + + if ($newStart > 0) { + $start = $newStart; + } + } + // Get a current list of members - $list = $this->getSimpleMembersList($where); + $listResult = $this->getSimpleMembersList($where, 'name', true, 'id', $start, $limit); + + // Get paging results + $numbDisplayed = $listResult['returned']; + $lastDisplayed = $listResult['last']; + if ($start == 1) { + $prevStart = false; + } else { + $prevStart = $start - $limit; + if ($start < 1) { + $start = 1; + } + } + if ($listResult['returned'] == $limit) { + $nextStart = $start + $limit; + } if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG_VERBOSE) { glmMembersAdmin::addNotice($list, 'DataBlock', 'Member Data'); } + // since we're doing paging, we have to break out just the member data + $list = $listResult['list']; + unset($listResult); + // If we have list entries - even if it's an empty list $success = true; $haveMembers = false; @@ -205,7 +261,15 @@ class GlmMembersAdmin_members_list extends GlmDataMembers 'categories' => $categories, 'haveFilter' => $haveFilter, 'filterArchived' => $filterArchived, - 'filterPending' => $filterPending + 'filterPending' => $filterPending, + 'numbDisplayed' => $numbDisplayed, + 'lastDisplayed' => $lastDisplayed, + 'paging' => $paging, + 'prevStart' => $prevStart, + 'nextStart' => $nextStart, + 'start' => $start, + 'limit' => $limit, + 'namesList' => $namesList ); // Return status, suggested view, and data to controller diff --git a/setup/adminMenus.php b/setup/adminMenus.php index 9cb7c9e9..570852af 100644 --- a/setup/adminMenus.php +++ b/setup/adminMenus.php @@ -38,12 +38,22 @@ if (apply_filters('glm_members_menu_members', true)) { add_menu_page( 'Members', - 'Members', + 'GLM Associate', 'glm_members_main_menu', 'glm-members-admin-menu-members', - function() {$this->controller('members');}, + function() {}, false, - '91.123' + '3.100' + ); + + // Add a sub-submenu for the "Member" "List" page + add_submenu_page( + 'glm-members-admin-menu-members', + 'Members List', + 'Members', + 'glm_members_main_menu', + 'glm-members-admin-menu-members', + function() {$this->controller('members', 'index');} ); // Add a sub-submenu for the "Member" "List" page diff --git a/setup/databaseScripts/create_database_V1.1.2.sql b/setup/databaseScripts/create_database_V1.1.4.sql similarity index 86% rename from setup/databaseScripts/create_database_V1.1.2.sql rename to setup/databaseScripts/create_database_V1.1.4.sql index 0d2d1b53..e3f61fae 100644 --- a/setup/databaseScripts/create_database_V1.1.2.sql +++ b/setup/databaseScripts/create_database_V1.1.4.sql @@ -1,6 +1,6 @@ -- Gaslight Media Members Database -- File Created: 12/09/14 15:27:15 --- Database Version: 1.0.43 +-- Database Version: 1.1.4 -- Database Creation Script -- -- To permit each query below to be executed separately, @@ -75,54 +75,6 @@ CREATE TABLE {prefix}cities ( ---- --- Contacts - used by various entities -CREATE TABLE {prefix}contacts ( - id INT NOT NULL AUTO_INCREMENT, - active BOOLEAN NULL, -- Contact is active flag - access INT NULL, -- Access type - See access table in plugin.ini - fname TINYTEXT NULL, -- First name of contact - lname TINYTEXT NULL, -- Last name of contact - contact_type INT NULL, -- Contact type - see contact_type table (individual, role, ...) - contact_role INT NULL, -- Contact WordPress user Role - org TINYTEXT NULL, -- Organization name - title TINYTEXT NULL, -- Title/Position - descr TEXT NULL, -- Description of position/responsibilities - Displayed - image TINYTEXT NULL, -- Image - addr1 TINYTEXT NULL, -- Address line 1 - Address is for contact, not necessarily for organization - addr2 TINYTEXT NULL, -- Address line 2 - city INT NULL, -- Pointer to city in cities table - state TINYTEXT NULL, -- Two character state code - matches states.ini entries - country TINYTEXT NULL, -- Two character country code - matches countries.ini entries - zip TINYTEXT NULL, -- ZIP/Postal Code - lat FLOAT NULL, -- Latitude of contact location - lon FLOAT NULL, -- Longitude of contact location - url TINYTEXT NULL, -- URL to information regarding this contact - office_phone TINYTEXT NULL, -- Office phone number - home_phone TINYTEXT NULL, -- Home phone number - or after-hours phone number - mobile_phone TINYTEXT NULL, -- Mobile phone number - alt_phone TINYTEXT NULL, -- An alternate phone number - fax TINYTEXT NULL, -- FAX number (do people still use these?) - email TINYTEXT NULL, -- E-Mail address - alt_email TINYTEXT NULL, -- Alternate E-Mail address - Also used to log-in - username TINYTEXT NULL, -- Optional username to use for login - password TINYTEXT NULL, -- Encrypted password - notes TEXT NULL, -- Notes - Not displayed on front-end - create_time TIMESTAMP NULL, -- Create date/time - modify_time TIMESTAMP NULL, -- Last modified date/time - ref_type INT NULL, -- Type of entity this contact is associated with - ref_dest INT NULL, -- Pointer to the specific entity of ref_type this contact is associated with - PRIMARY KEY (id), - INDEX(fname(20)), - INDEX(lname(20)), - INDEX(city), - INDEX(zip(10)), - INDEX(lat), - INDEX(lon), - INDEX(email(20)) -); - ----- - -- Files - Files are stored under /wp-content/uploads/glm-member-db/files/ CREATE TABLE {prefix}files ( id INT NOT NULL AUTO_INCREMENT, @@ -503,3 +455,25 @@ INSERT INTO {prefix}settings_terms 'Contacts' ) ; + +---- + +-- Theme Settings - Only 1 entry in this table +CREATE TABLE {prefix}settings_theme ( + id INT NOT NULL AUTO_INCREMENT, + PRIMARY KEY (id) +); + +---- + +-- Default Theme Settings entry +INSERT INTO {prefix}settings_theme + ( + id + ) + VALUES + ( + 1 + ) +; + diff --git a/setup/databaseScripts/dbVersions.php b/setup/databaseScripts/dbVersions.php index 1da28f88..2337d535 100644 --- a/setup/databaseScripts/dbVersions.php +++ b/setup/databaseScripts/dbVersions.php @@ -33,7 +33,9 @@ $glmMembersDbVersions = array( '1.0.43' => array('version' => '1.0.43', 'tables' => 26), '1.1.0' => array('version' => '1.1.0', 'tables' => 13), '1.1.1' => array('version' => '1.1.1', 'tables' => 13), - '1.1.2' => array('version' => '1.1.2', 'tables' => 14, 'date' => '4/11/16') + '1.1.2' => array('version' => '1.1.2', 'tables' => 14, 'date' => '4/11/16'), + '1.1.3' => array('version' => '1.1.3', 'tables' => 13, 'date' => '4/13/16'), + '1.1.4' => array('version' => '1.1.4', 'tables' => 14, 'date' => '4/18/16') ); diff --git a/setup/databaseScripts/drop_database_V1.1.2.sql b/setup/databaseScripts/drop_database_V1.1.3.sql similarity index 95% rename from setup/databaseScripts/drop_database_V1.1.2.sql rename to setup/databaseScripts/drop_database_V1.1.3.sql index 78bacb57..17b02fda 100644 --- a/setup/databaseScripts/drop_database_V1.1.2.sql +++ b/setup/databaseScripts/drop_database_V1.1.3.sql @@ -9,7 +9,6 @@ DROP TABLE IF EXISTS {prefix}amenity_ref, {prefix}category_member_info, {prefix}cities, - {prefix}contacts, {prefix}images, {prefix}files, {prefix}members, @@ -18,6 +17,7 @@ DROP TABLE IF EXISTS {prefix}regions, {prefix}settings_general, {prefix}settings_terms, + {prefix}settings_theme, {prefix}categories ; diff --git a/setup/databaseScripts/update_database_V1.1.3.php b/setup/databaseScripts/update_database_V1.1.3.php new file mode 100644 index 00000000..a6fb85ce --- /dev/null +++ b/setup/databaseScripts/update_database_V1.1.3.php @@ -0,0 +1,17 @@ +wpdb->query(" + RENAME TABLE ".GLM_MEMBERS_PLUGIN_DB_PREFIX."contacts TO ".$prefix."contacts; +"); diff --git a/setup/databaseScripts/update_database_V1.1.3.sql b/setup/databaseScripts/update_database_V1.1.3.sql new file mode 100644 index 00000000..e748a976 --- /dev/null +++ b/setup/databaseScripts/update_database_V1.1.3.sql @@ -0,0 +1,11 @@ +-- Gaslight Media Members Database +-- File Created: 11/11/16 15:27:15 +-- Database Version: 1.1.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 dashses + +-- Move contacts table to contacts plugin - Rename prefix +-- Doing nothing here - See update_database_V1.1.3.php + diff --git a/setup/databaseScripts/update_database_V1.1.4.sql b/setup/databaseScripts/update_database_V1.1.4.sql new file mode 100644 index 00000000..7b803183 --- /dev/null +++ b/setup/databaseScripts/update_database_V1.1.4.sql @@ -0,0 +1,31 @@ +-- Gaslight Media Members Database +-- File Created: 11/11/16 15:27:15 +-- Database Version: 1.1.4 +-- 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 dashses + +-- Move contacts table to contacts plugin - Rename prefix +-- Doing nothing here - See update_database_V1.1.3.php + + +-- Theme Settings - Only 1 entry in this table +CREATE TABLE {prefix}settings_theme ( + id INT NOT NULL AUTO_INCREMENT, + PRIMARY KEY (id) +); + +---- + +-- Default Theme Settings entry +INSERT INTO {prefix}settings_theme + ( + id + ) + VALUES + ( + 1 + ) +; + diff --git a/setup/validActions.php b/setup/validActions.php index f3b9208c..651e9545 100644 --- a/setup/validActions.php +++ b/setup/validActions.php @@ -62,6 +62,7 @@ $glmMembersValidActions = array( 'index' => 'glm-member-db', // General Options 'terms' => 'glm-member-db', 'development' => 'glm-member-db', + 'theme' => 'glm-member-db', 'import' => 'glm-member-db', 'addons' => 'glm-member-db', 'hooks' => 'glm-member-db' diff --git a/views/admin/management/header.html b/views/admin/management/header.html index 913b6f0a..da746808 100644 --- a/views/admin/management/header.html +++ b/views/admin/management/header.html @@ -5,8 +5,9 @@

List of Members

- - - - - - - - - - - - -{if $haveMembers} - {assign var="i" value="0"} - {foreach $members as $m} - {if $i++ is odd by 1} - - {else} - - {/if} - - - - - - - {/foreach} -{else} - -{/if} - -
IDMember NameAccessMember TypeDate Created
- {$m.id} - Active Profile - - {$m.name} - - {$m.access_short.name} - - {$m.member_type_short} - - {$m.created.date} -
(no members listed)