From 694d647dfcdb2049d998cd99254691e3c9b4ec08 Mon Sep 17 00:00:00 2001 From: Chuck Scott Date: Fri, 6 Nov 2015 15:37:38 -0500 Subject: [PATCH] Interim commit so Steve can review. --- activate.php | 212 ++++++ classes/data/dataContacts.php | 192 ++++++ config.php | 27 + config/plugin.ini | 6 + controllers/admin.php | 606 ++++++++++++++++++ controllers/front.php | 186 ++++++ deactivate.php | 62 ++ defines.php | 60 ++ glm-member-db-contacts.php | 132 +++- index.php | 2 + .../Plugin_Functional_Outline.odt | Bin 0 -> 40943 bytes readme.txt | 27 + uninstall.php | 28 + 13 files changed, 1534 insertions(+), 6 deletions(-) create mode 100644 activate.php create mode 100644 classes/data/dataContacts.php create mode 100644 config.php create mode 100644 config/plugin.ini create mode 100644 controllers/admin.php create mode 100644 controllers/front.php create mode 100644 deactivate.php create mode 100644 defines.php create mode 100644 index.php create mode 100644 misc/documentation/Plugin_Functional_Outline.odt create mode 100644 readme.txt create mode 100644 uninstall.php diff --git a/activate.php b/activate.php new file mode 100644 index 0000000..5b702a9 --- /dev/null +++ b/activate.php @@ -0,0 +1,212 @@ + + * @license http://www.gaslightmedia.com Gaslightmedia + * @release activate.php,v 1.0 2014/10/31 19:31:47 cscott Exp $ + * @link http://dev.gaslightmedia.com/ + */ + +// Check that we're being called by WordPress. +if (!defined('ABSPATH')) { + die("Please do not call this code directly!"); +} + +/* + * This class performs all necessary additional work when this + * plugin is activated. + * + * Currently the only actions are to add role capability to display and modify + * prototypes. + */ +class glmMembersContactsPluginActivate +{ + + /** + * WordPress Database Object + * + * @var $wpdb + * @access public + */ + public $wpdb; + /** + * Plugin Configuration Data + * + * @var $config + * @access public + */ + public $config; + + /* + * Constructor + * + * Note that the $noDatabaseCheck is used to access the database versions + * without triggering a database check. + * + * Performs all the work for this model + */ + public function __construct ($wpdb, $config) + { + + // Make sure the current user has this capability + if (! current_user_can('activate_plugins')) { + $this->addNotice("Interesting, you don't have permission to activate plugins."); + die(); + } + + // Save WordPress Database object + $this->wpdb = $wpdb; + + // Save plugin configuration object + $this->config = $config; + + // Set current plugin version + update_option('glmMembersDatabaseContactsPluginVersion', GLM_MEMBERS_CONTACTS_PLUGIN_VERSION); + + /* + * Add contacts roles + * + * Note that the members_manager capability is created by the main Member DB plugin + */ + + // Members Manager - Full control of all members and their data + add_role( + 'glm_members_manager', + 'GLM Members Manager', + array( + 'read' => true, + 'glm_members_management' => true + ) + ); + + // Own Member Manager - Full control of own member, location, facility (based on with which the contact is assocated) + add_role( + 'glm_own_member_manager', + 'GLM Own Member Manager', + array( + 'read' => true, + 'glm_members_management' => true // but only allowed to manage their entity type (member, location, facility, ...) + ) + ); + + // Member Contact - Standard contact for own member, location, facility, ... - no edit of member data + add_role( + 'glm_member_contact', + 'GLM Member Contact', + array( + 'read' => true + ) + ); + + // Restricted Member Contact - No login capability + add_role( + 'glm_member_restricted_contact', + 'GLM Member Restricted Contact', + array( + 'read' => true + ) + ); + + /* + * Add contacts capabilities + * + * Note that the glm_members_management capability is created by the main Member DB plugin + */ + + // May log in through members only area + $this->addRoleCapability( + 'glm_members_login', + array( + 'administrator' => true, + 'glm_members_manager' => true, + 'glm_own_member_manager' => true, + 'glm_member_contact' => true, + 'glm_member_restricted_contact' => false + ) + ); + + } + + /* + * Add a role capability to all current roles + * + * @param string $capability Name of capability to add + * @param array $default Whether capability should be on by default + * array( + * 'author' => false, + * 'contributor' => false, + * 'editor' => false, + * 'subscriber' => false + * ) + * + * @return void + * @access private + */ + private function addRoleCapability ($capability, $default) + { + // Get list of role objects + $roleObjects = $GLOBALS['wp_roles']->role_objects; + + // Get list of roles we can edit + $roles = get_editable_roles(); + + // For each role object + foreach ($roleObjects as $key => $role) { + + // Uncomment to reset capabilities + /* + if ( isset($role->capabilities[$capability])) { + $role->remove_cap($capability); + } + */ + + // Check if the role exists in list of editable roles and + // the capability does not exist + if (isset($roles[$key]) && ! isset($role->capabilities[$capability])) { + + // Check if a default value has been specified in the $default array + $enabled = false; + if (isset($default[$role->name])) { + + // It has, so use that + $enabled = $default[$role->name]; + + } + + // Add the role + $role->add_cap($capability, $enabled); + + } + } + } + +} + + +/* +// May be used to delete a capability from all roles +add_action( 'admin_init', 'glmMembersDeleteCapabilities' ); +function glmMembersDeleteCapabilities(){ + + // Put capabilities to delete here + $delete_caps = array( + 'glm_members_contact_manage_members' + ); + + global $wp_roles; + foreach ($delete_caps as $cap) { + foreach (array_keys($wp_roles->roles) as $role) { + $wp_roles->remove_cap($role, $cap); + } + } +} +*/ + +?> diff --git a/classes/data/dataContacts.php b/classes/data/dataContacts.php new file mode 100644 index 0000000..2f5a462 --- /dev/null +++ b/classes/data/dataContacts.php @@ -0,0 +1,192 @@ + + * @license http://www.gaslightmedia.com Gaslightmedia + * @release SVN: $Id: dataContacts.php,v 1.0 2011/01/25 19:31:47 cscott Exp $ + */ + +/** + * GlmDataContacts 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 GlmDataContacts 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_CONTACTS_PLUGIN_DB_PREFIX . 'contacts'; + + /* + * Table Data Fields + */ + +/************* NOT UPDATED YET ******************/ + $this->fields = array ( + + 'id' => array ( + 'field' => 'id', + 'type' => 'integer', + 'view_only' => true, + 'use' => 'a' + ), + + // Status + 'access' => array ( + 'field' => 'access', + 'type' => 'list', + 'list' => $this->config['memb_access'], + 'l_blank' => true, + 'required' => true, + 'default' => 30, + 'force_list' => true, + 'use' => 'a' + ), + + // Member Type + 'member_type' => array ( + 'field' => 'member_type', + 'type' => 'pointer', + 'p_table' => GLM_MEMBERS_PLUGIN_DB_PREFIX . 'member_type', + 'p_field' => 'name', + 'p_orderby' => 'name', + 'p_blank' => true, + 'required' => true, + 'force_list' => true, + 'use' => 'a' + ), + + // Member Name + 'name' => array ( + 'field' => 'name', + 'type' => 'text', + 'required' => true, + 'unique' => true, + 'use' => 'a' + ), + + // Member Name (stored by member updates) for sorting + 'member_slug' => array( + 'field' => 'member_slug', + 'type' => 'text', + 'required' => true, + 'use' => 'a' + ), + + // Date created + 'created' => array ( + 'field' => 'created', + 'type' => 'date', + 'required' => true, + 'use' => 'a' + ), + + // Active Version + 'active_id' => array ( + 'field' => 'id', + 'as' => 'active_id', + 'type' => 'pointer', + 'p_table' => GLM_MEMBERS_PLUGIN_DB_PREFIX . 'member_info', + 'p_field' => 'id', + 'p_id' => 'member', + 'p_where' => 'status = '.$this->config['status_numb']['Active'], + 'p_static' => true, + 'use' => 'gl' + ) + + ); + +/* - not updated for add-on yet + 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/config.php b/config.php new file mode 100644 index 0000000..27d17c8 --- /dev/null +++ b/config.php @@ -0,0 +1,27 @@ + diff --git a/config/plugin.ini b/config/plugin.ini new file mode 100644 index 0000000..a2a5cb2 --- /dev/null +++ b/config/plugin.ini @@ -0,0 +1,6 @@ +; +; Main Configuration File +; Gaslight Media Members Database Contacts Child Plugin +; + +[common] diff --git a/controllers/admin.php b/controllers/admin.php new file mode 100644 index 0000000..4a095d9 --- /dev/null +++ b/controllers/admin.php @@ -0,0 +1,606 @@ + + * @license http://www.gaslightmedia.com Gaslightmedia + * @release admin.php,v 1.0 2014/10/31 19:31:47 cscott Exp $ + * @link http://dev.gaslightmedia.com/ + */ + +/** + * Array of valid menu items and actions. + * + * These are the valid menu items and actions that are passed to the main + * GLM Members DB plugin, along with other information about this add-on, + * using the glm-member-db-register-addon hook. + */ +$glmMembersContactsValidActions = array( + 'admin' => array( + 'member' => array ( + 'contacts' + ) + ), + 'front' => array( + ) +); + +// Load glmPluginSupport class from main GLM Member DB plugin +require_once (GLM_MEMBERS_CONTACTS_MAIN_PLUGIN_PATH . '/classes/glmPluginSupport.php'); + +// Load Smarty Template Support from main GLM Member DB plugin +require_once (GLM_MEMBERS_CONTACTS_MAIN_PLUGIN_PATH . '/lib/smartyTemplateSupport.php'); + +/** + * Admin Controller Class + * + * This is one of perhaps multiple controller classes that provide + * controller services for a major portion of this plugin. Typically + * there are such classes for Admin and Front-End functionality, but + * there could be others. + * + * This controller class performs all admin related operations for + * this plugin by calling the appropriate model and merging the resulting + * data with the appropriate view to produce any output. + * + * All requests for this controller class come through WordPress admin + * menus via hooks that "call back" methods in this class for each admin + * menu item in this plugin. Form submissions from an admin page selected + * by a particular menu item are directed to WordPress using the page + * reference of that menu item. Because of this, the callback for a form + * submission is also handled by the callback target method used by that + * menu item. + * + * Admin form submissions must use the URI for one of this plugin's + * menu items. The form post parameters may also provide an "action" name + * in the case where the default menu item behavior is not desired. A + * pathname for the model to execute is then complied using the menu + * item name as the name of a directory under models/admin and the + * requested action as the file name of the model to execute. The name + * "index" would be the default menu item action model. In essence the + * controller locates the model by menu item name and action name. for + * example... + * + * models/admin/members/index.php + * models/admin/members/display.php + * + * Similarly, the view associated with the action would be in a directory + * named the same as the model, and the view file would be named "index" + * or the name of the action. + * + * These hooks are established using the WordPress add_action() + * function and the configureMenus() method below. Other methods in this + * class then recieve any request from a menu item selection or form + * + * submission associated with a menu item by WordPress calling one of the + * "callback" methods below. + * + * The callback methods do nothing other than to call the controller() + * method and passing it the name of the menu item assocaiated with the + * callback. + * + * The controller() method determines which model to execute, executes + * that model, determines which view to merge with the data returned by + * the model, creates output from the result of that merge, and sends + * that output to the user. + * + * In situations where it may be desired to output directly to the browser + * without being contained in the admin Dashboard, the contructor can be + * directed + * to bypass setting up the admin hooks and execute the controller() method + * directly then exit. This results in output from the model/view withing being + * contained in the normal WordPress dashboard context. To trigger this use the + * following two form fields. + * + * glm_display_direct = 'true' + * glm_menu_item = (menu item name associated with the desired model) + * + * (no prameters) + * + * @return void + * @access public + */ +class glmMembersContactsAdmin extends GlmPluginSupport +{ + + /** + * WordPress Database Object + * + * @var $wpdb + * @access public + */ + public $wpdb; + /** + * Plugin Configuration Data + * + * @var $config + * @access public + */ + public $config; + + /** + * Admin Controller Constructor + * + * This contructor is executed by the main plugin index file when the site + * Dashboard is displayed. It's responsible for setting up any needed hooks + * into WordPress and setting up any support classes required to do admin + * work. + * + * (no prameters) + * + * @return void + * @access public + */ + public function __construct ($wpdb, $config) + { + + // Save WordPress Database object + $this->wpdb = $wpdb; + + // Save plugin configuration object + $this->config = $config; + + // Hook into menu created by main GLM Member DB plugin + // If we don't do it this way, the member db menus may not be setup + add_action('glm-member-db-add-menu', array($this, 'glmMembersAddMenusContacts')); + + add_filter('glm-member-db-add-tab-for-members', array($this, 'glmMembersAddTabForMembers')); + + } + + public function glmMembersAddTabForMembers($addOnTabs) + { + + $newTabs = array( + array( + 'text' => 'New Tab', + 'action' => 'list' + ), + array( + 'text' => 'Another Tab', + 'action' => 'anotherAction' + ) + ); + $addOnTabs = array_merge($addOnTabs, $newTabs); + + return $addOnTabs; + + } + + + /** + * Called by glm-member-db-add-menu hook from main GLM Member DB plugin + * + * This adds an admin_menu action that calls the configureMenus() method to + * actually do the work of adding the menus. + * + * Since there needs to be an add_action() call with 'admin_menu" and have that + * then link in an action that can be called to do the menu pages, we need this + * intermediate method. + * + * @return void + * @access public + */ + public function glmMembersAddMenusContacts() + { + + // Add hooks to WordPress + add_action('admin_menu', array($this, 'configureMenus')); + + } + + /** + * Configure WordPress Menus for this Plugin + * + * This method is called by an add_action() hook setup in the contructor. We + * do it + * this way so that the menu related functions of WordPress are in scope + * when creating + * the additional menu items. WordPress will execute this call-back method + * when building + * its Dashboard menus. + * + * add menu function reference + * add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position) + * add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function) + * + * (no prameters) + * + * @return void + * @access public + */ + public function configureMenus () + { + + // A main Menu Test + add_menu_page( + 'Test Page 1', // Page title + 'Test 1', // Menu title + 'glm_members_edit', // Capability + 'glm-members-admin-menu-test-1', // Menu slug + array($this, 'glmMembersAdminMenuTest'), // Function to execute page + false, // Icon URL + '91.124' // Menu position + ); + + // A Test Sub-Menu + add_submenu_page( + 'glm-members-admin-menu-members', // Parent slug + 'Contacts', // Page title + 'Contacts', // Menu Title + 'glm_members_edit', // Capability required + 'glm-members-admin-menu-contacts', // Menu slug + array($this, 'glmMembersAdminMenuTest') // Function to execute page + ); + + // A Test Sub-Menu + add_submenu_page( + 'glm-members-admin-menu-test-1', // Parent slug + 'Sub-Test', // Page title + '  Sub-Test', // Menu Title + 'glm_members_edit', // Capability required + 'glm-members-admin-menu-sub-test-1', // Menu slug + array($this, 'glmMembersAdminMenuTest') // Function to execute page + ); + + } + + /* + * Menu item specific "Callback" methods + * + * These methods are called by WordPress when specific menu items are + * selected by the + * user or a form action is submitted associated with the menu item. + * + * These methods call the controller and pass it the menu item that was + * called + * but perform no other work. + * + */ + + // A test menu + function glmMembersAdminMenuTest() { +// $this->controller('contacts'); + echo "TEST"; + } + + + /** + * Contacts Admin controller + * + * This method is called by a plugin menu method. It is responsible for + * executing the approriate model, combining model data with a view, and + * outputing the result. It is therefore the core of the controller. + * + * This controller is supplied a menu item name and then determines if + * there is an additional action related to that menu item that needs to be + * executed rather than the default menu action. + * + * All models should return an array containing the following. + * + * '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. + * + * For a better explanation of how this all works, see the description for + * this class. + * + * Controller parameters + * + * @param string $menuItem + * Name of the menu item that is being processed + * + * @return void + * @access public + */ + public function controller ($menuItem, $action = false) + { + $errorMsg = ''; + + /* + * Because WordPress insists on forcing the timezone to UTC + * we need to save the current timezone setting, set the one + * we want to use, and then restore it after we're all done + * (see bottom of this script). + */ + $defaultTimeZone = date_default_timezone_get(); + date_default_timezone_set($this->config['settings']['time_zone']); + + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { + + // Also turn on SQL error messages + $this->wpdb->show_errors(); + + // If debug is VERBOSE + $consts = get_defined_constants(true); + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG_VERBOSE) { + $this->addNotice("
".print_r($_SERVER,1).'
', 'DataBlock', 'Server Defines'); + $this->addNotice('
'.print_r($consts,1).'
', 'DataBlock', 'Defined Parameters'); + // Not verbose + } else { + $ourConsts = array(); + foreach ($consts['user'] as $k => $v) { + if (strncmp($k, 'GLM_MEMBERS_PLUGIN', 18) == 0) { + $ourConsts[$k] = $v; + } + } + $this->addNotice('
'.print_r($ourConsts,1).'
', 'DataBlock', 'Defined Parameters'); + } + + $this->addNotice('
'.print_r($this->config,1).'
', 'DataBlock', 'Configuration Settings'); + $this->addNotice("
".print_r($_REQUEST,1)."
", 'DataBlock', "Request Data"); + if (isset($_FILES)) { + $this->addNotice("
".print_r($_FILES,1)."
", 'DataBlock', "Request Files Data"); + } + } else { + $this->clearNotices(); + } + + /* + * Determine model to execute + */ + + // Default action is "index" if an action wasn't specified in the controller call + if (!$action) { + $action = 'index'; + } + + // Get any requested "action" from a form submission and modify path/name + // accordingly. This modifies the previously set $action. + if (isset($_REQUEST['glm_action']) && $_REQUEST['glm_action'] != '') { + $a = sanitize_text_field($_REQUEST['glm_action']); + if ($a != '') { + $action = $a; + } + } + + // Loop till we have a final action + $loopCheck = 0; + $actionData = false; + do { + + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { + $this->addNotice("Requested Action: Menu item = $menuItem, Action = $action", 'Process'); + } + + $modelRedirect = false; + + // Verify that we have the requested menu item in the valid actions + if (!isset($GLOBALS['glmMembersAdminValidActions'][$menuItem])) { + + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { + $this->addNotice('Error in Admin Controller: Menu Item not specified!', 'Alert'); + } + + $modelRedirect = true; + $menuItem = 'error'; + $action = 'index'; + $errorMsg .= "Model doesn't exist: ".$modelName; + + } + + // Verify Menu item and action using array at top of this file + if (! isset($GLOBALS['glmMembersAdminValidActions'][$menuItem]) || + ! in_array($action, $GLOBALS['glmMembersAdminValidActions'][$menuItem])) { + + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { + $this->addNotice('Error in Admin Controller: Requested Menu Item is invalid! - '.$menuItem, 'Alert'); + } + + $menuItem = 'error'; + $action = 'badAction'; + } + + /* + * Execute the selected model + */ + + // Build model and path and class names + $modelName = GLM_MEMBERS_PLUGIN_PATH . '/models/admin/' . $menuItem . + '/' . $action . '.php'; + $className = 'GlmMembersAdmin_' . $menuItem . '_' . $action; + + // If model file doesn't exist - we have an error + if (!file_exists($modelName)) { + + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { + $this->addNotice("Error in Admin Controller: Model file doesn't exist - ".$modelName, 'Alert'); + } + + $modelRedirect = true; + $menuItem = 'error'; + $action = 'index'; + $errorMsg .= "Model doesn't exist: ".$modelName; + + // Otherwise, load and run the model + } else { + + // Load the model file + require_once ($modelName); + + // check for an invalid model class name + if (!class_exists($className)) { + + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { + $this->addNotice("Error in Admin Controller: Invalid Model Class Name - ".$className, 'Alert'); + } + + $modelRedirect = true; + $menuItem = 'error'; + $action = 'index'; + $errorMsg .= "Model class doesn't exist: ".$className; + + } else { + + // Check if this is a re-direct with parameters + $args = false; + + // Instantiate the model and ask it to perform the work + $model = new $className($this->wpdb, $this->config); + $results = $model->modelAction($actionData); + + // Check if there's been a model redirect request + if ($results['modelRedirect']) { + + $this->addNotice('
'.print_r($results,1).'
', 'DataBlock', 'Model Redirect'); + + // Set the new model action + $action = $results['modelRedirect']; + + // Check if there's also a menu item change + if ($results['menuItemRedirect']) { + $menuItem = $results['menuItemRedirect']; + } + + // Check if there's data to pass to the new model + if (isset($results['data']) && count($results['data']) > 0) { + $actionData = $results['data']; + } + + $modelRedirect = true; + } + + // Get the specified view file + $view = false; + if (isset($results['view'])) { + $view = $results['view']; + } + + // Check for invalid or missing view file + if (!$view || !is_file(GLM_MEMBERS_PLUGIN_PATH . '/views/'.$view)) { + + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { + $this->addNotice("Error in Admin Controller: Requested View file doesn't exist - ".$view, 'Alert'); + } + + $modelRedirect = true; + $menuItem = 'error'; + $action = 'index'; + $errorMsg .= "Bad or missing view file: ".$view; + } + + } // model class exists + } + + // This is just a sanity check on this loop to keep it from getting out of control + if (++$loopCheck > 10) { + die('

Serious failure looping on model load in "controllers/admin.php".

'); + } + + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG && $modelRedirect) { + $this->addNotice('Redirecting...', 'Process'); + } + + // Loop again if there's a model redirect + + } while ($modelRedirect); + + /* + * Check model results + */ + + // Get suggested view + $view = $results['view']; + + // If there's a general model failure use the error view + if (! $results['status']) { + $view = 'admin/error/index.html'; + } + + // Check for modified settings to save in conf + if (isset($results['settings'])) { + while (list($key, $val) = each($results['settings'])) { + $this->config['settings'][$key] = $val; + } + } + + /* + * Merge data returned from the model with the selected view + */ + + // Load Smarty Template support + $smarty = new smartyTemplateSupport(); + + // Add standard template parameters + $smarty->templateAssign ( 'adminDebug', GLM_MEMBERS_PLUGIN_ADMIN_DEBUG); + $smarty->templateAssign ( 'adminURL', GLM_MEMBERS_PLUGIN_ADMIN_URL); + $smarty->templateAssign ( 'baseURL', GLM_MEMBERS_PLUGIN_BASE_URL); + $smarty->templateAssign ( 'thisURL', GLM_MEMBERS_PLUGIN_CURRENT_URL ); + $smarty->templateAssign ( 'thisPage', (isset($_REQUEST['page']) ? $_REQUEST['page']: '') ); + $smarty->templateAssign ( 'glmPluginName', GLM_MEMBERS_PLUGIN_NAME ); + $smarty->templateAssign ( 'glmPluginMediaURL', GLM_MEMBERS_PLUGIN_MEDIA_URL ); + $smarty->templateAssign ( 'thisYear', date ( 'Y' ) ); + $smarty->templateAssign ( 'ref_type_numb', $this->config['ref_type_numb']); + $smarty->templateAssign ( 'settings', $this->config['settings']); + $smarty->templateAssign ( 'terms', $this->config['terms']); + + // Add data from model to Smarty template + if (is_array($results['data']) && count($results['data']) > 0) { + foreach ($results['data'] as $k => $d) { + $smarty->templateAssign($k, $d); + } + } + + $smarty->templateAssign ( 'thisAction', $action); + + + // If there's an error message, add that also + if ($errorMsg != '') { + $smarty->templateAssign('errorMsg', $errorMsg); + } + + // If view debug has been requested + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { + + glmMembersAdmin::addNotice("Template File: $view", 'Process'); + + $x = $smarty->template->getTemplateVars(); + + if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) { + glmMembersAdmin::addNotice($x, 'DataBlock', 'Template Parameters'); + } + + $templateVars = '
' . print_r($x, 1) . '
'; + glmMembersAdmin::addNotice($templateVars, 'Template Parameters', 'Process'); + + } + + // Generate output from model data and view + + $smarty->template->display($view); + + // Restore timezone that was set before our code was called + date_default_timezone_set($defaultTimeZone); + + } + +} + + diff --git a/controllers/front.php b/controllers/front.php new file mode 100644 index 0000000..1d8daf9 --- /dev/null +++ b/controllers/front.php @@ -0,0 +1,186 @@ + + * @license http://www.gaslightmedia.com Gaslightmedia + * @release front.php,v 1.0 2014/10/31 19:31:47 cscott Exp $ + * @link http://dev.gaslightmedia.com/ + */ + +/** + * Array of valid menu items and actions. + * + * + * The higher level elements are valid menu items. These correlate to + * actual menu or sub menu items that are hooks back to this controller + * class. + * + * The lower level items below each menu item are actions that may be specified + * by a "glmMembersAction" form field. + */ +$GLOBALS['glmMembersFrontValidActions'] = array( + + 'members' => array( + 'list', + 'detail' + ), + 'error' => array( + 'index', + 'badAction' + ) + +); + +// Load glmPluginSupport class +require_once (GLM_MEMBERS_CONTACTS_MAIN_PLUGIN_PATH . '/classes/glmPluginSupport.php'); + +/* + * This class controls which models are use for front-end functionality + * of this plugin. + */ +class glmMembersCOntactsFront extends GlmPluginSupport +{ + + /** + * WordPress Database Object + * + * @var $wpdb + * @access public + */ + public $wpdb; + + /** + * Plugin Configuration Data + * + * @var $config + * @access public + */ + public $config; + + public function __construct ($wpdb, $config) + { + + // Save WordPress Database object + $this->wpdb = $wpdb; + + // Save plugin configuration object + $this->config = $config; + + } + + /** + * Front-End controller + * + * This method is called by a plugin menu method. It is responsible for + * executing the approriate model, combining model data with a view, and + * outputing the result. It is therefore the core of the controller. + * + * This controller is supplied a menu item name and then determines if + * there is an additional action related to that menu item that needs to be + * executed rather than the default menu action. + * + * All models should return an array containing the following. + * + * '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. + * + * For a better explanation of how this all works, see the description for + * this class. + * + * Controller parameters + * + * @param object $atts + * Shortcode attributes + * @param string $content + * Content included by an Enclosing format shortcode + * + * @return void + * @access public + */ + public function controller ($atts, $content = null, $shortcode) + { + + // Set shortcode attribute defaults +/* + switch ($shortcode) { + + case 'glm-members-list': + $action = 'list'; + $request = shortcode_atts( + array( + 'map' => true, + 'category' => false, + 'category-name' => false, + 'alpha' => false, + 'search' => false, + 'amenities' => false, + 'detail-page' => false, + 'show' => false + ), + $atts, + 'glm-members' + ); + break; + + case 'glm-member-detail': + $action = 'detail'; + $request = shortcode_atts( + array( + 'map' => true, + 'id' => false, + 'show' => false + ), + $atts, + 'glm-members' + ); + break; + + } +*/ + /* + * Because WordPress insists on forcing the timezone to UTC + * we need to save the current timezone setting, set the one + * we want to use, and then restore it after we're all done + * (see bottom of this script). + */ + $defaultTimeZone = date_default_timezone_get(); + date_default_timezone_set($this->config['settings']['time_zone']); + + // Restore timezone that was set before our code was called + date_default_timezone_set($defaultTimeZone); + + return $out; + + } +} + +?> \ No newline at end of file diff --git a/deactivate.php b/deactivate.php new file mode 100644 index 0000000..e1e6a40 --- /dev/null +++ b/deactivate.php @@ -0,0 +1,62 @@ + + * @license http://www.gaslightmedia.com Gaslightmedia + * @release admin.php,v 1.0 2014/10/31 19:31:47 cscott Exp $ + * @link http://dev.gaslightmedia.com/ + */ + +// Check that we're being called by WordPress. +if (!defined('ABSPATH')) { + die("Please do not call this code directly!"); +} + +/* + * This class performs all necessary additional work when this + * plugin is deactivated. + */ +class glmMembersContactsPluginDeactivate +{ + + /** + * WordPress Database Object + * + * @var $wpdb + * @access public + */ + public $wpdb; + /** + * Plugin Configuration Data + * + * @var $config + * @access public + */ + public $config; + + /* + * Constructor + * + * Performs all the work for this model + */ + public function __construct ($wpdb, $config) + { + + // Save WordPress Database object + $this->wpdb = $wpdb; + + // Save plugin configuration object + $this->config = $config; + + delete_option('glmMembersDatabaseContactsPluginVersion'); + } + +} + +?> \ No newline at end of file diff --git a/defines.php b/defines.php new file mode 100644 index 0000000..25af54a --- /dev/null +++ b/defines.php @@ -0,0 +1,60 @@ +prefix.'glm_members_'); + + +// Parameters related to the Main GLM Member DB plugin - Depending on what's going on these may already defined by the main plugin +$pluginsPath = str_replace(GLM_MEMBERS_CONTACTS_PLUGIN_SLUG, '', GLM_MEMBERS_CONTACTS_PLUGIN_PATH); +define('GLM_MEMBERS_CONTACTS_MAIN_PLUGIN_PATH', $pluginsPath.'/glm-member-db'); +?> diff --git a/glm-member-db-contacts.php b/glm-member-db-contacts.php index 7480bef..621e7c5 100644 --- a/glm-member-db-contacts.php +++ b/glm-member-db-contacts.php @@ -34,7 +34,10 @@ * version nunmber of that release for the DB version. */ define('GLM_MEMBERS_CONTACTS_PLUGIN_VERSION', '1.0.0'); -define('REQUIRED_GLM_MEMBERS_PLUGIN_MIN_DB_VERSION', '1.0.41'); +define('GLM_MEMBERS_CONTACTS_PLUGIN_DB_VERSION', '1.0.0'); + +// This is the minimum version of the GLM Members DB plugin require for this plugin. +define('GLM_MEMBERS_CONTACTS_PLUGIN_MIN_MEMBERS_REQUIRED_VERSION', '1.0.43'); /* * Copyright 2014 Charles Scott (email : cscott@gaslightmedia.com) @@ -53,11 +56,128 @@ define('REQUIRED_GLM_MEMBERS_PLUGIN_MIN_DB_VERSION', '1.0.41'); * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -/** - * ******************************************************************************* - * - * *** Directory and File Structure *** - * + +// 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 - Getting this from main plugin +require_once('config.php'); + +// Required to be able to get user capabilities when being called as a filter from the main plugin +require_once(ABSPATH . 'wp-includes/pluggable.php'); + +/* + * Do some checks to make sure the main GLM Member DB is active and of a recceint enough version */ +// Function to generate message regarding main GLM Member DB plugin not installed and active +function glmMembersPluginRequired() { + echo ' +
+

The '.GLM_MEMBERS_CONTACTS_PLUGIN_NAME.' add-on requires the base GLM Member DB plugin to be installed and active!

+

The '.GLM_MEMBERS_CONTACTS_PLUGIN_NAME.' plugin has been de-activated.

+
+ '; +} + +// Find out if main GLM Member DB is intalled and active +include_once( ABSPATH . 'wp-admin/includes/plugin.php' ); +$plugin_name = 'glm-member-db/glm-member-db.php'; +$is_active = is_plugin_active($plugin_name); + +// If it's not active, then warn user and deactivate this add-on plugin +if ($is_active != '1') { + add_action( 'admin_notices', 'glmMembersPluginRequired' ); + deactivate_plugins('/'.GLM_MEMBERS_CONTACTS_PLUGIN_SLUG.'/'.GLM_MEMBERS_CONTACTS_PLUGIN_SLUG.'.php'); +} + +// Function to generate message regarding main GLM Member DB plugin version is not receint enought to run this add-on +function glmMembersPluginMinVerRequired() { + echo ' +
+

The '.GLM_MEMBERS_CONTACTS_PLUGIN_NAME.' requires that the main GLM Member DB plugin version be no older than ' + .GLM_MEMBERS_CONTACTS_PLUGIN_MIN_MEMBERS_REQUIRED_VERSION.'!

+

The '.GLM_MEMBERS_CONTACTS_PLUGIN_NAME.' plugin has been de-activated.

+
+ '; +} + +// Check for minimum GLM Member DB version that will work with this add-on +$glmMembersDatabasePluginVersion = get_option('glmMembersDatabasePluginVersion'); +if (version_compare($glmMembersDatabasePluginVersion, GLM_MEMBERS_CONTACTS_PLUGIN_MIN_MEMBERS_REQUIRED_VERSION) < 0) { + add_action( 'admin_notices', 'glmMembersPluginMinVerRequired'); + deactivate_plugins('/'.GLM_MEMBERS_CONTACTS_PLUGIN_SLUG.'/'.GLM_MEMBERS_CONTACTS_PLUGIN_SLUG.'.php'); +} + +/* + * Register this add-on with the main GLM Member DB plugin and get information on all add-ons loaded. + */ + +// Register this plugin with glm-member-db +function glmMembersRegisterContacts($addOns) { + + // Add this add-on to the add-ons array + $addOns[GLM_MEMBERS_CONTACTS_PLUGIN_SLUG] = array( + 'dir' => GLM_MEMBERS_CONTACTS_PLUGIN_PATH, + 'name' => GLM_MEMBERS_CONTACTS_PLUGIN_NAME, + 'short_name' => GLM_MEMBERS_CONTACTS_PLUGIN_SHORT_NAME, + 'slug' => GLM_MEMBERS_CONTACTS_PLUGIN_SLUG + ); + + // Return the array with our data added + return $addOns; +} +add_filter('glm-member-db-register-addon','glmMembersRegisterContacts', 10, 1); + + /* + * + * Activate and Deactivate hooks + * + */ + + // Activate + function glmMembersContactsPluginActivate () + { + global $wpdb, $config; + require_once (GLM_MEMBERS_CONTACTS_PLUGIN_PATH . '/activate.php'); + new glmMembersContactsPluginActivate($wpdb, $config); + } + register_activation_hook(__FILE__, 'glmMembersContactsPluginActivate'); + + // Deactivate + function glmMembersContactsPluginDeactivate () + { + global $wpdb, $config; + require_once (GLM_MEMBERS_CONTACTS_PLUGIN_PATH . '/deactivate.php'); + $x = new glmMembersContactsPluginDeactivate($wpdb, $config); + return false; + } + register_deactivation_hook(__FILE__, 'glmMembersContactsPluginDeactivate'); + + /* + * + * Determine which controller to load + * + * The first is for displaying notices in another window, possibly for debug output. + * + */ +if (is_admin()) { + require_once (GLM_MEMBERS_CONTACTS_PLUGIN_PATH . '/controllers/admin.php'); + new glmMembersContactsAdmin($wpdb, $config); + } else { + require_once (GLM_MEMBERS_CONTACTS_PLUGIN_PATH . '/controllers/front.php'); + new glmMembersContactsFront($wpdb, $config); + } + ?> \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..7e91415 --- /dev/null +++ b/index.php @@ -0,0 +1,2 @@ +N!*zd$>jK;Rn*tXfEv2EM7ZL90XX>Z5m zW@o}^Z|H34%xLdmYG-0^>}qRj=fdb>>fxgFf4!Nl>BK9xI|%f@H{i|GEL?4k>uV=agRoeO5%~e*5 zRh((#`!$3og>Kui^&DXLSIb{d5~{*+!+AVUK5YeE>C~n} zn>v=-o6TXn`V?kPXr-?7t@LtCG@B2S|N0}=-p9D}t>^Q#CxX&b*s~JN$F2%Pl^xx4 zx_aPJ|KkR0#P9FhyqCCZ1;Mli-_Al*znO?FajM2KqP<6O>UL&&-|uw!MYVx0nxvKDIayhP?|n(3gap6Bcy>CH}?w$?W-1?B~( ztz!Agxl8}g*I^?6x7idbe_(bo{zHG!D{(~&BJ_u zzxgux^VMeW<&F)CpC50Bf<4Fb_S)S(&vT`+o1Y(#s!i*ULwNRQ>mBatjJn`BN*`;^ z)3z%=x3_|k1RomN2*`A8*e@PrWeI zY~kGea|JATcz=pbj z#%{houP%4HZHHok_eEGXN>G?u;not1Q0Llw-U@!+jsqLf^ETcC?9<~XKI`5!1n^z! zKVRyF#t=MjM-}~UPW3-|65y3z7VG^|_reLij^YS-C1S80C)t-f&u#7L5TetZPHS8I zo(706R+~?WKA$A3o;N?=H!r<6Uu(K&Q5aanBp4d-vyM0Ktl1*`O$E0KzFlRjgRoGT zes0b!*XtvzOX52Y{@eU`sQuC6cO9)?a{YO6`GF(_w^nC8}9VHMG}0w`fpj^g4#abUlsHCa2$GH z^WXNU-Wb3C1Z@EO#qYlEx-1MsAeK`cFYwZSTGw$q#TyBoQP=%qRm7mvdIfC3@w)q_ z@22k~2}xb+4!qY-TS&YJK9?gUKTCPsXuXf=&$sCjS;)ie%{Q~794^P5djI!R$K9dI z0u%0k5uX2STYUHF^aD#~izV&XT7TJZc7HzDf4aRKmv8#Lo=XzAE_6L^$jFLnP$O5J5HeeYp$Fwf7wVQP=a~HCD>=d4>Gx&Tb^!YsPPq>i8O^WVi!KOC{-t)SCX)(d%v;Ac&`|0((Wi#IY&G_W$ZeCIF zc`x}6QYPHM>+!uJvG)v-e7>zzNJ_xhjr;ev00GP&*> zo@`8O#~WR|YtQBWFH`{|cus!P-W$cN?NkO8h$SV)t4)?8<>O2}Ih=R#ncvLkR7QfO zlPPB*l@OTH>9w40Cpi`K1#&fnih3co#C=}g(G~fw=lOhIf!$si7Le#y^w~idg88lM zbNwg(dBQy6;X2;m=VYOh=y^o)>AerxPv|!vS%O8kR-;)W1hK$zNwV2QhVYb0gV(*4 z!|1?|&6lNAI?X)ahrdnq)BjrRTWZw>63U7tVuQ%Hz*PbT>X6 z&(O4zW)@pIkOl?DS&{!9qqk_MAm0a@GttvL#}OJF*@&9pCpap|an-W?T4+3hB%($B zNEYGyhSHUhvf|!I7ULlu@AGChFsINm9BwCa%(CBm zF)L)+!*K-hlMp_nH!Hu4i(NwIqnWy4zw3K!L$wPefbaYa?DjawH1Cg*B=ii?gJp_D z(6q4I8-DD5s;P&_e5x;wr(?fqd5zR}V_W=wp7FlVxHVBQ{Rp8GM_K_X|Pe zHlM<8N5mj4qQ)3S9I1dxN?qdme7jXgwxDT+oP= zWriW5M?~5Y;Wpnd+~>i4pxlVb_WRHNHZIp_iROcyqD&A=wUl7ZZY>}gB?LuKuV%t0 zQ6b8j4817TKA=E12d3B}f(L;2?_Pd>T;gCsBjHE5N$czXw!zUS@;+mc09y@WoD$VQ zU5KNMc%-ZEOj3YAl2Smvm?<0p%JB%5-dy#JW`mApK%#|g6P8C&Ee;9;K^}>Lg^S;D z(B|6#Ck}Byjkqej|7=YmO3kg%7q^pzNaf(|FkyIuKwTad2qNQ0!H-=*Z~|+pP}BmV zjc+?#qcB+}7A8WXi_~$9AxeEo9(L8fLr&v5ykp5SSN7U8)2U8d)#Cg?B#Gr5>l>N zXj)wsv!o?RFZHu1ngIc`V52aHLIGCzjk0WnPm;s{ zJI)%$v8x(7&FMn0)0M*#tWv~%Hi)qjJNSsX7yQLD{7p;xX-Kcoz(g5PpksW$_DS6x z6~{|31Ypca&dfLK_7zp?Yh#)P~%5OJog2-Cp27{Y-h?c&hg?BmM|B{0LjMl!wGLf_{@rROsNe!vX zfpZL^CdU+KZvD0t3>{{pkJ}@J-OS7>_-BkrRHg}`FzmF69q*}Rv>E5f?H85qY$T^Z z$IH(6iqtQyd8kJ=L$PR5BM`htChc$27&8IN7}pUMQ`4TToA3Bej^u>!)$&>m;^@}m zG@in4gb08h>KVk|boYXtSAE(nyV2m^CnDBOUc!1(bE@q0Utst9-dT zjif>gjjcXUNNvtmhK-Ed3Ilz`=3U62s&E)tbX(`E*mnl!qV)%`Id%7CmxBhd`E;X^ zt6`j-$0^bf=h^crq?9NSv$9I8MO+iuakWWH!C?dd^x%7cvM3scFpBoNZeY7oQg#%H z7+cfe$qL|usa)QUc`U~{-wgT|D_sC|0+{3gz(sYs9d61?k=Efr6jA-&25{e+~SE-ZzwtZlhYm)Cl!=HdL` zKq}DFUC&qV3&RP6_?~TWfQEIQvWP2|xmfo1u83PvDOc)bnkMD>ytB zkCV`G5EW?mgAu4?en7(~^xRAQ4~f&1=b$;J(Q7rBj;8@YhX8;x01`&{v+i|TI}nZx z^Zn9mxx?%6g5T@Tq7V*j&mX{cLW?OhszY%EUo6L zanX)Mzyq+P#{W7^ecM46A|8iQG|(?QZ*wn8j@Yf2t{#h`8QVWzPUCq_Dujcfm3d_j zO@W2wbv?K5?_oUzu&cp(scbs%314vo zFr;1{T6$s)id%s*hYjuk&eHj`6W#Olhbc)~x5J9E$zdzFvgh;tavJDZm8#`cwr>EE zlwc(xSK!))ocZZ-ZA1=F3KK#y8Ucziz??-9M4%W9QACiY%5_0=+E?^{LGK4yve~Y( z6)*!$Kcma&54;J2639FNL^4Qaq(WMH*7Nz%vhMZ|AR`_S7`;mb?rVV#MKj(PZRWQJ zlZUzPgaw|*rRe}%cPM~^1bb}s6w(979VB7qKW~~J$v-6I_ewx+-gQ5kDGb1FAVCwF zCmInBHaA62j&<~@Of0R0vz0Oe=K!`ydd%^gzrX}RL}7b zCUMNj6r}ZdWKY%%n(kP*aU6HpHuR81VAg}Snk#ms&`EMQo9%QSKTc#XQBrTl8!1|f zkCt8VL#3iG?atr=nxnW$tbQV$!~s(&afnhR={bP?p5oj)iygRfQBe~9BpRVy9;5#J zqu)fm!#r_)|GN1p5OjN+|H+paNQwEpb|?@o5fSqG=>O?{=xgYzhP+rv(VjwpL9)Pz zyhFqO8a1lj$aS)g0k2{ZH%;9&zFUjo-w<1}h8r-JZ z-)&?oWyWRVjUlnBmQSoOwHZsd;B#Abeu-d!85UsAzcyJm$P9?v?e8VT)DkVF8aiO( z%Pcd@@BiXrHANH7O#7}N7xl$DJ5(I3hy`-EL3|}hMN8t}cZ=8%Em6j@uSu5c^E-2! zDV3EOOdz@lCJd~}Btl%-A7v@u`!P`d%6N=v^X(er3%88i45&1@D#@*5H)6+l)Jd1UcuO8@Jct+UK7s`Gr}8vhs9gJ7G|KTdzx5-!D%C{59T zII3BP-JF^IW^8a@5%i1_`!r&_-Cq+VtTAeik4s)f<|)TO0(VbrE#d(gX2IfzQ-jmM z-JpKhLK|+A@N&Gw4?l^L`1P%JYrOzHN5Y1x&BcKffh@g8BC|a1deP0DEygbb4y3k z5YPz3k+Z~T9Jk%7unPKa>Q*xxGk>I(KKK|~Spf>_dwvVwzPzLn{3_DUlo2|*+0y?pq-ceB)1 z0eJUu?V`w&bJdzmWt7#L_0VAR<%;Dq-M+5^KmpB@zf!_gB{E}Hi?t}(VfyQKv4%5N z7{v(o-kV515&i@v%EoT;&C)c_@w|EYKRqHW12<@NroU9d3$!rrzRmOP3Mh1LqXYj@ zZ|AGjgJO-VjIu&j!m{EvRKYuj7erl@G`Vb!*sp==j5rA=5oB@1B;N$K#MuJCy-{@8 zP_~G1JHtgGV310-{u5pe%TEvH~*1MTy)dx6pmu)=TuH#Nm9?(omx~ti&0dgy5ggHOBnPfJn|3| zH36?7i|0$?e1L(iz)T`-QsOVa$&=sB9D9xIL4qL!i+zg0)hgl+Dr9R>N_dvNESDEO z3}_}CR3(%C78$_2g~$rWx`iWDUds>totm?{MhhGJ$2{EZOrdCF*oiHl5*dY-#%#nW z)uNz>!1W-yYAIxb=l7 z8qn0l9kq%U*;9X-7KWa1^TXl9F}J=y{5v*@uqdgU!!DGcyss43FC^l7yBcs2_QHdQ zNdhU%k5*t0pr0Ql>gYV%YT#aEPT7T0{}m!j0_u!3Z|!@zy7HpPJ$@8wA*{+CRnco*$Aw4|Z=p~Q8d&R$ z9w=x^<~X;B6UXxC_!ARbFq36yTqxBpNc80toW z!wd?a5GbEI-KJkW8&Q@Z~nbt;W@vn?JQXg#nZHIZXWGIMD!qDnv#>9ITZcf1lV=DQ@ z>mG!F5`&<^X2#^Ma=DnU0!<4_wg?(P;zdxqMp##ioD!R~lxadq*mLy#9?Dn1;8E}K zGo1{2#_}jvj8aqx8sg}MjcqPkgVMYu%2ESf#eeL^*7#g~!O-v-5a338VZcTZw3Bm2 zm7pSmMqr??^5#-a_|TKlgyTJv3bUB5=7hT%@9k_0$sZGoI%I$OFj*DzVUF zcPtL+^*JDFrcy?u_#)+w??PNAOC^;4uFp8p{EeUo0>kRZX&-0{&^BiTmyG7%Val*+E zYKtZ-DT3;yyCz98A8hoq#BS9MCwE?!ToVg2&<^7&NPYs8^J3VD@)tHFmh+G*eQHq@ zobY#7#h|lv+7uVU8F;B6y}kl1JeMZwCNX}dLtgBuFpH%--xm;>FBd&>hZ7TSHx93x zO>2!yjtF^4H;cR-k^|31wfkur`C`YtH!3ejZynFTjIBJl&ouWT{u4ZfosIc(=QPX) z7lUa8eNK>jnp&5~_@$P4ZYu5z&L+h$eg1F(P5A)IC8mhw$F^_CqYEK>+xv|Y3O6xR zszk-_-?X*48a_{#cYU85Y2-^5p!H(xngrD zSpbt^Cq&zdS>Ya9SuVQ2*jj`Aubd^UP2_!lo_2}6b*{SY#-H(@m(J|fGyVHxAHp<$ z&qvWd5421nh$(FB!@e?-iaE2%IF4;OHI#qtfJqd^{kGmhqsf6o>IrC&+A~Tk%?#{( zHwktYC604zb2W2svXQnfxAzR}Zc|0=D#yzrcwxf6BxbJ1p$u78qj#$bDvO0Pjn&mD zc4cMjO*4PK@OJz%$A2B1b+BxStAX$0z%+x>N~H)QNewLx`wgxn+vF<#3dOv``?T;$|gGHQ>VQWgH9^x==q+44yB`iEw z+4`^3ng`aMj*Cyve62?!ALK4=V6Lg*fNFg*;sAIo7Vi`HW6vB*fSoX515!_~Y#ufn z`O4r2h^uw(7Vso`qmz|i*$RHoqi6iXdJ*D3GppFQ+_m?A{ipxbkF_Q;>ZH5q+g@GY zoh8kLpa*D_F(H*de%pYtSPaYg0)pp!kH7yx*UwvkNBRSGYyUJ15if%Sf>>!p#}crd zv^#F^0Xqk=Es#0g@J?jvKa}#tO)?hO_5FUi~E2v5*mMfub7W1N4K#}?ZqdG3a0Mq>+?>oj@-tSe)7x3Mpj&X-z8n=d zOB#zoghu>w?>g0O|CG2PDQXyF5Er-`3trma<1pwApx@*q7cJ14Iv-nanSHlTBNK>O z7{f$9&>j@UgMwJCgmW1 z=r6CR=VXbRu&+jLkhv6%Jj{E^dNG~T?=KUw=Hu<~Q?%7_7dWq@wmHlq(@6&qJloxz zNWVLzr1sd<+nnO^-oUh0JkhlNmi<8za>p4heaNs0TOwjZN53PxTC0wOw}K@aE*p=n zC4Qn&n1@m7RHfM>%;T2D95`}f4OW0MDKs9=?&99Iz`S3Z8Vq1H9m8Ort^L8NhF{;g z`r8(-Jvw{%Cm3Ptv`{h&wC6PzJiCos{$Llgt88FP0MXTR1lD-wC=#%dNYKh;=e7@gJay~$C{UdhR zE$7H9M1B?~3xCU`|Nb+e=_JBrWF?kyL{`Q`EG%hWfp?OCWYuu7N*!Ri&ek{$>nDaM zTk9y=Ltb>4b_yudjSTk6XG36jJs!g%9rIt5g$ev z!+f>>)zGf%USnrK*(mMC)P3^WLad2;)eaDpqOq9#yb0OR7vn(c1YJ>bGsDuM6+eC% zL0J?KXT9zvD~6%7ab^Z{4_S9&A@t*OAf@A04nb1P-IX^$98QexZgm zRA7h%jjT3XOX9PJ5K^0j2}3UUV6}neu-;?HWYjuDi_350*Ee3_v(z z5KHBmc)VQ?i*jFQP83ni*p=rv!5f$|&J{}{!)Eh)b2JwlDSXzzz(do3Zr@g#G{b!MUWE5v$!jAnp3pqZ6b|d{ez(q(I9)p|3OY( zdteLrv_eQ$?$Gq;{8gQUjjP+q`Z|E36yP6$^MuIz9I9YXv3M3Y!iUa))Z@PeL4+b# zC0U?xT0PF#F~OP3;~KTk7Mpzx=ll8)>US#3cAl!t;2{x~2(dG!>o!R1ua4QorQYac zd!!-{RaKjx+giX`T`{5M8B66=+(M&aF|Lyg1=&&B(!nP) z(n?3!M7)(U`NzCRk&6A8`l~z*i9?5wqG}c)W+##;SC)W>t%87>JZ3}is^B3PLVamx z4z^WlC@`%$ZLqPb36n3k#l-B^Hu+Qx6>ARi)D)FG|2!gkpt7pz<}9YJbC!Pbi138g z*fn)Ume-_~q+p6F#&}Am;T2Shu%HkpXQZ>V7R1f`0nAnrv;+DH4YppH@;;^?I=7DS z)*P{Zs{<-kw=^jFxxd38l)how2sqJ&YRs0F;K^qm|Dux^bM~7xnf->MYnRcg6X|gC zm2aywImXP(&5)u@S!QnJx;b;HMgwyG5h@SLRvvCP!Oc6+|JA^PLANUB#~Cs{7xPEe zL=gwjgjm>fXgQm_D-ZP3)_m3NT5U4aGnX)~QnXPqZ|X0gEkkBJ;EN?Jm_y_48Z?yZ z^Ji$=Q)0W#CUMy$F(@T?4puC7s8uFPo986CCUbdP{69b7s{kHLQpA^#vhP)0zUukC z(}Tq_@|XvZgXTTl_{4vEf>D~1Y-BindpuxjWJXq-%={KE&AT(R$H+X?{+q5M?$7B` zt@F~swm~=Rq)vyMt=xV{jcOnak8J{kd@&CZrD#CHdv7rMf`ZhT#ubdMzxz|bhn>G z)U7*0kV41(8Hd`L;U^i>#`(nd031@2$D&gh_k=K^`Qtm`cD@>DyiCAHoWfrC5A&ep zxZZJ&T>eiyN4>@>Ztla^x!+Y)n&XSN35wSZ*2{Hq!jP-Dgah1vs#zYM;Z>G=G2YAD+jJoa2xT_QooBsZfh9I_QK0uUrD|GowYF*J8 zt}Jw4?lPr6^ETIW{QA=c_TO^QoR-~02v3@2)-2Qyd~I2N%^{QL9Xyh{k9~fd0BN2O zYITjPotrJ7g;l$gVb1AcZVYs4tmX=|xHpX1^pf6jY$FgONwq18b zatC@4dN{0$`#zYQh|%8{sgxgfZTkRJ++zwR08zm0qB@lV0Sp8auNReFiCW=nTy`t9 z?5JP~Xw`^7P%5EGYR7V?VNDK;F|oN2D>l2&IRb_LY;lu&8{J`ZNs+2Sv| zlmq*Kl_9Skvh#9(>WWmXJFqvJJlX>|Q%a|-@ck3w=+LIP;j@1ka-ICJ$1{grmvuSK zCa`tGZt@JVjQGmrw6DhSh+nS;kVP@`kCIc0h+qgS$kyRfQ_KPlsPS2hAhn615OLXH zU6+d6$R)`z5|#E-q$Js1b5dx(lX?UYh|Ct?2rI=_lG|@VW}!21*>CV!Z3qa4%JHu^ zLlGeaU3~zliH_?bJoZj7dj(kfLVog;;=D$Os-xEerf^u>5_-4=xVhv}WTmkYqU@Gdj zeJCqIHcka9F*gYBLV@kK%m8cy$$vqKMKct9+Qqq2hN6CafpbS2P2fDtaYp((M?Xeh zt5TS-*Vze#jN$}dEH-j{Zbl#1-Sg9xnE#Mao=bn+f#JgcJef>;WfQzT^~>I|b#zG*$}gw4nY|>twK) zFWPiIQH-V=*a9QN<-Q%)VlaZQ!=$3C5;3bB{&mdfRE8*Xwf`BS<1{$l>FXTtAwV;-Z_9*zV zpNLwO;$2i0Svh(%Tl{RII@fyVO=Y)QZ9#pL81F-lG<%|1p;@oD2SV9RA<9oUDJeY- zFJzt^GZ?%Yb^zg7u$tc^3p^29S}q|yD|_V?26C0IQG@z3@Y0+#Uj}hFY_KaEbOs{L7C{&hl-yvO;gEY-RkNsrH~M3HK(xT6~=Dl&-0DArloQ z+Hy6KW(PYmuTcMyr*31ABVx6oR+Yma|5qcSoK0nM@vfB5gFn_ z>tl#AKm*~xa)-$}aRG)V!OaZE$XBR8s5nhsEXd@-G!GdhJju}&C(d~g1w!^%%@zb9!jaQTz&*yIGQs2`(lDpd6Jj| zzF6|SQsQ1AGRPFuVO2@ctceztdc2g2X?zP^Jo%f+Z8{9@n%d061>c#d3av=R;h629 zzSZHuxBy4na9-#S$qOH`(fzgf6zPg?(FY;W(q5rbyvL;HY=`doU0TsXPP!uw8%lo7jE^A|fP zw|d+-2}{N|J4&=B+paxQ28>my=$R>IE%o)g_2X63-epSIQ9~yU=3mS8%_>jt-6>f28Cf8%6%1$cqcS&>Yx_QzP z_1PH+N665&*P_a+lD#nzMY0-6WO9 z7pzSX^;Q@4s9+6Ko$v#p755-aMe$9Ru}y4<*B*9VO5z5=Q>)H`(EsiFg>hb5oBPqJ zZkQ&*F>oj{NMcBhC^lQ)HVDcnCG_VVMQE6MgsKRWT+^R3+SeIUk>WneU}q^@SM$I0 zyaN{GY71r)o}ztg^3EFtg*t8cl;c>{Htb0i8)4k_BBiT2oUDnRO@|reJn=luF5M)^ zl`2HygIb*deliY=&Ca&!#l#?bSHGaw0ZeZr>JkK1x{IcJuo*F75lsXA`~+}OE9JK! z+B-3556ILyf8+Ef?FSCpkZN)9B^RoxEEKg#HXVdk`7X%WuELH?q7D0ZtvEGHu3{8KQ1lsYXw1)QcK})TE~|h5}B0 zcZ}kqu|7{p+4YwJQ;k1i*!u9~?CkL7>Uun5>Jpd^JSx|78?Ip%3UqHHP1XP z#C4-^=7Asy7X9~tgORs>c3O!pAgBnrn9;Mr6;Q+Tuwsk@&;J~cEt-)SqU_Wg09Gp2 zLAxK-=w~34YAZJVc}gI2uOGh+THO~b6X_)H2lB~;K3BqKo_cWGUN1n*cW~LM+wXj8 zYb=!x1`*ffAfb;9k5sxJL$}(9zqi$d$Smp0=Rk$p_pM34J z%m#=!iLe!Njg1+94^!bKzZ4*6H75&Y%(UnL}pBwI{=gk zx60|Ra~I(r%0hL7P=W(Mn3=phF3h*g7@x$JW z1iXEjp=E6cbEK)bcIXliB)Z0o05v;Z&+C}~ajiJ}Ud0a)#TI6n*X8JHCz?qGQuG{< zbU=TjFzKjsC(7ls%V=OH0Ps%LnSziuAeaT{E|sNY7btCgBLI;SMz}2l@~1$BayppE z>`jaV^PCZN^)Rj#?#W*BnL`w;;0F{BAa>&mWTV0Q0Oeic@(H6acsWprn_lp=rm=#o zlEZEdHpqNBZ_?ZBo6sadIpE8JW4#bQ3tsg?sO3xC(ppfTE!E;dr8*}wL4E>>oq|9& zi1a2w=?d7%jc#9W3kPVK11uefz9b6SyZSu=p387Vyx*5dK9z+IY4&*Y@BtF9+ArZP z9ZEm!4_v4hI-qsY2SHyZ)ug1HnfW2{o%+9hYIUbs76uF&uDSW6u(?D#;^7N{Z4L!ekzrX%F{GKgN9dE&d@j>)|8wXUW?9 zZ3rE5K^EFo$zwMq5v`J@)eJc99l8^dG(ro94g_fj@L9uxM60pk(`h-9f#L}T@6zhj z&9Qqzw3>=Vq@>Qg{o)EpJvh{0QLz`zP>hsqTXsxT&d7_cm=#bFbp}VyNMAR=iN^aJ zMnXOET)DQvZPGTEiKXMj6vzse@JRWJASlqbdRHlX>=FC1t=Do28O6dv89mzHV@mGI zP)4!&JRH*g!CHh4dnXBD-yo;1+~5)QYV-)DmsxSJ$1~v_@4)!bl^g zkuM?;dvpDZ-m7ko2kHGThO#B^p9n|a1EieR8Cd9Q-F4;=f!$LbUx6HRgNf(2(C5UzWvb&%a8=o4zN#Z7&q}$rS7ix?-{9~l zoH(P$s}$rhLoC3+VmTCUQ#uQ`Q@J`s59#1*PZ36R|G8A>kH&HSI{8gOk7_gwU%N)I z)(TnX)mP?~6J?rx^mj40zL$0r_-;zg@_)>$=SGreY;iZlRO)!J>MLNu$^vLC!1t>t zikfQSU1_Lf8$#0)MF(llRK=?#U|v>>%4e0v7o9bQ=lp`f<3=2thuASUnAFAAB>noP9!3z%z4=MTZVvri0av8>t_a5x#B z!&u@H;{p-Ht8(a;izVZi&Q>*)kZ_1n$F=G%W@`n}Sq^$P^v>W5Rexg`np7AYq%iYO zkizycP8Ujq+}-s4`j~9=(FFoEp?L=w1*0q<0d}iP&r<)&dvvE12aEaY#hg%F1qORa+atbU8@dXVz`D!n_ruZLnMC zIgzCc?O|<`E(9OVixN+k6`OgQb6_`?qwh*YRE;o9Srl6cpp zrWP^Ia#xq|a6KrH8Mjyqx- z0|ulq1iW8;?SMF#11uO^04V%h04kY;klyPX0=i`;Oy4&`nKl|`u53O})=GfsBkkkn ziZJhkwXqS$F^@;(PXwVf8$9O5x@D~ zNaB8&YRJ-;|6<91cI>(HwNd7YG2ODUzLiSeSOUI*Lv$qDcd<>Xwy}g$iJv~eqB;D@MQ4J&kHAl+ zsmD3Mfu7&JUjXfb6}iU-Xs=wxsCSrqg?bgf$%bna^vn=NoF!pj3p4DCsB`CcAB%$h z+Pg>D%4j$OVThr(Mfd9?hFC}|zS8S=cLI{3(nhnj=4;h(lm$#Xc$ci_%Nj11^^XaRM%fO@{gW7O8qHf0W8abz_dcJu*EXzNF7`APNK! zZ7G@xHYB&S?2db$e5~t6AmC}z@9`@bPL($DVj~ploqA}qNB(H%D)|PLfK{IL6haN2 z0;qD~m1shdGX<(XV?1{=qK1G+v+i-+PGN75N~rlvUfE7vh6l2?3{Fh>(+a1afXFYE zOW%X4_A6urpq#M!VDP!awcsZaMR2Y6VB1K=FcDqex$r8yj|Udi_8ruRFcMFv553}8 zx?+&p|8%h`7LQvXF7g^*O+_acZfkasxl$lJDw}h$u91iY*5+{w@ds_*digLY^?~V@ zFsie$HZntUe8d#W_~yRvy!T8&w7I(_bl#yj31vh3KDj7sDR@(K-uEY|6wAvR)3Z8a z6d`R&o49iqS_=m8d^DUP`Z`-kTW7-Xc@%dz*1~dIQ zR32>>9t{h@Ew^sRt$iAsuUsy;Tv*0fXd4?=v@ z<&`cdIL&kvX?$9#j+JnYW9*rhm~%aq4+Qol$$DMO7|#D&v+;>H8apF1lM1%MSyy4T zYNKi^%IP}rpr)|5W?YZ_Lz~l1DDr_>CHk6m%ny#u6euFf$-Uc#Op{SkkphN;>}yKv z);H9-c5V1H+G>*0A;y;2NVmWYkK~667ZO`ey2YKDDB0W%i(aKr%58N9SaLJAeV-OE zSG3sFsi+8v+J_5_J#;I?0UdnN2o#V^hw)g3%7AOJOPU@-=~!g0wym{pfH!k@Kv+Mi zi0bp1!i1*Ss2*&s#p0;azPLx#bc|O7(GYJsGX4{vZb;JhGrQuAd8+F>f%EsQGBN{Ftu=Mc4i18Z#c?+D_MQ<>#(X0F^id;?J|2Pl zSn9i4dVI6vIBl1fsQisEzE_sF=0#PNj@GIh7PU@INx{T}k>JNEQf;ZMDR+2=ER$A4 z$Z@=?V2TUnxrMUapB4a70@-c9_ZJonK>rXuxxB(d0dXzVxSlStXrc2knz~-BWy+;7 z>aGIG+=I7?SU}xb=S8lgn#^J!7e*(DOHA2I5wYtu$)Rw*p+B@1+4Bd+tI%Tq_5$!d zqEcOsK;4^ATULg9te5*T_lDn!ixn=7>Bn!m4#^Fh_4dWc?V(saPk<410x`v4ZX>T1 zom}6Wz0nO%$vo$S2`#`1ObjG*2XB{W1xyVv_E zN`>8OfjW%bp`ZtKncOq@D>5W1$3+@*7@@~jAZ}{kql0L>|y zvji$=b(J(T0zvNIA=aWeeiyV@US(j#r7o2m1QU+im+|aoyMSe53tcJGw)&q5DluYq z-9?+NRoPjMk%X6ALP;bQA5X)Aq&e7UY;;#BUt%OfjYdQQ7t|I}SOqBb`$SY9li(yZZ;F(#LnCJL`NIjt1?_UDTJnOfnLNSrcn{FOPvNr?x z&Nz*|X&ra(ua7W6@q9P1`DRl&qampBpZ#B$#E6VC=nj93Q$HQ!3NJOGI&WJS`=1t? z7P^+ETzbd=Pu7$Py`c+M4#3Y~ z>tPF-gGgEvpLWXC{WlV^>y_%&qL8ZGs55%EX4_7MZk7WDeggaNiy>3x;jhHE7328x zvHA?RB>gc&n6Wl@rWUQ$CYp^d)Yr0@_>He2QDF$TBciR;l;F>|PXFcjLALoidr9Zw z5KUey)hExu?7}ur)#5^>s`ukXt@+fSXsp{B=?h?70*IW!QqHu@L?Q9tDGjv(vzr9W zEOmw4zI$XuB&z_A9hH>KYDdlhWP5CiPkJmV9=N!USrlV0D+@L0`ta4TuBtpDw-rOC z&MR=zkW9g3UVAEI1sL2r90ZO3@W?S)K@Pih+OV7Q^nFCVDwciS%?<4WEAbyOv^(NA zAxt5Fj~@cbR<=A6q9kvHLUpSvC7l$bf@-^6x>f1;1B|n+`Dgc0c3>M z*55khqPPn+jbH7EuI2n=h$|0OfoI@qDUaSgB_QZh8GJ5D@$q6k(H5gTP=0BUM}&WY zuc@68^NaW!57N_c zLkK0|eC5|JU`%e&-SYN2E+|VTB9GiMvj}nTC5VCm?uL%blk7Lz3En0ej`VU5F7aPr z8{GKa*EG^Oux8Awb+(7*V~%UFvcbfaVX~8Fz{v=-fdCfz_$zzELgQ6i(^B_S<%oZk zn zg}4P6>JdFO`;XDuz7=_&1G623-pJFBeR)>lP+Fp!{H3%o@O{f76U_QkxT?!4!;rsv z(|80%aE-?0Ll;OyG_=a)$*>TpA86E?)BI`Zj%ynTyU7ankHA>4gBKhl`#kfzI~&4s zF&#|chb!ycuP&x9k*as)oHR!RYp*ES=KwXI9nQ*m$kZ zYdsy67+ZfR$gLLpelQ{9zLb)q-U-SIc&#EB_v*I6$ZUjviL4O!x^bL-{$4$!onEf2 zj&b{6Sgh|0f0*IXf5kx&nEQAH^L z&+owGIWSrJ-#B$gfV5l#DSG`EGNO7)GIS>ck5F>7I=!x}iP;GNVVCUl?%Hz;a-HlS z%Mo-cEqF`buO6*y=YdfnA;(Msk$Q`Nzt4dLSV9m+?ZE%=b(S%CMBNs~o#HOV-Q69E zySuwfad&qu?q0MMcZ$2aYm2+y;k&s%@7IK+gp_ny;pyIsfJK7@SX;8oH=Y(sBpYEsDaL%Sux7fEj-`^T15+iKpqj3KR34FV?I9Lwdu?@IUO{;W?j? zgW=NBmQ634GzVT0K4bHz>x2ECzYsy7v?xSK$SF5-4``I~08@bzTd&a^5t$5^su)HQ z+gnBP42qlE^0)9<0R8a25DIp))Lyw0qE;(q0mcPKDMk<4Kbe~uhRb+NK0rzP6gC64MeHv9k_^okwmWCySMaz80VuUm&}Q|p#Od*$^S zej^l&5mSk2m&bCqU2WQlwZP+~5XYx?I3;45ZK-+lFNi>q2Z`*Gns-EDgAF4a63#}8K)Y!d zqbq#cWA*c?a0D3RenB!!trq97yqcxxX5brh`+j*OjXK2ut?N8OUax1_&su!~^t1<+0+Dr)#1c#QE zqIOR@+_7D=+d@&&qTR5wO<-mV^pk0fQqG+0-tCRlDTi#QNyon!bpVIwpSS$JtL*n? z1G0K}|J+K)Ekg)dF=^9^JgyGlI2b6bT)BxRThYG7(&PJy!K0sMFpw96YK2lI*|tFk zQOdY_?hWuyv|o=gPW%}STdh^u;I}ZC5CtC4xF%qnc$g;Gpcn42+1dZXyc-y%0$;iS z0WpPO$HoK4B@*%2cbxc*P>AK5RI&Fp-$ z^QCqG$5dquj(Bp8Ggf{b&IV@zWTQ;A90nM(&B>OdAow3I9jyPP)lNxHr*q%ATjV?b zHP+5B-83Dvlc&MdLy035@^o{Qj68wM4rZEf)-DnlBisy4Wf*NisMrX?nG{e#p63yE zfY93$%_mHT%iOPCjZq;6PrCb4w-puH+XTTgj*uUhMbXm3{CjDO-EbHoMCua$rHqph zO;(|{d0D|F^}y@7nEmaX0A#hg>eLQqAIQJoKwGMhu&N z&43E8-a3489kax62?cy?*U`kpPIM4Df;e(}nX@GxTc<1EaWO$K{!LSfV=94R03 zw8J`fGE;F!t+x3dU7_P@*6Yx6b6}U;qldJkFPgpL#aQdKxo zWNv&f$Sw5^4987)A?o4EOzq*YG>7ZAnq+PR>&E&p?wkUtdBMP~*?t~ojvDMzUzr4} z!)fabZl}FyEOeJ@HhRMRJboFEvzQI2!*_#BJMy9K$*YuR1m)1?3A#iHD@KJPGc{%6 z_w&j&&a%xVgSj0H(aL}_WO=J>p^SUVSO!9gFC_y>Tx%Lj+NVYj{j_1K5y*~3jNC`M zf-fsmLQ{aeG(N72#;v;gNhtvmH~(f!qI1znYrb6q#hNo<|Mfr9*oB5FvhSZATt2bzW1+h$KL>^NjVlQ>xv)Xrpc`Gzi-T25PAK? zxWNAGxR&lp8mTkt2j2PLpAORV05?KM_d%LY&kBx3cJCGwO=b9!u4N>{K~G3T&=rt0 z`S^^vekL9Hh+01Nf1>k_n@PZw0=x%E$J~WJOCGVAPh`!MV2wdHp(=5q-$VP>-SLdc z>*E#YhN}ut^Ti9kd(8UX{Mg!WVJO#cF9=NllLa_Cyg=ia=Xsb(8pmZknnFE{=X>Q5 z?tu@zk!pYw`^dc=01iwf;(%Ol#}WSp@xM~JA8h&X!P^T}8aK$3qo+GSJQ|D|apWh! zXQ_!08Ukw8cA+TqDMZ94n{5Ok6jG~D&a3J#)S&O=LXK5mr@H0dJd&JIQ+K|<16qz1bP zrvf4^%gyS|)P>YkY%oJPL!0YQ`9Dh4JAXXsDsF5mGqH#YwHGrh-&NZ zw6;k=zL$g-HiRJ(%)m*(xpE99N$e;SaoHd|oGj6CZ>KUamWiu!inxWOWR&a4L4{*V zQyM8p$dVkOZma%y=H`=wS6)N;=zZAwc~dB7+f*mV+vC4$Cf8(&2@umpw!D;U~{AcQ)aJe{!EOBmWB z3M`l80PGO4brckihT5kY&J9gmr4he;b{+VD=NYuQS^?(Tg6zn+dp_sY_G}8dEHPPB zU>{voc)h;fxbWkYjz~ums)V*`@Uh5|?Wk1P^F@k9N#>la|BUuDe)i-|qBZiXKv?YC zh5+!H+nxgg-aurWE(6W2FBTk@>jH7`-HOphaVC7y)-t zV}k27RF$0uo6lJvHUeUuCtoQt+SsJz$7rO5GKHvFh^j8`1&GciHUlbOZ$x z+6Pi^JMbIMfCh3Ysi5b24P^TDf%1f%8)La|!G+KeicF;Q&jH^sl$mNZux+VIQAV_J zfH>X~mqe8D?!#GG>1NRMdxS{|joR|3K?vLy3mtS4Vmpb8eg@?`Zc*hk0wt%B<1P03 znf?WPP9tX)8VSxp9=kq*VAg?-pGc(PdY0x^vzaW3F9PFGDM?hg5`APth3PKor~QbZ z;iR}??N|k(5ZAVAziLp~#$vU5k*9ZX7H6fk#pl$gE4q=pk}#_ksd1rqQ#C$EYEi(rk%{?9DNB8V5V)FguiD~0zO4ks>ZtVJ&z&{X6p)v5My8?r_p?fVCF=js{<7RG#3Hl;>!5YlwyNzPiYX`F644lYz_RAsW<)}Jekn+Ml|qch(kv$}@}Y(94b>F6R_~ssJ`AkRZ@?AzH$@yKyyS?%m)rKnMsq5KHQ_w?4-S&F&>f_Ii+1W2 z42BH(pNQzX$6l-?%c;UouppusBNX|3^5bpo(pleTK?>S7BAS}?va6W5&@{(U>M|iR z)Hts4`tp&ceB->=ND%UbvPh;rDe|1kg1_xUD2?6DgD{k<#BX?Gm@{0GaknZV%DdYA z8L^xAC}{O;=0!opLTYy+qUh;fm+~*N#3NPtGB}n_hb&A#j0le}qr)^OFPfn+Ek|V& z6YBMQ!tDnP@&jgLpI>{xk$U)$LepHf@DQwfPzZeH%avTU$XJp0FAT070je4k8R>r<= z)P`t6TR_o_;1j|w*Y*-4xB^13o#~7oJJpT4NK>Y*H<%ag%H2x7pon~D;5jR3*Zsob zfeU=peXtwby}_N9NRA$F7{Op7C^N^lU#|1vtnb?5Si}zH<6JWWN5=y~O~oUvGGpb~ z^Q`-d`4PM`D=|f)CWRd&dOVDf8{?bY3t8KYCxzlPNwR7^8^l3PW zrX5vc$>xumV7A&xGii9-RGaCVbomv0eM3q( zhs3bn(MLcYQ83kGm1zH+nKNrHP;6ZLt$-m3QZVA8#@^mGJ!v7N zx~~p>Wuf17o_x^ka@H5myof5|Jf%z#4bh(D#FtHG8+&+~)IE(Cc9aHL)=YmIr(*Uw ztg#rxoxM^!EL@uh{FBy~F(TS@3wUB&#?l$x$#XEXxm_q4`L++zAY(U6WU!dNv5~kQ zPlK`ndAVa?;Q8HvW9+I0m`t<)M1o5<06qe65)tNFu)Xphq^NwB*??~(#bqlT zy_f8EQO5xgiVeeGN8JQ!07d8fQSZVGVBo>U%>~Nc!(w0}ki+ZF0MUK}0}d!7zX9|K ziKs+cBiuHNnr@@{1i;v#@IdIjGy!OOLV+u=8TrRp20othY(?YLWk(YiyK$-kPm<)#|UHp1Cd%X@aJ#+94*;WCn5v} zd1GiJUNvODSW&D4k5qN~9O62|w;xwg=40N9+zmQKC<=F_FQH&$I=3f34SXwALqD@? z2kTf-6msh0P#7H5H26ClQNXjJT0uTPSaTuX%ygV^-D?1!rI^qk5()z#vmt1^%tVIM z&^v)MkbwzG7EWh{eGQ3pAb>@?0nk4r*MK!z z>bO~&N3c|XJFM&5p%`hG;Q?q8k#hJb1Ko|jOtX9rPa^*MrP>&WrQ=>;#&-=Gao)?Y zAq96P1FP~91qit@a3j^1Ab18xP1hTi+xY6jrrC!1-Zie*9|!QN%o%MYS*NKxfyo9T zXpDEXrcKMxc6kO@*kYwOTzN)M0Kx&zK!NvP)9EV4IiE5+8wShJhAtV}g)kmFqIF3D zQxG!c*pkCSp>iKK4yi>}A=5*QOb!AO1&Q|uWPo8)EDRvR#i3zE+=jJD+W!nz{pWJL zV@3Km(mr=#(M3j>-{SXGPtb$U@l+fS5ne|*xYbT7;Yky%&2G83=?@+{Sqr<+j8M5n zbcK_@4GB#2RIz!BY$fVl*C4pY&zQ{c#icYpQK{8YIR~Z{bXlB#MO0Sd-iq69^_~b^ zwOA@*)_3){PbK_PEqoS)trM1yy{7;5#H&t=GSNH%y)839D%wXRCC$O}oOBFxG*4Mk zb8E3+WlcPln|oZ9bz#G$3tztLtM?c6*;dSIwzJF00r$Fh54HOv7c6PEW>2 zzQq?}ZQgvFZc8uYW;$E?5tv<&P>)Yt^C3c^>!TJg+#G*)Q%Grs$a|G5d3d^Sh0>m^ zMhtCGN(&h7Q`K&6u_SufL|F5(MmbVB55mH6l~0aestfA2l8L9LwU^ek^LXGlPxC zeQ6eRZ`d;2IIc*y5=(;^jp<06{avX=;1x3%hqUZ)hDc*ZWgUjnu}q?Z8wyJ6p!ZRY zGkxNG040R-4hSZUij~>*RynTGT=_{mndT^`|ESv9_XR`^B5FMox@`n}Z&B0YNzd^%|pC+;cO06C48K`zdD!ra5yZhOv?TcBa|wF*1p4 zE_Y_8`pe;#poH3k7p=39ci9CK@G#jwSyr^_3_-Kz0Nn>0x|5KKVx(j}J3Y+sT{fD@ zQ>(>ZZ+%8{l)tIXI_ecXYXBfHKAMx(g1<@ ztsc>!_t`?=1}9?vOCnsF3!dk$qJcI>2&h-3ggqe#5HJdZS85WRI4(S6U(zjV*nt%| z6%PiV1GFEoRpXld)G+m1Z+RCX-!unC4~=AKiW8wWx-tPa68{~3u;47JkaEHrskz@` z6_O8pVBMzTXTLke!%W40Sj)lX_0<0FNEnSQ*{&%-}QjXsm-{V zP1cNn@Hou*LI(O<){xV7dAm9YH4#+txW-2DfSH;`Wv+B(E8NA!H7RzHr zLA=K@?IPK09G-apdl{utU>~s0>k9t^XvJv{nqchB{3smuPG0GIi9Za&oc z62S%*=p_fsXL6ae9zU+cMS@kJKZd>@9@uFBM#*()&rwY5mT>=Oio!QR8g=7-Rb)dI z@$gOKcHjVMmfHXs!9;LP;h4nMDkghcM8GISaI8Al(rhJ!bUv#@pd&ZPc#B8LhZM$Q zPx%lcSp<*Sj-%~_(yK>yF(Z0XFvITzb&_ul$a|IWG<*7y)NFf-2f!%0L6^>gTiI%W zPD_RUVCgyK#-z6_*NX;ei1Fx%QboG2tO$-8sUX9k!(u2MOkK)soEgpG$&FylS|C=ZTJCzRv_MK;cbKF?_ z2YctPuAWzKS~+r=E<1QU=ZTkCn~-8%4DIlCQ~s^W9m_W#7VS1igWV$q_=PdUj|4rvgs zqV%tLRTY*)R?wY2h3&hMJCzm1zjHEM8a7c;)|I~nnlecVyP$QMZsDz+5?IOKyo|L4)f|OzFhB?$KQd2eGwuQJ9gG5Da$i==1vLl1uzOv>p(Kh1C$xk#Z`lNn=ap-1{#m7z;9OG*_2 zQI$*M&$4ORD*`9D6!a)>y}*&_sJ4_wVsoJwuVLb_#*?`+Rv>W44j)=+z0AU8?m^#0t?EC`r zf=N~Z9`Mh+zz$n7ohCfddFO+EQp?oF;P?+tB%f^8S*gzq8B9QW%a)%vb@y&e27uY# z>n*7Oq;z$mOG3YXtM}`e*S71n7iC|*}8{*DEc$~|7 z7jq${G~{;jsBj?TZw6)bdx};@y6%|(RJNyuAvu4%0e8;P9Q<6VPZ)&85jr+HGzAEW za>%;J2jDd6wcYUdbb+`GJ?Qz8S=L%)GhyGMh_4K%%B)9TGiGQ%hP=8@m>9Vy5PxQ2 zK8$yLlupA|a`d+F`4s=C$2fG@`U2AA*-B&C3qb5^O*+qt<>9qe^b0`HBSXMpDb99a zIsPl5qp3kUmfHArf4bfMLN7IXOiwHD%?s;vrU;r*LGaBLE)?q8RVnK6d1u-(Ltz^U zBBAW;Vv;kml)3hM|E8ggf?>7m@q7z31fax+5X#*^RNR*m2Ig7}=_Bpb3=-){~kou}I4s zu~JoFJZfi~FUcQH{uoXq^_9S))4-Q5VOxUk(!ifp3-+)I_No=7Pqi4rY9db|qwv*d zTq{LAK4-}PBPH%4^cgD0vMQSj5=N$hYb_VG>pK|+N;a-?Bb5J8*iJ8T z-H}C4>hsV}`T9mg5f7qV#0E0X8L;TJ2gZ9?v9#<}%jd-STf&BAX>-|i?>ssyRBD3ll;fg#d1EIyOVE?FScEk_OH zOw}-&BU&s9+HR!iuTRFMJ|QD$_5NNiLGofBszj0TiCZWD2_Q8OXuuzVp@5NSo7ozMM|*cMQp(cNZ8Kb_~mx4!*w7R}i$^ zlwDyp?RKgp{yWt}%Ul-PqaVGFOQh!7)V?P&20`sH z?f?mf7*!l;k7xb|lTi?**WL9nvcw?>*0f?aCNvR}Rg>8mX4GCuS&zJTT|V4dX-P4jI3qatfPJY-_-i8IteD7-zOw8Mf^^JjS`|;s{@LzqV8Ia3 zJo09h?hKy9wTJl!RCz7Xl~LiCHo@3Di(H3Ci$D`m)J9m^2>iujA`8;EiRVrgVRc;3 zeFJh#NqU>J5+f_2gBVuvW)>QxjHDS*BIM8#?S61N93B9Ehn_{NvYb+C3T`UZ9>kt& z*=N8y<~9Z^3xJ;DpD(f*Y<~MM@e$27M_P}lLaPA3LP>5KVpcW|4ML(QT_)4RpRu;t z`m;EKl?aL^=%Qbh5x6v#zH^LWB@5v(E4i+V%p7u!b)tGK1sg{`;a-Z1gKIvM=HJo@ zON6+-RVcMr@r3CsS&HASAN;+|ha?gT>2bg+fsu?m-)OhsHrSxKIwb0}55Pz*o~{K_ z<$q)v7A6hg4X-NoFM`-T#38~ym^Em1*T!)f2nL&V>gDk~Xef#As28N^?={f0HPVy5 z_7ZE>Ch9G(w#oE|!1JBhXL0A~Tg_S~mbtH*W3+FIIN2W(#b&RcfJ($89P5C7ad0(a zDW`389&{{8c^TO44Bzgnd^A%)T@uCBc1Ix&i$C7_B;+bisr8sG0D1QbJh7z$`@QJZ#Y~BU`fj5ip@kmhc;V2 zHxochJPBZbkqv!Fz#LDux&<462MEfDJbOQ|M9FpB9=h?3| z5WF2wdM|y z6=<}Uy+HgtgnthMf0i!NL`Ww@#qoL_lT4fvDb%FEi!DW14f1$7NBM)E7?)m=OQJY0T;qtgr zA%+KG(AT~+0REIxMBm4q@G=(<5GR0m@YQ|Y$$17xC_B#0N;PMre|7Iq{KGIK71<%s zM#9M&Cq*!kNy?VrMXh!L)J!t?6F0QCzqixYBCIo;jx>Cbwiu#|dA0Nl$tlqO%ec6o+S8qz?QSm}j!UEB)Vc z?@W}i94Y;`F+9lCi!Z(Jn-Slns z@rdiCKYs*EgFHif^%=xr3A_Yj0h?PGe?A&$P0(Vm zB2dtzhK`He(6iunq1W??;&E8RjD`ykOENeOYeoIKVPHgMy;3Ea^sg(+LIN`kP1|Y{ z**TyvHdP(|7PC;q>JHbnrC9SkZKX?If2qEG7+w%=P>fKb9 z+;wO9l!KZIPNpWrCI_rV6x-uQAw{+7ly(y`m$&gWm*j8(03RtVR59rI+lCRQ^7N{_ zYo&$P$l<3<36^N7OlYs*KJ+q}^{1_q$uQt}#wGoS6VEjmGmvs7T*8 zrQkbsuZQ6d8I@1^Nx>L0Cd3XlJ#s)$$-}ZFWKgI{vy%)6tzK@8j&qmo@|rRnWW_0KVkD#Lay z=ho#)i@WwkGsgWdhQW^KDucIEN`WibH~ao23|jSoTwn%s1<>{J<0tY{ZK9#UBEo(_ zTu6nQ$9@q2((V#f2V?211tyLVqJY(54njJ9ACT}QUN48hSp|WUkK%dHxdscgT9-X$ z#IckZu(bhj^{Lkt~|xp>GGQ@WHXmaY6WL1=j85HDDB8GLoTw z*a6+Xjg!_Ug3)iDA$7TB`e`=zM<#8pKIAEiVU>VttThz#M!IFKn6e7VM=rdxM#fV~ zwt9wn;Tsn*(yd&q(CJDeWe~*s6A15TO(&q*mWVt)->@$PultQ65W9h-VGjiy2L~|x za*s80Qu+#MMI^u$CO-OzyD0GMSDJrfYgf%rK62B^9?}AUV;)L1$mW48H@k(8{7I~ueCLpNE@VA(Pah+&P0XK1HM3|{|lO>8&58@P=(IXx}4@x9xY!Vlq`Qi{4HveQ_ zM5_$SwKczRQ`%Sc>(9eSvpWO78l5n{116oU?&I>X1Q}I?6L^@Iu_>rT5ybj$kUfp* zpOASyzgb8k#Sj$S3Q%K^Lj$g*9ZhyyyT?D|OOCm-;n%Nn2mfhjkDr`;Q@iUnnA=dR z6uBIb#gPo{$eTtKpcu{%(Yje*$1jSvH?|_1RmJs507o_=fDZ6`e;uKVjf23h4>7GQ z&V^NEU|@)gjYa9MsX@$0^0Bmpu^i>mS34V2t4D>T;3E*bd-Wo0P|4{_&fzuDEh-hp zP&9cTV2gG_%nl^{_jDG`9ZY0Xe|P7WP&Q$BsGy^xLo8@Io~@Ujfzz77#Qf6&Og?>a zexA(x2XXK2lwwXxtX<186#C&Lg6uYeZGG<2x;VvPM_Ft_&UvTjP3hONTc5XQw@kYl zQwgr)*jGMSKHrb`4cU^nGL{d)a{Gja_?dSHa$Z98hD<~~8S)mIL(Y%4&6=%OByMBZ zx&ep1wT^E@0L@9vKjIXsfoMEzdVfXnRobswMFmqFs=RcHdoRtif+z6xf_4D?LHah^g9(_xBob(?RuJ4Q` zKjVnq*|4emFJan@XSb9WgkRUInH3A8Fj0As+Zj#t_IczA0@S%Ofd4Ike z8(k3gaC1u>DmwobNTHoFodn^Iu~^xb5XK`w^B+JAmtD<2lA z<1%6)pLMn(6R9~X2%N40#GH6a$gp66gE?jh-~ayo+v2dr!^=BZUY(uC>mE03(B)&) zOfDF7tut;=x_^CmdA`AGelS{7U+-jM5?l=h1vLx`amIoL%j2kA3uPdxWwk-S$`_aiI#K&C1Fi{6IhZBxz}v7k(ZV8Jb-)uCIRiE=!;F@-wY9?#4?8=# zt)cz+%nV|eSgjG+i4MOv++0Te4yw5#%g9F-R;i?`D<|zfAh(*Rv^X?{QE((eS-3f5EF7RNS%P7AX32eNiZ~0Q~1{E zsx%kN7!DI|0SQnThLQ;QJPOIa7YZ~avCCIt_6{dYXJur-toSiyhKw)#1-5bAh8hgA zB|FRoG+Pizxt5WApj$b%w6=z$N`MUn#?d6DG(T25T~B8B_sOvB92}r`=Xqp-Vjz`g z5mG22B#IhFHSy;7n5y{>K#>6t5%_D)#NW1daZ%}NmU;o+=b@pYfGIxsq_?*hfNUt^ zXUu7tqF$c@A!$85J*G$w4_aESj!>M(;?hLzcRpCvHSon*rarKsbVh?9kbiz8C==lC zQePYzw&g;ANic9nSaP5RRWUo~6??dbz4ZrLNoh8fI?#smVsBl>3&7qT+XM7-2k>tW$a zLGOFI#5;bKJ7N=0vND*cv{+{*>&4pz)17481p#K?+*N)Q*O(mp`0NVt9Ox7c^$mIA zZ1W?-3N~UEl=kRq+cpHVZTc5!({wAXg=4m$?bk1kEb>g`Yv7=^1m9WS zV~WAhYm36uJ=clQ6y<|j?htLZPBW8mX_$(MvpH;*= zG7tMRFKOfiZ6=~ba9dLJrrEn%`{di(TW4n{N9;nTp)nX1XpnG7sg(=Mjh!slTcCvX zw#UUL|3Y}FyW`9845^$M83p$lhF%e2xeN}a{V);(n&c7jJK$*C?G6s{;h3A7E24<{ z+ooR{j5G^H{HuZ}biLbuYnG%M6>j%7g)Jj&eWAM9X8>Br*VosXv}P?%dFg8#tVech zN(R%PPM%*?Jab*01Pkon?Iz0^Gf_w83(^R(%XTae?SJ2vfp@rXc88AmK5J{gHAlf} zZCoS^c8?K#6Uo{i{bOa;9lR4k+o>G+iz0##P3I8Ws^1m+n)Qo$%kN5#4rt3pJ6n_} z;(H;=GG*05_L$jp?;VeX%6iOL%q9VOW;Nyk|LZUI33pCGI^38B;?Z_fMs|_5X1Ex7 zrlg~-!#d3VKM}i0Xw!DZX{~{yG?>+Ol1tOJvn+2F;Rw3ZD@VneJZe#PupK^_wVm$# zBr6fSsP-=h9P?4%Q&jBfyEbAX4J;VznZMLH~Q2E({FJe~#w;{K^0Q zLje9`Yvk$R=1Om3WMXM%Vrpngg=}nS2(xWwn4k#4vKxpEq9_9m)3YqWg8?kY1it?N z%fSObHF2$67&bXb{$sjl^$;mX%?Y@4Jme9JVL}mx6&Cf%qp2H|nU6 zq{8IsMFG#UamLFFZZ;KEbI4sd^!feM&-qAVd<#YbkZYkK@T%v0co)TP4YI9dB%GjMJWg z{N>Wd6v-!kuuO2sY)ZjvzV_F&Ng&!v?J40GopQpX38oqcV(bZA<+Z)?3h6loo~((2 zmFfy7`V~nBe;E(()v;7|0iqLZxHcX*^%(QSM~cq|ERU~>9rrR7z2&{ zG-%~ObK8dBH^KCB3!M=Qlz1&wl9raFwfRPuXn6menSjZ^AfdhI({e6TNEo9L@!0Tu^X$5Siejr$r)%#AbcrL4g-XKMHfIqLdXH8#Vhe)YZbh z?!-=-BNo?~hCXoOMs!Pj8N)ki-fc&3dj0Q<)31GG8v66q!iZ5%mouowHQ_A2m|pQH zvafR2Jo`~(AmsVqOQ1|2h@-g}loagn=(82?y%c=<6RrNywjkc4~el`RH18!p84&N>|X8M*o%4>5(J2?)P^)EARjXPH41Z9#gX>x*B@= zN%Z?QqwX;zLi*~sW5&OcXilE=BLlRz80WkefdCRfE1>IZzt;5G?di@fk=QQ z-I+5CB-yV=Y~GEN(1jc!?3)o%PeMGViJ-919TgRmscE12tCKlpHvSs?+l3cZ`8V?u z?OWtkY?xMIm2pO}S^Lhr9BMTRwEDC+>)KqkMj_we9IoS*NNy zOAMhR(r6~RM_(=q_mz}RlVvmhw3SW>4(^uXw2mOrE zFAfB>HWbwQ4jj21GJ|UGZU}bw+IUUoOnXn<+f%Cl-(%J4uzYd9~waOzIePzjA6650n>AHXuyHxZqDSXvhbr&?nBB#mqPlG&Win5m= z=jZbB+b*0`|M!cPLgRDD$fpdCTLa@m7WgrZ5HbG`%W?#L<8zjWYRbdC#pGnH%+~uF z%0tqn-za}TyTe|jaW(vN4OS=o;}(UQz}o3L-JZ%e4D=FdNg zsiusdFWBe&S>FUjWr6j6<-q!hOUu#^?ZVIX{l%H@XW`PodW3jjJvj|$CS40rwO6d3~g0VExz6>$CC!>(f4O1W~wR>~?GA(PZqTPm}BB6|}`>0(Au8 zRot4i7>!HN__mSHVNj{B0S|oSJ-06ZQSE4f@lNVXdcn7&FiGQh=y6E`(i`}HD1zSl zBqncr+m@lhU)m@90-x(tw)c9&JLWDlm3r)dY5&G0Y2mwvQwe9&6g6&U@Iw}s{im7K z5@+HA!$KkCFI-D%#7Z5=?}_v!yEgQPr~Z%DuDOMU(5>hBF`3FAHPMkrcmn+zHQ1BO z%jBIo(gv9A1oHj2o^5LL(QDMCe|EmzFP8GH@6 zFu>kH3fm3lK1O;e>pb6fW#Z#*Z0p}?gO4fs3VXnO(^3civMxklmki_2&asd!WKQuI z7tfqZ=RJ~47jjX}-v!r>HZgew`hX_Ln+4;}i*7o!N1Z9x%D-@53_|=jSYhzRdvzZh zHAwh9#KANpnOT(24vu?`*gZQ*FnYK(4#WZs$8mJJs^iMd9XwG64&* z$;O%VWrS2~2kY^ciE>L;96fn^XA+w0_G-1NGJA`<*GciZXZ=|n8vU6OityPM*GPp} zK<1hTc6NH)StqTxPWyn{UPH}Sk0JhB!v-%@98z;&Nd+NZAPgT)kua5&Khhu`3{1wo zF9a~o_EKyVOEmGW;t!}i4j;v=n8rKVySb;ZBXIRESR6HXOzx4eGy;n{!r;v_L!#qk z+noE(*7@$p^!MS#ynBlmM{{vxq)jk|{_S$*i{NR&SHOl@s}duOF^FOO_9dni9wlu= z?-y+gjzFmH9}?MO_?q?tuTk`@Oqg4|Kx0&LY;PAXu<`JNFXA9;!c&eZxa%@&A^3E9 zV5X?M-n**HTRrP=gkVQKx8U`8H7kUvN&nOh+)z^2_DHEAO@aY7=_SDy;lK@6oJ6N~0D#pE&Ba7=xnCK>WK8KiSA56j4R!=3bs>rnn$I&O6a zHiQclMu}CuSwsE+C1?|UU*%?~_&emElItOqJXsL`g*ao5N&b%o8$&#W(Bw0S6L{&U zH1p0MK1ESKO^0p2*y&Uri=zD zZy)QcQ+;1d5a%|m-wv%;NGKi-YuT1=e~dugfcz2_8B z&SsgPALmPZaB^d#oByuXvsD}c5mJ;+>Dz?w@nC$koXSD_kIC?$L2ePyBb4);caYL@CX^|C}W%&0!BJJ*Pw)l1;gJt^5|!78Zs(ateG4h@#Iu+|Dz5k-Tq}WPnI?RA1ZWgmI3~Yh3v35X`z&V| z^+1B);QQuVpmCHvmbBqZUBoPz-8!%sFfc4QgB*1fO|53D-tJ}mRbFn}5u0>yDXly< zqYq4}-`twT&;qk-&W1!iyu{dT->%9lZ=ykz>zUw8sbF}# z##X%7hn)`gb8eU`5;$h}*5U5YaL}tH=A)*E9|ghaRrK^Vf0z@K!GYJ+gq4hEP)Nur zQ#wU%mr6o*P|a{OCn52x-Wy30-Ar zBJ_aN_82=L22r)X4?9S|*>%1u{=m-WHzUXh6af1$c>Vg>T9{z1%#i?%NOtglYb`!I z6&EvCS1Wr9m(Sj!Q(MPnl?T~Ru$!dcTlzNwqHIzeamhgn6qF=unM0qx5VY1=;y%d7 zd$nmkb$S}EYc5Oufu0rPmS6ksc|QxL#?O4VZ=MEJo$b`Rfe6gI&Rzx{gv~!4;J*^S zxAl*Ngil8>$(ke_Y6mkni5UP149(FRJH z-hwkkyiYDiv$f8mr>`?#pk zn}1-{F8=T_$#UK?G6|o!`g*c-PC*4~HfJ z#U6U$COM5aZQZkZ`iG#hW^Kt>we@E?J%U_wwo-Um{bYUwMF8 z^`zNz&^Q=*m$W5+z3ow`h?{E>LxCH7CA_onaJQx`u(O4-hZ4Dp*os5rf=T1H0;e)3 zk~rkd*~m!V8OhbHX??L)G9eOtdbugHkllW9Obi2qC}t;)vf)dJN$EtI!V9#4iSCscabSvN_B;5yAY(`ND`pb2k_VNPyicB7 z!AReesu7L*&bzWSKDL(JrhI`SCHg4g?9|9tWigmXyg%QN9Baekos8AH*=BHXgUC?G zbn7dONB+{P+;CK8WTFU^{7+@q0oGK~wdsOVv-FOLqKHI#Q@T`P>BUe21PBm9?RxZAKM*&Q(`~|{OJRj6gqn*JF+2>UzE+tJvs!2&M}P+Xj!*f!yIKtQ^(WtM(AXhT zd*ee3@O6z9WUrH~HzD6t5-1Z$Mw&p4LRhZNP53hVMn2WG_7WS#VLVoU+<)b&u1YY` zl?_^U#z}umwsG7Du}NHzH}=H#63(5jp}1ad zmNBNu)hYQl0)MNJN>=q_>NvKLzF?`f8=11H{;9pUIeR+b)-aT26+W*SAG%_D8RJA*I%O`2@3Sw zxNmKDeu z1d0kKM|Q2p$_x@K75DXpo{xt0^TjbXf9Uu4x?5v;3Gew15x2zIHT(V=!|Z!1&yz(w zZ475lef0Jxc-EOwxR}x6BH^G4oJkfQ^d+U8J~m0kjRn_`_O9FPN$k-p;K zyVyoba`tMp#f}SF@-7QIQz_bkP5N&798Dd6y6&;y!USPm(5S|lb{L9%Z*3b+DvsCb zSSJ&Cm7x#Fj&KRw?av{kQ1y=Z6L`;5_NoeiGH-%gLY%H)`43@s2Ch%TJ0a};Zg?D0 zl-*F;Lify8{4@>xq;bQBAu+yEZEuMsp5b@bPj)G_@>7Qp>%+aGII81zk@fjl&5o0V zn(=De67xC4BhS*nA?HF{(iuo}&I_mq`{qaTPew37^Vilvi9v!%5OS6^#WE7(P9NUl z4@<=Tmn-j7snG;GzItbzM+K8fQLdB9teNN=9Zss%^PiJRUb?AyJxZ!iA3)uT-# z7QTC_`3Xw5iuTV#6nuBWLI%*Fq_1HioD}m+KCF|3aNQIVavTu5&J~15QOpR@dtqK- zia%KLrgz;4s%;^wHm(ceI~@VAm>YvTr=Q7fp;84Zc`_WUcDY`-V{00Fx1U^Q!HX2U zt)!>Js%W6Y$l238L*}YMT|8{nJWqD~mTh@jy#aJbK6R5=mC;z;5m`H89(Yryw}X?p zi@h%m9C|WU=p* z=}!3#%gNtle+w4wlg`367RQMqlt2QjXF%L_PHWQ?-T2ERbQK%MSy&mN%4He-R&Jg* zk&xK~ev%4*k+&3i^mv|!3VWzvsa(hBZY>q7ScXB*0mCYfGeo(_qvgZ8^GROk-9dV9 z^`e3jL~i5Mgk6^O<^eCp$~K z(!GAhd~ePU%0MtZNx&hjQ+EmyU6hI74OdDJqFfe`~GoaZ&FCNBP5XdQHLi$ zTNjT&j4TE?I_$y0qTVFOPih5>FI0_;fGN2q-34!X`)m8Re3fC*BIAW`|A@A z=KiGl>aTbM$lY|m1!Oyo+r!)|ZW7B5Li&s*M2@}Q5GqNyIhPfyV~EJ8<%RaCm$Ub@ z9Oyie#}BUP=I|@k=9WH!pD@fALJo}u$uO-lOZSE# ziRpLTU87FbOSp;l+CGhY&HXm_<^|OZWn$$GN17CrR{&yf2_eY{U14}_6T1}s`^^*$ zoK`uFeqyeiPEvjRsVISNLeEF`)QWle2m%?pq;krRo6#$E6h_z7%BRN0L^_Ql1xBU_ zy{^fUa|MPq+f_2vSDcy)%Nn@?>49$6ff5s@+z)?ez!P zR^vopbPpe^4R2kdsc|V{a$vL!S*d$c1r`zk-%={KDF;2HUz=EfBQ0d>!Vr`hqv_Qh4=PxvRmJaNTyv z6-32r)!7>wg7E||si-NL8f9zudy?`N!}(tD)h2#aoL?C+Y$1)?Rz><){CSK5`uOfF zaBN9vLJ+AH@SLNK;>HoVhx=@IHa6ad|~on2y=#u2RVJ zUEBDESc&FL(5YKb-dubq7fw;xh?fOJCqt2jbzS;k+D6yIG2)m(n)mJ=@9Iy7pR=8W zWv31+6xs1xlg~CP3>ii#z=ymE3Q9;1MGUW{GhaUHT)X*xfG#PPy90!CGA7T=zTd2& zP$BYQlM;*m*j`e&mO|7x{rZ(=;qBrQ_<+@vep*C`JikUFW1QUyVVzOd!%O^I&XHxm;yc1b8D zkh!ii2Ph#?)X~ub<;nPZeielWRLM=T8JOSrIypT$yhkW1yoal!)7hisWe7ZziP~VL z6Fki36>>F5-G>`su~7Szu{+Srk#Ko6&l;Ua6fS>DI@o=cbh>jG1SnIhm*THO{o7G8HOpA73N8)TwX}TV_FyhHEpb007Gr^8x%O z`$WL^W0I<-twc_az%v)FLZi9B0$910(^e}W^zUY;h6fVb1m8{sZbGPHj# z#a*rkcDcQ4`TPle<8F&PbC>&Ll8D_o+OBZY2pVt%&AS$NR2R_5)ue>f7IRD_rQQ z1EZ`k=^}$M?(FKcwA+N=E-+$m?-p(MvJk;R9`1@F?Mly6Rw2dv2O@ELEjs3(Mjh-TL!}+!V4GRcBeFzsU?nLS@x@4 zfg|5?Ec;cpW?$0vaE~oQJ>#v65m29-w8OjscMq-8C%>A|Zcdp?uSAcqt5Mj}ovilY zLcTWluxapR;y>p*zPZyq!Xk{pcQ^pGHr0JuRzG>(}1I-Z77^9{0< zaphh=nyvWS+M;*%5h*wE(X~}9V$??IYxf`WtqkHBh^XPWSuY6b0CuE>CG=W+0?o!X z_j^tSy;?u4KCn4a|4h@idq#$t@NJx+6A6g0u)yNKn($#_L@=-&)B@tBjzJlbM^3qATw25x5owRCX=J44}iPP{z!aC;Yf7{tzn z=Rd#t|F<|NXRx!2lPTEo7dYCVl;;eG+y3t;Oa=dUl!YxE>mW9H)M`2V>MxPPTdU~Gwlc|*&;8;k}0>5qnn!oZdgCmw02GYo9+^b4!~ ztd%fUvj^KjY=41c{7Iqqj&Msyh|`63`k56lc^?nuw4}HdRUh#D-M`Q)>uN1!yLMLc z*>y_l>E1UM+S$VTTAM^8xGH-SQvR*}Y+-QYY3uvi3Ac3K&U(*#^t&;8P!=bJ2h$w8}KYYkgRF#I59{6aVfv@Q7EJ0B?Un!PPAg>aJCFGpocm1 z=-Bh#-Hb*qY~^wzN}8rF(c8hO31ej12hb$;E?2glAE=Gl<_gC#lMYVZpgyj6d=c)L zBj9$Ki&7W+6#@Bn*UYa`Ep>UH&3mJ!80cRFQ;#KQb}nU+r6}WCM7yQiSrVB_M7QBB zIpDP;?SK*O>SmtQcG7E$_w$2sz0BPk40f1M0pn%K5iU;Ms32tFBqQ99etJzUrg@7aOk#F?Nqh{yCRTH1a|!(2u~2VVv)alneYm z(ElUy;@bavLpg<=2Yr4-T^v9#ae;GjkGL2m_z`!JIlr$bH1Yev<{R=`gPxawg@x&} z=YmN3XZOFzv)?<$IqpY23qR3wk$3-H4~BWqg*N%0^*QI^|5l6YPqdu#@_(x(@F!Z% zdHO$EzPT;t8+;>MOt(3gx}RwI7vBC=vtfApTqsihX{LX$_P>A6?|ne^C(16e_`mDH u@c6j|{Y20Atp2^tE>aMN*UzN|t?8#M|GP3i0p>OU`h`R@7f%}I>i+;I)WkRd literal 0 HcmV?d00001 diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..9b4902b --- /dev/null +++ b/readme.txt @@ -0,0 +1,27 @@ +=== Gaslight Media Member Database Contacts Child Plugin === +Contributors: cscott@gaslightmedia.com +Donate link: http://www.gaslightmedia.com +Tags: Gaslight Media,Plugin,Members Contacts +Requires at least: 3.0.1 +Tested up to: 3.4 +Stable tag: 4.3 +License: GPLv2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html + +This is the Gaslight Media Members Database Contacts Child Plugin. + +== Description == + +The Gaslight Media Members Database Contacts Child Plugin is an add-on to the Gaslight Media Members Database, +which is required to install and run this plugin + +== Installation == + +This section describes how to install the plugin and get it working. + +e.g. + +1. Upload `plugin-name.php` to the `/wp-content/plugins/` directory +1. Activate the plugin through the 'Plugins' menu in WordPress + + diff --git a/uninstall.php b/uninstall.php new file mode 100644 index 0000000..0702743 --- /dev/null +++ b/uninstall.php @@ -0,0 +1,28 @@ + + * @license http://www.gaslightmedia.com Gaslightmedia + * @release admin.php,v 1.0 2014/10/31 19:31:47 cscott Exp $ + * @link http://dev.gaslightmedia.com/ + */ + +// Check that we're being called by WordPress. +if (!defined('ABSPATH')) { + die("Please do not call this code directly!"); +} + +//if uninstall not called from WordPress exit +if (!defined('WP_UNINSTALL_PLUGIN')) { + die("Sorry, uninstall must be called by WordPress!"); +} + + +?> -- 2.17.1