From 869dd53bf8f31b4c54d907e5c3e40fe67d681add Mon Sep 17 00:00:00 2001
From: Chuck Scott '. BUtils::_('Registration Successful. '). BUtils::_('Please').' '.BUtils::_('Login').'
+ The category or parent category of this post is protected. You can change the category protection settings from the manage content protection menu.
+ Visit the
+ Simple Membership Plugin Site
+ to read setup and configuration documentation. Please give us a rating if you like the plugin.
+ Debug log files have been reset! Debug log files could not be reset! ' . BUtils::_('Settings updated!') . '
":"\n");
+ }
+ foreach($this->warn as $warn){
+ $msg .= 'WARN: ' . $warn . ($to_screen?"
":"\n");
+ }
+ foreach ($this->notice as $notice){
+ $msg = 'NOTICE: ' . $notice . ($to_screen?"
":"\n");
+ }
+ return $msg;
+ }
+ public static function writeall($path = ''){
+ if (empty($path)) {$path = SIMPLE_WP_MEMBERSHIP_PATH . 'log.txt';}
+ $fp = fopen($path, 'a');
+ $date = date("Y-m-d H:i:s");
+ fwrite($fp, strtoupper($date) . ":\n");
+ fwrite($fp, str_repeat('-=', (strlen($date)+1.0)/2.0) . "\n");
+ foreach (self::$intance as $context=>$intance){
+ fwrite($fp, strtoupper($context) . ":\n");
+ fwrite($fp, str_repeat('=', strlen($context)+1) . "\n");
+ fwrite($fp, $intance->get());
+ }
+ fclose($fp);
+ }
+
+ public static function log_simple_debug($message, $success, $end = false) {
+ $settings = BSettings::get_instance();
+ $debug_enabled = $settings->get_value('enable-debug');
+ if (empty($debug_enabled)) {//Debug is not enabled
+ return;
+ }
+
+ //Lets write to the log file
+ $debug_log_file_name = SIMPLE_WP_MEMBERSHIP_PATH . 'log.txt';
+
+ // Timestamp
+ $text = '[' . date('m/d/Y g:i A') . '] - ' . (($success) ? 'SUCCESS :' : 'FAILURE :') . $message . "\n";
+ if ($end) {
+ $text .= "\n------------------------------------------------------------------\n\n";
+ }
+ // Write to log
+ $fp = fopen($debug_log_file_name, 'a');
+ fwrite($fp, $text);
+ fclose($fp); // close file
+ }
+
+}
diff --git a/classes/class.bMemberUtils.php b/classes/class.bMemberUtils.php
new file mode 100644
index 0000000..60ce8fe
--- /dev/null
+++ b/classes/class.bMemberUtils.php
@@ -0,0 +1,35 @@
+is_logged_in()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static function get_logged_in_members_id() {
+ $auth = BAuth::get_instance();
+ if (!$auth->is_logged_in()) {
+ return bUtils::_("User is not logged in.");
+ }
+ return $auth->userData->member_id;
+ }
+
+ public static function get_logged_in_members_level() {
+ $auth = BAuth::get_instance();
+ if (!$auth->is_logged_in()) {
+ return bUtils::_("User is not logged in.");
+ }
+ return $auth->userData->membership_level;
+ }
+
+}
diff --git a/classes/class.bMembers.php b/classes/class.bMembers.php
new file mode 100644
index 0000000..7311dd0
--- /dev/null
+++ b/classes/class.bMembers.php
@@ -0,0 +1,198 @@
+ BUtils::_('Member'),
+ 'plural' => BUtils::_('Members'),
+ 'ajax' => false
+ ));
+ }
+
+ function get_columns() {
+ return array(
+ 'cb' => ''
+ , 'member_id' => BUtils::_('ID')
+ , 'user_name' => BUtils::_('User Name')
+ , 'first_name' => BUtils::_('First Name')
+ , 'last_name' => BUtils::_('Last Name')
+ , 'email' => BUtils::_('Email')
+ , 'alias' => BUtils::_('Membership Level')
+ , 'subscription_starts' => BUtils::_('Access Starts')
+ , 'account_state' => BUtils::_('Account State')
+ );
+ }
+
+ function get_sortable_columns() {
+ return array(
+ 'member_id' => array('member_id', true),
+ 'user_name' => array('user_name', true)
+ );
+ }
+
+ function get_bulk_actions() {
+ $actions = array(
+ 'bulk_delete' => BUtils::_('Delete')
+ );
+ return $actions;
+ }
+
+ function column_default($item, $column_name) {
+ return $item[$column_name];
+ }
+
+ function column_member_id($item) {
+ $actions = array(
+ 'edit' => sprintf('Edit', $_REQUEST['page'], $item['member_id']),
+ 'delete' => sprintf('Delete', $_REQUEST['page'], $item['member_id']),
+ );
+ return $item['member_id'] . $this->row_actions($actions);
+ }
+
+ function column_cb($item) {
+ return sprintf(
+ '', $item['member_id']
+ );
+ }
+
+ function prepare_items() {
+ global $wpdb;
+ $query = "SELECT * FROM " . $wpdb->prefix . "swpm_members_tbl";
+ $query .= " LEFT JOIN " . $wpdb->prefix . "swpm_membership_tbl";
+ $query .= " ON ( membership_level = id ) ";
+ $s = filter_input(INPUT_POST, 's');
+ if (!empty($s)){
+ $query .= " WHERE user_name LIKE '%" . strip_tags($s) . "%' "
+ . " OR first_name LIKE '%" . strip_tags($s) . "%' "
+ . " OR last_name LIKE '%" . strip_tags($s) . "%' ";
+ }
+ $orderby = filter_input(INPUT_GET, 'orderby');
+ $orderby = empty($orderby) ? 'user_name' : $orderby ;
+ $order = filter_input(INPUT_GET, 'order');
+ $order = empty($order) ? 'DESC' : $order;
+
+ $sortable_columns = $this->get_sortable_columns();
+ $orderby = BUtils::sanitize_value_by_array($orderby, $sortable_columns);
+ $order = BUtils::sanitize_value_by_array($order, array('DESC' => '1', 'ASC' => '1'));
+
+ $query.=' ORDER BY ' . $orderby . ' ' . $order;
+ $totalitems = $wpdb->query($query); //return the total number of affected rows
+ $perpage = 20;
+ $paged = filter_input(INPUT_GET, 'paged');
+ if (empty($paged) || !is_numeric($paged) || $paged <= 0) {
+ $paged = 1;
+ }
+ $totalpages = ceil($totalitems / $perpage);
+ if (!empty($paged) && !empty($perpage)) {
+ $offset = ($paged - 1) * $perpage;
+ $query.=' LIMIT ' . (int) $offset . ',' . (int) $perpage;
+ }
+ $this->set_pagination_args(array(
+ "total_items" => $totalitems,
+ "total_pages" => $totalpages,
+ "per_page" => $perpage,
+ ));
+
+ $columns = $this->get_columns();
+ $hidden = array();
+ $sortable = $this->get_sortable_columns();
+
+ $this->_column_headers = array($columns, $hidden, $sortable);
+ $this->items = $wpdb->get_results($query, ARRAY_A);
+ }
+
+ function no_items() {
+ _e('No Member found.');
+ }
+
+ function process_form_request() {
+ if (isset($_REQUEST['member_id']))
+ return $this->edit(absint($_REQUEST['member_id']));
+ return $this->add();
+ }
+
+ function add() {
+ $form = apply_filters('swpm_admin_registration_form_override', '');
+ if (!empty($form)) {echo $form;return;}
+ global $wpdb;
+ $member = BTransfer::$default_fields;
+ $member['member_since'] = date('Y-m-d');
+ $member['subscription_starts'] = date('Y-m-d');
+ if (isset($_POST['createswpmuser'])) {
+ $member = $_POST;
+ }
+ extract($member, EXTR_SKIP);
+ $query = "SELECT * FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id !=1 ";
+ $levels = $wpdb->get_results($query, ARRAY_A);
+ include_once(SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_add.php');
+ return false;
+ }
+
+ function edit($id) {
+ global $wpdb;
+ $id = absint($id);
+ $query = "SELECT * FROM {$wpdb->prefix}swpm_members_tbl WHERE member_id = $id";
+ $member = $wpdb->get_row($query, ARRAY_A);
+ if (isset($_POST["editswpmuser"])) {
+ $_POST['user_name'] = $member['user_name'];
+ $_POST['email'] = $member['email'];
+ $member = $_POST;
+ }
+ extract($member, EXTR_SKIP);
+ $query = "SELECT * FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id !=1 ";
+ $levels = $wpdb->get_results($query, ARRAY_A);
+ include_once(SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_edit.php');
+ return false;
+ }
+
+ function delete() {
+ global $wpdb;
+ if (isset($_REQUEST['members'])) {
+ $members = $_REQUEST['members'];
+ if (!empty($members)) {
+ $members = array_map('absint', $members);
+ foreach ($members as $swpm_id) {
+ $user_name = BUtils::get_user_by_id(absint($swpm_id));
+ BMembers::delete_wp_user($user_name);
+ }
+ $query = "DELETE FROM " . $wpdb->prefix . "swpm_members_tbl WHERE member_id IN (" . implode(',', $members) . ")";
+ $wpdb->query($query);
+ }
+ }
+ else if (isset($_REQUEST['member_id'])) {
+ $id = absint($_REQUEST['member_id']);
+ BMembers::delete_user_by_id($id);
+ }
+ }
+ public static function delete_user_by_id($id){
+ $user_name = BUtils::get_user_by_id($id);
+ BMembers::delete_wp_user($user_name);
+ BMembers::delete_swpm_user_by_id($id);
+ }
+
+ public static function delete_swpm_user_by_id($id){
+ global $wpdb;
+ $query = "DELETE FROM " . $wpdb->prefix . "swpm_members_tbl WHERE member_id = $id";
+ $wpdb->query($query);
+ }
+ function show() {
+ include_once(SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_members.php');
+ }
+
+ public static function delete_wp_user($user_name) {
+ $wp_user_id = username_exists($user_name);
+ $ud = get_userdata($wp_user_id);
+ if (!empty($ud) && (isset($ud->wp_capabilities['administrator']) || $ud->wp_user_level == 10)) {
+ BTransfer::get_instance()->set('status', 'For consistency, we do not allow deleting any associated wordpress account with administrator role.
'
+ . 'Please delete from Users menu.');
+ return;
+ }
+ if ($wp_user_id) {
+ include_once(ABSPATH . 'wp-admin/includes/user.php');
+ wp_delete_user($wp_user_id, 1); //assigns all related to this user to admin.
+ }
+ }
+
+}
diff --git a/classes/class.bMembershipLevel.php b/classes/class.bMembershipLevel.php
new file mode 100644
index 0000000..747c1d0
--- /dev/null
+++ b/classes/class.bMembershipLevel.php
@@ -0,0 +1,70 @@
+is_valid()) {
+ $level_info = $form->get_sanitized();
+ $wpdb->insert($wpdb->prefix . "swpm_membership_tbl", $level_info);
+ $id = $wpdb->insert_id;
+ $custom = apply_filters('swpm_admin_add_membership_level', array());
+ $this->save_custom_fields($id, $custom);
+ $message = array('succeeded' => true, 'message' => BUtils::_('Membership Level Creation Successful.'));
+ BTransfer::get_instance()->set('status', $message);
+ wp_redirect('admin.php?page=simple_wp_membership_levels');
+ return;
+ }
+ $message = array('succeeded' => false, 'message' => BUtils::_('Please correct the following:'), 'extra' => $form->get_errors());
+ BTransfer::get_instance()->set('status', $message);
+ }
+
+ public function edit($id) {
+ global $wpdb;
+ $query = $wpdb->prepare("SELECT * FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id = %d", $id);
+ $level = $wpdb->get_row($query, ARRAY_A);
+ $form = new BLevelForm($level);
+ if ($form->is_valid()) {
+ $wpdb->update($wpdb->prefix . "swpm_membership_tbl", $form->get_sanitized(), array('id' => $id));
+ //@todo meta table and collect all relevant info and pass as argument
+ $custom = apply_filters('swpm_admin_edit_membership_level', array(), $id);
+ $this->save_custom_fields($id, $custom);
+ $message = array('succeeded' => true, 'message' => BUtils::_('Updated Successfully.'));
+ BTransfer::get_instance()->set('status', $message);
+ wp_redirect('admin.php?page=simple_wp_membership_levels');
+ return;
+ }
+ $message = array('succeeded' => false, 'message' => BUtils::_('Please correct the following:'), 'extra' => $form->get_errors());
+ BTransfer::get_instance()->set('status', $message);
+ }
+ private function save_custom_fields($level_id, $data){
+ $custom_obj = BMembershipLevelCustom::get_instance_by_id($level_id);
+ foreach ($data as $item){
+ $custom_obj->set($item);
+ }
+ }
+}
diff --git a/classes/class.bMembershipLevelCustom.php b/classes/class.bMembershipLevelCustom.php
new file mode 100644
index 0000000..5ac7952
--- /dev/null
+++ b/classes/class.bMembershipLevelCustom.php
@@ -0,0 +1,93 @@
+fields = array();
+ }
+ public static function get_instance_by_id($level_id){
+ if (!isset(self::$instances[$level_id])){
+ self::$instances[$level_id] = new BMembershipLevelCustom();
+ self::$instances[$level_id]->level_id = $level_id;
+ self::$instances[$level_id]->load_by_id($level_id);
+ }
+ return self::$instances[$level_id];
+ }
+ public function load_by_id($level_id){
+ global $wpdb;
+ $query = 'SELECT * FROM ' . $wpdb->prefix . 'swpm_membership_meta_tbl WHERE level_id=%d';
+ $results = $wpdb->get_results($wpdb->prepare($query, $level_id), ARRAY_A);
+ foreach($results as $result){
+ $this->fields[$result['meta_key']] = $result;
+ }
+ }
+ public function set($item){
+ $meta_key = preg_replace('|[^A-Z0-9_]|i', '', $item['meta_key']);
+ $new = array(
+ 'meta_key'=>$meta_key,
+ 'level_id'=>$this->level_id,
+ 'meta_label'=> isset($item['meta_label'])?$item['meta_label']:'',
+ 'meta_value'=>$item['meta_value'],
+ 'meta_type'=> isset($item['meta_type'])?$item['meta_type']:'text',
+ 'meta_default'=> isset($item['meta_default'])?$item['meta_default']:'',
+ 'meta_context'=> $item['meta_context'],
+ );
+ if (isset($this->fields[$meta_key])){
+ $new['id'] = $this->fields[$meta_key]['id'];
+ $this->fields[$meta_key] = $new;
+ }
+ else{
+ $this->fields[$meta_key] = $new;
+ }
+ $this->save($this->fields[$meta_key]);
+ return $this;
+ }
+ public function get($meta_key, $default=''){
+ $meta_key = preg_replace('|[^A-Z0-9_]|i', '', $meta_key);
+ if (isset($this->fields[$meta_key])){
+ return maybe_unserialize($this->fields[$meta_key]['meta_value']);
+
+ }
+ return $default;
+ }
+ public function get_by_context($context){
+ $result = array();
+ foreach ($this->fields as $key=>$field){
+ if ($field['meta_context'] == $context){
+ $result[$key] = $field;
+ }
+ }
+ return $result;
+ }
+ private function save($field){
+ global $wpdb;
+ if (!isset($field['meta_key'])){retern;} // cannot continue without key field.
+ $meta_key = preg_replace('|[^A-Z0-9_]|i', '', $field['meta_key']);
+ $query = $wpdb->prepare(
+ 'REPLACE INTO ' . $wpdb->prefix. 'swpm_membership_meta_tbl
+ (level_id, meta_key, meta_label, meta_value, meta_type, meta_default, meta_context)
+ VALUES(%d, %s, %s, %s, %s, %s, %s); ',
+ $this->level_id,
+ $meta_key,
+ isset($field['meta_label'])? sanitize_text_field($field['meta_label']): '',
+ isset($field['meta_value'])? sanitize_text_field($field['meta_value']): '',
+ 'text', // at the moment we have only one type
+ '',
+ isset($field['meta_context'])? sanitize_text_field($field['meta_context']): 'default'
+ );
+
+ $wpdb->query($query);
+ }
+ public static function get_value_by_key($level_id, $key, $default= ''){
+ return BMembershipLevelCustom::get_instance_by_id($level_id)->get($key, $default);
+ }
+ public static function get_value_by_context($level_id, $context){
+ return BMembershipLevelCustom::get_instance_by_id($level_id)->get_by_context($context);
+ }
+}
diff --git a/classes/class.bMembershipLevelUtils.php b/classes/class.bMembershipLevelUtils.php
new file mode 100644
index 0000000..0a81afb
--- /dev/null
+++ b/classes/class.bMembershipLevelUtils.php
@@ -0,0 +1,10 @@
+BUtils::_('Membership Level'),
+ 'plural' => BUtils::_('Membership Levels'),
+ 'ajax' => false
+ ));
+ }
+ function get_columns(){
+ return array(
+ 'cb' => ''
+ ,'id'=>BUtils::_('ID')
+ ,'alias'=>BUtils::_('Membership Level')
+ ,'role'=>BUtils::_('Role')
+ ,'valid_for'=>BUtils::_('Access Valid For/Until')
+ );
+ }
+ function get_sortable_columns(){
+ return array(
+ 'id' => array('id',true),
+ 'alias' => array('alias',true)
+ );
+ }
+ function get_bulk_actions() {
+ $actions = array(
+ 'bulk_delete' => BUtils::_('Delete')
+ );
+ return $actions;
+ }
+ function column_default($item, $column_name){
+ if($column_name == 'valid_for'){
+ if($item['subscription_duration_type'] == BMembershipLevel::NO_EXPIRY) {return 'No Expiry';}
+ if($item['subscription_duration_type'] == BMembershipLevel::FIXED_DATE) {return date(get_option('date_format'), strtotime($item['subscription_period']));}
+ if($item['subscription_duration_type'] == BMembershipLevel::DAYS) {return $item['subscription_period'] ." Day(s)";}
+ if($item['subscription_duration_type'] == BMembershipLevel::WEEKS) {return $item['subscription_period'] ." Week(s)";}
+ if($item['subscription_duration_type'] == BMembershipLevel::MONTHS) {return $item['subscription_period'] ." Month(s)";}
+ if($item['subscription_duration_type'] == BMembershipLevel::YEARS) {return $item['subscription_period'] ." Year(s)";}
+ }
+ if($column_name == 'role') {return ucfirst($item['role']);}
+ return stripslashes($item[$column_name]);
+ }
+ function column_id($item){
+ $actions = array(
+ 'edit' => sprintf('Edit',
+ $_REQUEST['page'],$item['id']),
+ 'delete' => sprintf('Delete',
+ $_REQUEST['page'],$item['id']),
+ );
+ return $item['id'] . $this->row_actions($actions);
+ }
+ function column_cb($item) {
+ return sprintf(
+ '', $item['id']
+ );
+ }
+ function prepare_items() {
+ global $wpdb;
+ $query = "SELECT * FROM " .$wpdb->prefix . "swpm_membership_tbl WHERE id !=1 ";
+ if(isset($_POST['s'])) $query .= " AND alias LIKE '%" . strip_tags($_POST['s']). "%' ";
+ $orderby = !empty($_GET["orderby"]) ? mysql_real_escape_string($_GET["orderby"]) : 'id';
+ $order = !empty($_GET["order"]) ? mysql_real_escape_string($_GET["order"]) : 'DESC';
+
+ $sortable_columns = $this->get_sortable_columns();
+ $orderby = BUtils::sanitize_value_by_array($orderby, $sortable_columns);
+ $order = BUtils::sanitize_value_by_array($order, array('DESC' => '1', 'ASC' => '1'));
+
+ if(!empty($orderby) && !empty($order)){ $query.=' ORDER BY '.$orderby.' '.$order; }
+
+ $totalitems = $wpdb->query($query); //return the total number of affected rows
+ $perpage = 20;
+ $paged = !empty($_GET["paged"]) ? mysql_real_escape_string($_GET["paged"]) : '';
+ if(empty($paged) || !is_numeric($paged) || $paged<=0 ){ $paged=1; }
+ $totalpages = ceil($totalitems/$perpage);
+ if(!empty($paged) && !empty($perpage)){
+ $offset=($paged-1)*$perpage;
+ $query.=' LIMIT '.(int)$offset.','.(int)$perpage;
+ }
+ $this->set_pagination_args( array(
+ "total_items" => $totalitems,
+ "total_pages" => $totalpages,
+ "per_page" => $perpage,
+ ) );
+
+ $columns = $this->get_columns();
+ $hidden = array();
+ $sortable = $this->get_sortable_columns();
+
+ $this->_column_headers = array($columns, $hidden, $sortable);
+ $this->items = $wpdb->get_results($query, ARRAY_A);
+ }
+ function no_items() {
+ BUtils::e( 'No membership levels found.' );
+ }
+ function process_form_request(){
+ if(isset($_REQUEST['id'])){
+ return $this->edit($_REQUEST['id']);
+ }
+ return $this->add();
+
+ }
+ function add(){
+ global $wpdb;
+ $member = BTransfer::$default_fields;
+ if(isset($_POST['createswpmlevel'])){
+ $member = $_POST;
+ }
+ extract($member, EXTR_SKIP);
+ include_once(SIMPLE_WP_MEMBERSHIP_PATH.'views/admin_add_level.php');
+ return false;
+ }
+ function edit($id){
+ global $wpdb;
+ $query = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}swpm_membership_tbl WHERE id = %d", absint($id));
+ $membership = $wpdb->get_row($query, ARRAY_A);
+ extract($membership, EXTR_SKIP);
+ include_once(SIMPLE_WP_MEMBERSHIP_PATH.'views/admin_edit_level.php');
+ return false;
+ }
+ function delete(){
+ global $wpdb;
+ if (isset($_REQUEST['ids'])){
+ $members = $_REQUEST['ids'];
+ if(!empty($members)){
+ $members = array_map('absint', $members);
+ $members = implode(',', $members);
+ $query = "DELETE FROM " .$wpdb->prefix . "swpm_membership_tbl WHERE id IN (" . $members . ")";
+ $wpdb->query($query);
+ }
+ }
+ else if(isset($_REQUEST['id'])){
+ $id = absint($_REQUEST['id']);
+ $query = $wpdb->prepare("DELETE FROM " .$wpdb->prefix . "swpm_membership_tbl WHERE id = %d", $id);
+ $wpdb->query($query);
+ }
+ }
+ function show(){
+ $selected = 1;
+ include_once(SIMPLE_WP_MEMBERSHIP_PATH.'views/admin_membership_levels.php');
+ }
+ function manage(){
+ $selected = 2;
+ include_once(SIMPLE_WP_MEMBERSHIP_PATH.'views/admin_membership_manage.php');
+ }
+ function manage_categroy(){
+ $selected = 3;
+ include_once('class.bCategoryList.php');
+ $category_list = new BCategoryList();
+ include_once(SIMPLE_WP_MEMBERSHIP_PATH.'views/admin_category_list.php');
+ }
+}
+
diff --git a/classes/class.bMessages.php b/classes/class.bMessages.php
new file mode 100644
index 0000000..9bfac9f
--- /dev/null
+++ b/classes/class.bMessages.php
@@ -0,0 +1,23 @@
+messages = array();
+ }
+ public function get($key){
+ if(isset($this->messages[$key])){
+ $m = $this->messages[$key];
+ $this->messages[$key] ='';
+ return $m;
+ }
+ return '';
+ }
+ public function set($key, $value){
+ $this->messages[$key] = $value;
+ }
+}
diff --git a/classes/class.bPermission.php b/classes/class.bPermission.php
new file mode 100644
index 0000000..d1c8642
--- /dev/null
+++ b/classes/class.bPermission.php
@@ -0,0 +1,60 @@
+init($level_id);
+ }
+
+ public static function get_instance($level_id) {
+ if ($level_id ==1 || $level_id == md5(1)){
+ wp_die('Invalid Membership level!');
+ }
+ $key = is_numeric($level_id)? md5($level_id): $level_id;
+ if (!isset(self::$_this[$key])){
+ self::$_this[$key] = new BPermission($level_id);
+ }
+
+ return self::$_this[$key];
+ }
+
+ public function is_permitted($id) {
+ return $this->post_in_parent_categories($id) || $this->post_in_categories($id) || $this->in_posts($id) || $this->in_pages($id) || $this->in_attachments($id) || $this->in_custom_posts($id);
+ }
+
+ public function is_permitted_attachment($id) {
+ return (($this->bitmap & 16) === 16) && $this->in_attachments($id);
+ }
+
+ public function is_permitted_custom_post($id) {
+ return (($this->bitmap & 32) === 32) && $this->in_custom_posts($id);
+ }
+
+ public function is_permitted_category($id) {
+ return (($this->bitmap & 1) === 1) && $this->in_categories($id);
+ }
+
+ public function is_post_in_permitted_category($post_id) {
+ return (($this->bitmap & 1) === 1) && $this->post_in_categories($post_id);
+ }
+
+ public function is_permitted_post($id) {
+ return (($this->bitmap & 4) === 4) && $this->in_posts($id);
+ }
+
+ public function is_permitted_page($id) {
+ return (($this->bitmap & 8) === 8) && $this->in_pages($id);
+ }
+
+ public function is_permitted_comment($id) {
+ return (($this->bitmap & 2) === 2) && $this->in_comments($id);
+ }
+
+ public function is_post_in_permitted_parent_category($post_id) {
+ return (($this->bitmap & 1) === 1) && $this->post_in_parent_categories($post_id);
+ }
+
+ public function is_permitted_parent_category($id) {
+ return (($this->bitmap & 1) === 1) && $this->in_parent_categories($id);
+ }
+}
diff --git a/classes/class.bPermissionCollection.php b/classes/class.bPermissionCollection.php
new file mode 100644
index 0000000..d0c1366
--- /dev/null
+++ b/classes/class.bPermissionCollection.php
@@ -0,0 +1,49 @@
+permissions = array();
+ }
+
+ public static function get_instance(){
+ self::$_this = empty(self::$_this)? new BPermissionCollection():self::$_this;
+ return self::$_this;
+ }
+
+ public function load($level_ids = array()){
+ if (empty($level_ids)){
+ global $wpdb;
+ $level_ids = $wpdb->get_col("SELECT id FROM {$wpdb->prefix}swpm_membership_tbl WHERE id != 1");
+ }
+
+ foreach($level_ids as $id){
+ $this->permissions[] = BPermission::get_instance($id);
+ }
+ }
+
+ public function get_permitted_levels($post_id){
+ $levels = array();
+
+ foreach($this->permissions as $permission){
+ if ($permission->is_permitted($post_id)){
+ $levels[$permission->get($id)] = $permission->get('alias');
+ }
+ }
+
+ return $levels;
+ }
+}
diff --git a/classes/class.bProtection.php b/classes/class.bProtection.php
new file mode 100644
index 0000000..be1f8b2
--- /dev/null
+++ b/classes/class.bProtection.php
@@ -0,0 +1,66 @@
+msg = "";
+ $this->init(1);
+ }
+
+ public static function get_instance() {
+ self::$_this = empty(self::$_this) ? (new BProtection()) : self::$_this;
+ return self::$_this;
+ }
+
+ public function is_protected($id) {
+ if ($this->post_in_parent_categories($id) || $this->post_in_categories($id)) {
+ $this->msg = '
- View debug log file by clicking here.';
+ $debug_field_help_text .= '
- Reset debug log file by clicking here.';
+ add_settings_field('enable-debug', 'Enable Debug',
+ array(&$this, 'checkbox_callback'), 'simple_wp_membership_settings', 'debug-settings',
+ array('item' => 'enable-debug',
+ 'message'=> $debug_field_help_text));
+ add_settings_field('enable-sandbox-testing', BUtils::_('Enable Sandbox Testing'),
+ array(&$this, 'checkbox_callback'), 'simple_wp_membership_settings', 'debug-settings',
+ array('item' => 'enable-sandbox-testing',
+ 'message'=>BUtils::_('Enable this option if you want to do sandbox payment testing.')));
+
+ }
+
+ private function tab_2() {
+ }
+
+ private function tab_3() {
+ register_setting('swpm-settings-tab-3', 'swpm-settings', array(&$this, 'sanitize_tab_3'));
+
+ add_settings_section('email-misc-settings', BUtils::_('Email Misc. Settings'),
+ array(&$this, 'email_misc_settings_callback'), 'simple_wp_membership_settings');
+ add_settings_field('email-misc-from', BUtils::_('From Email Address'),
+ array(&$this, 'textfield_callback'), 'simple_wp_membership_settings', 'email-misc-settings',
+ array('item' => 'email-from',
+ 'message'=>''));
+
+ add_settings_section('reg-prompt-email-settings', BUtils::_('Email Settings (Prompt to Complete Registration )'),
+ array(&$this, 'reg_prompt_email_settings_callback'), 'simple_wp_membership_settings');
+ add_settings_field('reg-prompt-complete-mail-subject', BUtils::_('Email Subject'),
+ array(&$this, 'textfield_callback'), 'simple_wp_membership_settings', 'reg-prompt-email-settings',
+ array('item' => 'reg-prompt-complete-mail-subject',
+ 'message'=>''));
+ add_settings_field('reg-prompt-complete-mail-body', BUtils::_('Email Body'),
+ array(&$this, 'textarea_callback'), 'simple_wp_membership_settings', 'reg-prompt-email-settings',
+ array('item' => 'reg-prompt-complete-mail-body',
+ 'message'=>''));
+
+ add_settings_section('reg-email-settings', BUtils::_('Email Settings (Registration Complete)'),
+ array(&$this, 'reg_email_settings_callback'), 'simple_wp_membership_settings');
+ add_settings_field('reg-complete-mail-subject', BUtils::_('Email Subject'),
+ array(&$this, 'textfield_callback'), 'simple_wp_membership_settings', 'reg-email-settings',
+ array('item' => 'reg-complete-mail-subject',
+ 'message'=>''));
+ add_settings_field('reg-complete-mail-body', BUtils::_('Email Body'),
+ array(&$this, 'textarea_callback'), 'simple_wp_membership_settings', 'reg-email-settings',
+ array('item' => 'reg-complete-mail-body',
+ 'message'=>''));
+ add_settings_field('enable-admin-notification-after-reg', BUtils::_('Send Notification To Admin'),
+ array(&$this, 'checkbox_callback'), 'simple_wp_membership_settings', 'reg-email-settings',
+ array('item' => 'enable-admin-notification-after-reg',
+ 'message'=>''));
+ add_settings_field('enable-notification-after-manual-user-add', BUtils::_('Send Email to Member When Added via Admin Dashboard'),
+ array(&$this, 'checkbox_callback'), 'simple_wp_membership_settings', 'reg-email-settings',
+ array('item' => 'enable-notification-after-manual-user-add',
+ 'message'=>''));
+
+ add_settings_section('upgrade-email-settings', BUtils::_(' Email Settings (Account Upgrade Notification)'),
+ array(&$this, 'upgrade_email_settings_callback'), 'simple_wp_membership_settings');
+ add_settings_field('upgrade-complete-mail-subject', BUtils::_('Email Subject'),
+ array(&$this, 'textfield_callback'), 'simple_wp_membership_settings', 'upgrade-email-settings',
+ array('item' => 'upgrade-complete-mail-subject',
+ 'message'=>''));
+ add_settings_field('upgrade-complete-mail-body', BUtils::_('Email Body'),
+ array(&$this, 'textarea_callback'), 'simple_wp_membership_settings', 'upgrade-email-settings',
+ array('item' => 'upgrade-complete-mail-body',
+ 'message'=>''));
+ }
+
+ private function tab_4(){
+ }
+
+ private function tab_5(){
+ register_setting('swpm-settings-tab-5', 'swpm-settings', array(&$this, 'sanitize_tab_5'));
+
+ add_settings_section('advanced-settings', BUtils::_('Advanced Settings'),
+ array(&$this, 'advanced_settings_callback'), 'simple_wp_membership_settings');
+
+ add_settings_field('enable-expired-account-login', BUtils::_('Enable Expired Account Login'),
+ array(&$this, 'checkbox_callback'), 'simple_wp_membership_settings', 'advanced-settings',
+ array('item' => 'enable-expired-account-login',
+ 'message'=>BUtils::_("When enabled, expired members will be able to log into the system but won't be able to view any protected content. This allows them to easily renew their account by making another payment.")));
+ }
+
+ private function tab_6(){
+ }
+
+ public static function get_instance() {
+ self::$_this = empty(self::$_this) ? new BSettings() : self::$_this;
+ return self::$_this;
+ }
+ public function selectbox_callback($args){
+ $item = $args['item'];
+ $options = $args['options'];
+ $default = $args['default'];
+ $msg = isset($args['message'])?$args['message']: '';
+ $selected = esc_attr($this->get_value($item), $default);
+ echo "';
+ echo '
'.$msg.'';
+ }
+ public function checkbox_callback($args) {
+ $item = $args['item'];
+ $msg = isset($args['message'])?$args['message']: '';
+ $is = esc_attr($this->get_value($item));
+ echo "";
+ echo '
'.$msg.'';
+ }
+
+ public function textarea_callback($args) {
+ $item = $args['item'];
+ $msg = isset($args['message'])?$args['message']: '';
+ $text = esc_attr($this->get_value($item));
+ echo "";
+ echo '
'.$msg.'';
+ }
+
+ public function textfield_small_callback($args) {
+ $item = $args['item'];
+ $msg = isset($args['message'])?$args['message']: '';
+ $text = esc_attr($this->get_value($item));
+ echo "";
+ echo '
'.$msg.'';
+ }
+
+ public function textfield_callback($args) {
+ $item = $args['item'];
+ $msg = isset($args['message'])?$args['message']: '';
+ $text = esc_attr($this->get_value($item));
+ echo "";
+ echo '
'.$msg.'';
+ }
+
+ public function textfield_long_callback($args) {
+ $item = $args['item'];
+ $msg = isset($args['message'])?$args['message']: '';
+ $text = esc_attr($this->get_value($item));
+ echo "";
+ echo '
'.$msg.'';
+ }
+
+ public function swpm_documentation_callback() {
+ ?>
+
+ tabs as $id=>$label):?>
+
+
+
+ get_value('login-page-url');
+ $joinus = $this->get_value('join-us-page-url');
+ if (empty ($login) || empty($joinus)){
+ return 'Simple Membership is not configured correctly.'
+ . 'Please contact Admin';
+ }
+ return BUtils::_('Please'). ' ' . BUtils::_('Login') . '. '. BUtils::_('Not a Member?').' '.BUtils::_('Join Us').'';
+ }
+
+}
diff --git a/classes/class.bTransactions.php b/classes/class.bTransactions.php
new file mode 100644
index 0000000..1336de9
--- /dev/null
+++ b/classes/class.bTransactions.php
@@ -0,0 +1,55 @@
+insert($wpdb->prefix . "swpm_payments_tbl", $txn_data);
+
+ }
+
+ static function parse_custom_var($custom) {
+ $delimiter = "&";
+ $customvariables = array();
+
+ $namevaluecombos = explode($delimiter, $custom);
+ foreach ($namevaluecombos as $keyval_unparsed) {
+ $equalsignposition = strpos($keyval_unparsed, '=');
+ if ($equalsignposition === false) {
+ $customvariables[$keyval_unparsed] = '';
+ continue;
+ }
+ $key = substr($keyval_unparsed, 0, $equalsignposition);
+ $value = substr($keyval_unparsed, $equalsignposition + 1);
+ $customvariables[$key] = $value;
+ }
+
+ return $customvariables;
+ }
+
+}
\ No newline at end of file
diff --git a/classes/class.bTransfer.php b/classes/class.bTransfer.php
new file mode 100644
index 0000000..e77722b
--- /dev/null
+++ b/classes/class.bTransfer.php
@@ -0,0 +1,65 @@
+ '', 'last_name' => '',
+ 'user_name' => '', 'email' => '',
+ 'password' => '',
+ 'phone' => '', 'account_state' => '',
+ 'member_since' => '', 'subscription_starts' => '',
+ 'address_street' => '', 'address_city' => '',
+ 'address_state' => '', 'address_zipcode' => '',
+ 'company_name' => '', 'country' => '',
+ 'gender' => 'not specified',
+ 'membership_level' => '2');
+ public static $default_level_fields = array(
+ 'alias' => '', 'role' => '',
+ 'subscription_period' => '', 'subscription_duration_type' => BMembershipLevel::NO_EXPIRY);
+ public static $admin_messages = array();
+ private static $_this;
+ private $message;
+
+ private function __contruct() {
+ $this->message = get_option('swpm-messages');
+ }
+
+ public static function get_instance() {
+ self::$_this = empty(self::$_this) ? new BTransfer() : self::$_this;
+ self::$_this->message = get_option('swpm-messages');
+ return self::$_this;
+ }
+
+ public function get($key) {
+ $sesion_key = $_COOKIE['swpm_session'];
+ $m = '';
+ if (isset($this->message[$sesion_key])){
+ $m = $this->message[$sesion_key]->get($key);
+ }
+ update_option('swpm-messages', $this->message);
+ return $m;
+ }
+
+ public function set($key, $value) {
+ $sesion_key = $_COOKIE['swpm_session'];
+ if (!isset($this->message[$sesion_key])){
+ $this->message[$sesion_key] = new BMessages();
+ }
+ $this->message[$sesion_key]->set($key,$value);
+ update_option('swpm-messages', $this->message);
+ }
+
+ public static function get_real_ip_addr() {
+ if (!empty($_SERVER['HTTP_CLIENT_IP'])){
+ $ip = $_SERVER['HTTP_CLIENT_IP'];
+ }
+ else if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){
+ $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
+ }
+ else{
+ $ip = $_SERVER['REMOTE_ADDR'];
+ }
+ return $ip;
+ }
+
+}
diff --git a/classes/class.bUtils.php b/classes/class.bUtils.php
new file mode 100644
index 0000000..bfcd0ec
--- /dev/null
+++ b/classes/class.bUtils.php
@@ -0,0 +1,385 @@
+No Expiry' .
+ '' .
+ '' .
+ '' .
+ '' .
+ '';
+ }
+
+ // $subscript_period must be integer.
+ public static function calculate_subscription_period_days($subcript_period, $subscription_duration_type) {
+ if ($subscription_duration_type == BMembershipLevel::NO_EXPIRY) {
+ return 'noexpire';
+ }
+ if (!is_numeric($subcript_period)) {
+ throw new Exception(" subcript_period parameter must be integer in BUtils::calculate_subscription_period_days method");
+ }
+ switch (strtolower($subscription_duration_type)) {
+ case BMembershipLevel::DAYS:
+ break;
+ case BMembershipLevel::WEEKS:
+ $subcript_period = $subcript_period * 7;
+ break;
+ case BMembershipLevel::MONTHS:
+ $subcript_period = $subcript_period * 30;
+ break;
+ case BMembershipLevel::YEARS:
+ $subcript_period = $subcript_period * 365;
+ break;
+ }
+ return $subcript_period;
+ }
+
+ public static function get_expiration_timestamp($user) {
+ $permission = BPermission::get_instance($user->membership_level);
+ if (BMembershipLevel::FIXED_DATE == $permission->get('subscription_duration_type')) {
+ return strtotime($permission->get('subscription_period'));
+ }
+ $days = self::calculate_subscription_period_days(
+ $permission->get('subscription_period'), $permission->get('subscription_duration_type'));
+ if ($days == 'noexpire') {
+ return PHP_INT_MAX; // which is equivalent to
+ }
+ return strtotime($user->subscription_starts . ' ' . $days . ' days');
+ }
+
+ public static function is_subscription_expired($user) {
+ $expiration_timestamp = BUtils::get_expiration_timestamp($user);
+ return $expiration_timestamp < time();
+ }
+
+ public static function gender_dropdown($selected = 'not specified') {
+ return '' .
+ '' .
+ '';
+ }
+
+ public static function get_account_state_options() {
+ return array('active' => BUtils::_('Active'),
+ 'inactive' => BUtils::_('Inactive'),
+ 'pending' => BUtils::_('Pending'),
+ 'expired' => BUtils::_('Expired'),);
+ }
+
+ public static function account_state_dropdown($selected = 'active') {
+ $options = self::get_account_state_options();
+ $html = '';
+ foreach ($options as $key => $value) {
+ $html .= '';
+ }
+ return $html;
+ }
+
+ public static function membership_level_dropdown($selected = 0) {
+ $options = '';
+ global $wpdb;
+ $query = "SELECT alias, id FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id != 1";
+ $levels = $wpdb->get_results($query);
+ foreach ($levels as $level) {
+ $options .= '';
+ }
+ return $options;
+ }
+
+ public static function get_all_membership_level_ids() {
+ global $wpdb;
+ $query = "SELECT id FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id != 1";
+ return $wpdb->get_col($query);
+ }
+
+ public static function get_user_by_id($swpm_id) {
+ global $wpdb;
+ $query = $wpdb->prepare("SELECT user_name FROM {$wpdb->prefix}swpm_members_tbl WHERE member_id = %d", $swpm_id);
+ return $wpdb->get_var($query);
+ }
+
+ public static function get_user_by_user_name($swpm_user_name) {
+ global $wpdb;
+ $query = $wpdb->prepare("SELECT member_id FROM {$wpdb->prefix}swpm_members_tbl WHERE user_name = %s", $swpm_user_name);
+ return $wpdb->get_var($query);
+ }
+
+ public static function get_registration_link($for = 'all', $send_email = false, $member_id = '') {
+ $members = array();
+ global $wpdb;
+ switch ($for) {
+ case 'one':
+ if (empty($member_id)) {
+ return array();
+ }
+ $query = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}swpm_members_tbl WHERE member_id = %d", $member_id);
+ $members = $wpdb->get_results($query);
+ break;
+ case 'all':
+ $query = "SELECT * FROM {$wpdb->prefix}swpm_members_tbl WHERE reg_code != '' ";
+ $members = $wpdb->get_results($query);
+ break;
+ }
+ $settings = BSettings::get_instance();
+ $separator = '?';
+ $url = $settings->get_value('registration-page-url');
+ if (strpos($url, '?') !== false) {
+ $separator = '&';
+ }
+ $subject = $settings->get_value('reg-complete-mail-subject');
+ if (empty($subject)) {
+ $subject = "Please complete your registration";
+ }
+ $body = $settings->get_value('reg-complete-mail-body');
+ if (empty($body)) {
+ $body = "Please use the following link to complete your registration. \n {reg_link}";
+ }
+ $from_address = $settings->get_value('email-from');
+ $links = array();
+ foreach ($members as $member) {
+ $reg_url = $url . $separator . 'member_id=' . $member->member_id . '&code=' . $member->reg_code;
+ if (!empty($send_email) && empty($member->user_name)) {
+ $tags = array("{first_name}", "{last_name}", "{reg_link}");
+ $vals = array($member->first_name, $member->last_name, $reg_url);
+ $email_body = str_replace($tags, $vals, $body);
+ $headers = 'From: ' . $from_address . "\r\n";
+ wp_mail($member->email, $subject, $email_body, $headers);
+ }
+ $links[] = $reg_url;
+ }
+ return $links;
+ }
+
+ public static function update_wp_user_Role($wp_user_id, $role) {
+ $preserve_role = 'yes';
+ if ($preserve_role) {
+ return;
+ }
+ if (self::is_multisite_install()) {//MS install
+ return; //TODO - don't do this for MS install
+ }
+ $caps = get_user_meta($wp_user_id, 'wp_capabilities', true);
+ if (in_array('administrator', array_keys((array) $caps))) {
+ return;
+ }
+ do_action('set_user_role', $wp_user_id, $role); //Fire the action for other plugin(s)
+ wp_update_user(array('ID' => $wp_user_id, 'role' => $role));
+ $roles = new WP_Roles();
+ $level = $roles->roles[$role]['capabilities'];
+ if (isset($level['level_10']) && $level['level_10']) {
+ update_user_meta($wp_user_id, 'wp_user_level', 10);
+ return;
+ }
+ if (isset($level['level_9']) && $level['level_9']) {
+ update_user_meta($wp_user_id, 'wp_user_level', 9);
+ return;
+ }
+ if (isset($level['level_8']) && $level['level_8']) {
+ update_user_meta($wp_user_id, 'wp_user_level', 8);
+ return;
+ }
+ if (isset($level['level_7']) && $level['level_7']) {
+ update_user_meta($wp_user_id, 'wp_user_level', 7);
+ return;
+ }
+ if (isset($level['level_6']) && $level['level_6']) {
+ update_user_meta($wp_user_id, 'wp_user_level', 6);
+ return;
+ }
+ if (isset($level['level_5']) && $level['level_5']) {
+ update_user_meta($wp_user_id, 'wp_user_level', 5);
+ return;
+ }
+ if (isset($level['level_4']) && $level['level_4']) {
+ update_user_meta($wp_user_id, 'wp_user_level', 4);
+ return;
+ }
+ if (isset($level['level_3']) && $level['level_3']) {
+ update_user_meta($wp_user_id, 'wp_user_level', 3);
+ return;
+ }
+ if (isset($level['level_2']) && $level['level_2']) {
+ update_user_meta($wp_user_id, 'wp_user_level', 2);
+ return;
+ }
+ if (isset($level['level_1']) && $level['level_1']) {
+ update_user_meta($wp_user_id, 'wp_user_level', 1);
+ return;
+ }
+ if (isset($level['level_0']) && $level['level_0']) {
+ update_user_meta($wp_user_id, 'wp_user_level', 0);
+ return;
+ }
+ }
+
+ public static function update_wp_user($wp_user_name, $swpm_data) {
+ $wp_user_info = array();
+ if (isset($swpm_data['email'])) {
+ $wp_user_info['user_email'] = $swpm_data['email'];
+ }
+ if (isset($swpm_data['first_name'])) {
+ $wp_user_info['first_name'] = $swpm_data['first_name'];
+ }
+ if (isset($swpm_data['last_name'])) {
+ $wp_user_info['last_name'] = $swpm_data['last_name'];
+ }
+ if (isset($swpm_data['plain_password'])) {
+ $wp_user_info['user_pass'] = $swpm_data['plain_password'];
+ }
+
+ $wp_user = get_user_by('login', $wp_user_name);
+
+ if ($wp_user) {
+ $wp_user_info['ID'] = $wp_user->ID;
+ return wp_update_user($wp_user_info);
+ }
+ return false;
+ }
+
+ public static function create_wp_user($wp_user_data) {
+ if (self::is_multisite_install()) {//MS install
+ global $blog_id;
+ if ($wp_user_id = email_exists($wp_user_data['user_email'])) {// if user exists then just add him to current blog.
+ add_existing_user_to_blog(array('user_id' => $wp_user_id, 'role' => 'subscriber'));
+ return $wp_user_id;
+ }
+ $wp_user_id = wpmu_create_user($wp_user_data['user_login'], $wp_user_data['password'], $wp_user_data['user_email']);
+ $role = 'subscriber'; //TODO - add user as a subscriber first. The subsequent update user role function to update the role to the correct one
+ add_user_to_blog($blog_id, $wp_user_id, $role);
+ } else {//Single site install
+ $wp_user_id = email_exists($wp_user_data['user_email']);
+ if ($wp_user_id) {
+ return $wp_user_id;
+ }
+ $wp_user_id = wp_create_user($wp_user_data['user_login'], $wp_user_data['password'], $wp_user_data['user_email']);
+ }
+ $wp_user_data['ID'] = $wp_user_id;
+ wp_update_user($wp_user_data);
+ $user_info = get_userdata($wp_user_id);
+ $user_cap = (isset($user_info->wp_capabilities) && is_array($user_info->wp_capabilities)) ? array_keys($user_info->wp_capabilities) : array();
+ if (!in_array('administrator', $user_cap)) {
+ BUtils::update_wp_user_Role($wp_user_id, $wp_user_data['role']);
+ }
+ return $wp_user_id;
+ }
+
+ public static function is_multisite_install() {
+ if (function_exists('is_multisite') && is_multisite()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static function _($msg) {
+ return __($msg, 'swpm');
+ }
+
+ public static function e($msg) {
+ _e($msg, 'swpm');
+ }
+
+ public static function is_admin() {
+ return current_user_can('manage_options');
+ }
+
+ public static function get_expire_date($start_date, $subscription_duration, $subscription_duration_type) {
+ if ($subscription_duration_type == BMembershipLevel::FIXED_DATE) { //will expire after a fixed date.
+ return date(get_option('date_format'), strtotime($subscription_duration));
+ }
+ $expires = self::calculate_subscription_period_days($subscription_duration, $subscription_duration_type);
+ if ($expires == 'noexpire') {// its set to no expiry until cancelled
+ return BUtils::_('Never');
+ }
+
+ return date(get_option('date_format'), strtotime($start_date . ' ' . $expires . ' days'));
+ }
+
+ public static function swpm_username_exists($user_name) {
+ global $wpdb;
+ $member_table = $wpdb->prefix . 'swpm_members_tbl';
+ $query = $wpdb->prepare('SELECT member_id FROM ' . $member_table . ' WHERE user_name=%s', sanitize_user($user_name));
+ return $wpdb->get_var($query);
+ }
+
+ public static function get_free_level() {
+ $encrypted = filter_input(INPUT_POST, 'level_identifier');
+ global $wpdb;
+ if (!empty($encrypted)) {
+ return BPermission::get_instance($encrypted)->get('id');
+ }
+
+ $is_free = BSettings::get_instance()->get_value('enable-free-membership');
+ $free_level = absint(BSettings::get_instance()->get_value('free-membership-id'));
+
+ return ($is_free) ? $free_level : null;
+ }
+
+ public static function is_paid_registration() {
+ $member_id = filter_input(INPUT_GET, 'member_id', FILTER_SANITIZE_NUMBER_INT);
+ $code = filter_input(INPUT_GET, 'code', FILTER_SANITIZE_STRING);
+ return !empty($member_id) && !empty($code);
+ }
+
+ public static function get_paid_member_info() {
+ $member_id = filter_input(INPUT_GET, 'member_id', FILTER_SANITIZE_NUMBER_INT);
+ $code = filter_input(INPUT_GET, 'code', FILTER_SANITIZE_STRING);
+ global $wpdb;
+ if (!empty($member_id) && !empty($code)) {
+ $query = 'SELECT * FROM ' . $wpdb->prefix . 'swpm_members_tbl WHERE member_id= %d AND reg_code=%s';
+ $query = $wpdb->prepare($query, $member_id, $code);
+ return $wpdb->get_row($query);
+ }
+ return null;
+ }
+
+ public static function account_delete_confirmation_ui($msg = "") {
+ ob_start();
+ include(SIMPLE_WP_MEMBERSHIP_PATH . 'views/account_delete_warning.php');
+ ob_get_flush();
+ wp_die("", "", array('back_link' => true));
+ }
+
+ public static function delete_account_button() {
+ $allow_account_deletion = BSettings::get_instance()->get_value('allow-account-deletion');
+ if (empty($allow_account_deletion)) {
+ return "";
+ }
+
+ return '';
+ }
+
+ public static function encrypt_password($plain_password) {
+ include_once(ABSPATH . WPINC . '/class-phpass.php');
+ $wp_hasher = new PasswordHash(8, TRUE);
+ $password_hash = $wp_hasher->HashPassword(trim($plain_password));
+ return $password_hash;
+ }
+
+ public static function get_restricted_image_url() {
+ return SIMPLE_WP_MEMBERSHIP_URL . '/images/restricted-icon.png';
+ }
+
+ /*
+ * Checks if the string exists in the array key value of the provided array. If it doesn't exist, it returns the first key element from the valid values.
+ */
+
+ public static function sanitize_value_by_array($val_to_check, $valid_values) {
+ $keys = array_keys($valid_values);
+ $keys = array_map('strtolower', $keys);
+ if (in_array($val_to_check, $keys)) {
+ return $val_to_check;
+ }
+ return reset($keys); //Return he first element from the valid values
+ }
+
+}
diff --git a/classes/class.miscUtils.php b/classes/class.miscUtils.php
new file mode 100644
index 0000000..de16ab7
--- /dev/null
+++ b/classes/class.miscUtils.php
@@ -0,0 +1,167 @@
+This page and the content has been automatically generated for you to give you a basic idea of how a "Join Us" page should look like. You can customize this page however you like it by editing this page from your WordPress page editor.
If you end up changing the URL of this page then make sure to update the URL value in the settings menu of the plugin.
'; + $swpm_join_page_content .= ' + Free Membership +You can register for a Free Membership or pay for one of the following membership options
'; + $swpm_join_page_content .= ' + [ ==> Insert Payment Button For Your Paid Membership Levels Here <== ] + '; + + $swpm_join_page = array( + 'post_title' => 'Join Us', + 'post_name' => 'membership-join', + 'post_content' => $swpm_join_page_content, + 'post_parent' => 0, + 'post_status' => 'publish', + 'post_type' => 'page', + 'comment_status' => 'closed', + 'ping_status' => 'closed' + ); + + $join_page_obj = get_page_by_path('membership-join'); + if (!$join_page_obj) { + $join_page_id = wp_insert_post($swpm_join_page); + } else { + $join_page_id = $join_page_obj->ID; + if ($join_page_obj->post_status == 'trash') { //For cases where page may be in trash, bring it out of trash + wp_update_post(array('ID' => $join_page_obj->ID, 'post_status' => 'publish')); + } + } + $swpm_join_page_permalink = get_permalink($join_page_id); + $settings->set_value('join-us-page-url', $swpm_join_page_permalink); + + //Create registration page + $swpm_rego_page = array( + 'post_title' => BUtils::_('Registration'), + 'post_name' => 'membership-registration', + 'post_content' => '[swpm_registration_form]', + 'post_parent' => $join_page_id, + 'post_status' => 'publish', + 'post_type' => 'page', + 'comment_status' => 'closed', + 'ping_status' => 'closed' + ); + $rego_page_obj = get_page_by_path('membership-registration'); + if (!$rego_page_obj) { + $rego_page_id = wp_insert_post($swpm_rego_page); + } else { + $rego_page_id = $rego_page_obj->ID; + if ($rego_page_obj->post_status == 'trash') { //For cases where page may be in trash, bring it out of trash + wp_update_post(array('ID' => $rego_page_obj->ID, 'post_status' => 'publish')); + } + } + $swpm_rego_page_permalink = get_permalink($rego_page_id); + $settings->set_value('registration-page-url', $swpm_rego_page_permalink); + + //Create login page + $swpm_login_page = array( + 'post_title' => BUtils::_('Member Login'), + 'post_name' => 'membership-login', + 'post_content' => '[swpm_login_form]', + 'post_parent' => 0, + 'post_status' => 'publish', + 'post_type' => 'page', + 'comment_status' => 'closed', + 'ping_status' => 'closed' + ); + $login_page_obj = get_page_by_path('membership-login'); + if (!$login_page_obj) { + $login_page_id = wp_insert_post($swpm_login_page); + } else { + $login_page_id = $login_page_obj->ID; + if ($login_page_obj->post_status == 'trash') { //For cases where page may be in trash, bring it out of trash + wp_update_post(array('ID' => $login_page_obj->ID, 'post_status' => 'publish')); + } + } + $swpm_login_page_permalink = get_permalink($login_page_id); + $settings->set_value('login-page-url', $swpm_login_page_permalink); + + //Create profile page + $swpm_profile_page = array( + 'post_title' => BUtils::_('Profile'), + 'post_name' => 'membership-profile', + 'post_content' => '[swpm_profile_form]', + 'post_parent' => $login_page_id, + 'post_status' => 'publish', + 'post_type' => 'page', + 'comment_status' => 'closed', + 'ping_status' => 'closed' + ); + $profile_page_obj = get_page_by_path('membership-profile'); + if (!$profile_page_obj) { + $profile_page_id = wp_insert_post($swpm_profile_page); + } else { + $profile_page_id = $profile_page_obj->ID; + if ($profile_page_obj->post_status == 'trash') { //For cases where page may be in trash, bring it out of trash + wp_update_post(array('ID' => $profile_page_obj->ID, 'post_status' => 'publish')); + } + } + $swpm_profile_page_permalink = get_permalink($profile_page_id); + $settings->set_value('profile-page-url', $swpm_profile_page_permalink); + + //Create reset page + $swpm_reset_page = array( + 'post_title' => BUtils::_('Password Reset'), + 'post_name' => 'password-reset', + 'post_content' => '[swpm_reset_form]', + 'post_parent' => $login_page_id, + 'post_status' => 'publish', + 'post_type' => 'page', + 'comment_status' => 'closed', + 'ping_status' => 'closed' + ); + $reset_page_obj = get_page_by_path('password-reset'); + if (!$profile_page_obj) { + $reset_page_id = wp_insert_post($swpm_reset_page); + } else { + $reset_page_id = $reset_page_obj->ID; + if ($reset_page_obj->post_status == 'trash') { //For cases where page may be in trash, bring it out of trash + wp_update_post(array('ID' => $reset_page_obj->ID, 'post_status' => 'publish')); + } + } + $swpm_reset_page_permalink = get_permalink($reset_page_id); + $settings->set_value('reset-page-url', $swpm_reset_page_permalink); + + $settings->save(); //Save all settings object changes + } + + public static function reset_swmp_log_files() { + $log_reset = true; + $logfile_list = array( + SIMPLE_WP_MEMBERSHIP_PATH.'/log.txt', + ); + + foreach ($logfile_list as $logfile) { + if (empty($logfile)) { + continue; + } + + $text = '[' . date('m/d/Y g:i A') . '] - SUCCESS : Log file reset'; + $text .= "\n------------------------------------------------------------------\n\n"; + $fp = fopen($logfile, 'w'); + if ($fp != FALSE) { + @fwrite($fp, $text); + @fclose($fp); + } else { + $log_reset = false; + } + } + return $log_reset; + } + +} \ No newline at end of file diff --git a/classes/class.simple-wp-membership.php b/classes/class.simple-wp-membership.php new file mode 100644 index 0000000..879c887 --- /dev/null +++ b/classes/class.simple-wp-membership.php @@ -0,0 +1,687 @@ +user_login); + if (!empty($swpm_id)){ + $password_hash = BUtils::encrypt_password($pass); + global $wpdb; + $wpdb->update($wpdb->prefix . "swpm_members_tbl", array('password' => $password_hash), array('member_id' => $swpm_id)); + } + } + + public function save_attachment_extra($post, $attachment) { + $this->save_postdata($post['ID']); + return $post; + } + public function filter_attachment($content, $post_id){ + if(is_admin()){//No need to filter on the admin side + return $content; + } + + $acl = BAccessControl::get_instance(); + if (has_post_thumbnail($post_id)){ return $content;} + if ($acl->can_i_read_post($post_id)) {return $content;} + + + if (isset($content['file'])){ + $content['file'] = 'restricted-icon.png'; + $content['width'] = '400'; + $content['height'] = '400'; + } + + if (isset($content['sizes'])){ + if ($content['sizes']['thumbnail']){ + $content['sizes']['thumbnail']['file'] = 'restricted-icon.png'; + $content['sizes']['thumbnail']['mime-type'] = 'image/png'; + } + if ($content['sizes']['medium']){ + $content['sizes']['medium']['file'] = 'restricted-icon.png'; + $content['sizes']['medium']['mime-type'] = 'image/png'; + } + if ($content['sizes']['post-thumbnail']){ + $content['sizes']['post-thumbnail']['file'] = 'restricted-icon.png'; + $content['sizes']['post-thumbnail']['mime-type'] = 'image/png'; + } + } + return $content; + } + + public function filter_attachment_url($content, $post_id){ + if(is_admin()){//No need to filter on the admin side + return $content; + } + $acl = BAccessControl::get_instance(); + if (has_post_thumbnail($post_id)){return $content;} + + if ($acl->can_i_read_post($post_id)){return $content;} + + return BUtils::get_restricted_image_url(); + } + + public function admin_init_hook(){ + BSettings::get_instance()->init_config_hooks(); + $addon_saved = filter_input(INPUT_POST, 'swpm-addon-settings'); + if(!empty($addon_saved)){ + do_action('swpm_addon_settings_save'); + } + } + + public function hide_adminbar(){ + if (!is_user_logged_in()){//Never show admin bar if the user is not even logged in + return false; + } + $hide = BSettings::get_instance()->get_value('hide-adminbar'); + return $hide? FALSE: TRUE; + } + public function shutdown(){ + BLog::writeall(); + } + public static function swpm_login($user, $pass, $rememberme = true) { + if (is_user_logged_in()) { + $current_user = wp_get_current_user(); + if ($current_user->user_login == $user){ + return; + } + } + $user = wp_signon(array('user_login' => $user, 'user_password' => $pass, 'remember' => $rememberme), is_ssl()); + if ( is_a( $user, 'WP_User' ) ) { + wp_set_current_user( $user->ID, $user->user_login ); + } + do_action('swpm_after_login'); + if (!BUtils::is_ajax()) { + wp_redirect(site_url()); + } + } + + public function swpm_logout() { + if (is_user_logged_in()) { + wp_logout(); + wp_set_current_user(0); + } + } + + public function wp_login($username, $password) { + $auth = BAuth::get_instance(); + if (($auth->is_logged_in() &&($auth->userData->user_name == $username))) { + return; + } + if(!empty($username)) {$auth->login($username, $password, true);} + } + + public function wp_logout() { + $auth = BAuth::get_instance(); + if ($auth->is_logged_in()){ + $auth->logout(); + } + } + + public function sync_with_wp_profile($wp_user_id) { + global $wpdb; + $wp_user_data = get_userdata($wp_user_id); + $query = $wpdb->prepare("SELECT * FROM " . $wpdb->prefix . "swpm_members_tbl WHERE " . ' user_name=%s', $wp_user_data->user_login); + $profile = $wpdb->get_row($query, ARRAY_A); + $profile = (array) $profile; + if (empty($profile)){ + return; + } + $profile['user_name'] = $wp_user_data->user_login; + $profile['email'] = $wp_user_data->user_email; + $profile['password'] = $wp_user_data->user_pass; + $profile['first_name'] = $wp_user_data->user_firstname; + $profile['last_name'] = $wp_user_data->user_lastname; + $wpdb->update($wpdb->prefix . "swpm_members_tbl", $profile, array('member_id' => $profile['member_id'])); + } + + public function login() { + ob_start(); + $auth = BAuth::get_instance(); + if ($auth->is_logged_in()){ + include(SIMPLE_WP_MEMBERSHIP_PATH . 'views/loggedin.php'); + } + else { + $setting = BSettings::get_instance(); + $password_reset_url = $setting->get_value('reset-page-url'); + $join_url = $setting->get_value('join-us-page-url'); + + include(SIMPLE_WP_MEMBERSHIP_PATH . 'views/login.php'); + + } + return ob_get_clean(); + } + + public function reset() { + $succeeded = $this->notices(); + if($succeeded){ + return ''; + } + ob_start(); + include(SIMPLE_WP_MEMBERSHIP_PATH . 'views/forgot_password.php'); + return ob_get_clean(); + } + public function profile_form() { + $auth = BAuth::get_instance(); + $this->notices(); + if ($auth->is_logged_in()) { + $out = apply_filters('swpm_profile_form_override', ''); + if (!empty($out)){return $out;} + $user_data = (array) $auth->userData; + $user_data['membership_level_alias'] = $auth->get('alias'); + ob_start(); + extract($user_data, EXTR_SKIP); + include(SIMPLE_WP_MEMBERSHIP_PATH . 'views/edit.php'); + return ob_get_clean(); + } + return BUtils::_( 'You are not logged in.'); + } + + public function notices() { + $message = BTransfer::get_instance()->get('status'); + $succeeded = false; + if (empty($message)) { return false;} + if ($message['succeeded']) { + echo "Q`W}dBZSTBLdMo6n$n_ta%&S{(NArq4yO8>fF)6$mw zM}Ft4*Exlyh#h mFwiUulYs$9(JN^e1e;k zZ@n#Gb4cX (OmmY{4IqBIGQi=6ezv+)NOfH{ahm;NvWffhg-4@#6_r%>f8IziY zDyw?(;-y{LpkD;%h~NY&AdHuH?#+nA+ADZ|e%|J|B^m@$3HL;!d^laQp1HuTUHU4F zl#@DX@RuvQmTiP!-7Y ={@gk`$-n|Chwe^-Rqo9% b~;LqFm+dyCcpW z2iGNtiO~MR`ic#9UBY?K2;JIbe~qP~C^O!oGT^Lct)1Q!cHBGuc}VVD?b}V}-f86o zWd5uWc(PX#UiSOY5=p@l#ntaiyq-=ZtmgkVS26?XW4>B?{yi$KDOUIDm3ZYyx(WP& zM*E1ngGbOs6& 2J7O3&q|pVm=P7FDG; z8Eq>&0($=!f0XwXTLn&?QB>qPKl-#()ywc)Ua`p=;E|@X?)hFPj3#PByi}$xglzy` z;sd`VHWfQ>8Ty@>WxxI&4< |L%ecutF z!E>2+?WnWZLu~++mjh8EcraX_!BoZJs+CIVW(quYO)*=0OKm1^NCzS8gD7con)ow3 ze5RE@I17P6;TLTb_8Gl#rms4tvHiFKX;A%`hMFceYQ-9167Mm0xyVD8DdMU=%!!f& z0 D` zRx8qo@pH1qetoIKin;=iQgxpL_4bQ-abcWyySYsVt;RTr$}fG@ ?$EOVl5Tw{V0P*&Q UUzN?0sj_AM=?-g@(_7v_8gTf~q)BVaKMYRAM#T^FsF#?=bQtsuQ0w2RB*rzd! zs*WfOq_xE7@xl8h-EkIWy2yxiWuuAXT1lzcu{7m|8mfn(Co6*E>6rPVgB9pkQcBa% zGp;RKGg&Pdw9R} EP(S5*cUr~Vb?(x6TC zxYrUX?yqS0V=|wU`E=v%rj3dw8@rqmobrbpQ<_tC>M3uHa3eJ>j;N{N-EX7)C>I@> z#^ixqBY}^3U47ep&9s~Dg(8thccz$PUl)wYp7GG}bp6;;@iO4DOSvd_$~ljX;r{LY zaT`DA?plhUc1Yn@_Vt~{B(Uini(+RV0s4%!1txo}JUYs$s!tN+VxlAZ?ZQ)V!-?9D z?;jcM@z*ohdj9%oxStqqJcJMX&@I4r4$RIQ buz%E)E9(yN3q&DPZ|PRGL9ku~NgN0@IF?#&}r>Neg2N533Cmz|XE zdvoRLqe9u%^xCnVxzgBXek7iElWzlSTMh&7F@HVkDlH%Ttp^z2U?!x6*IV=73{iXo zwX*h1NERoBSOz1?4QgfUhpK6py;$x*=j8|d h+CidO#eRv`L)@zA!V~aZwu*DWlq!i`4$--?hYZ85C z`oUmU(R-}v<2~E@S#?rALVvBkziu0y>t(-;-Mkwq-dMUJ*R;?j1+tj;X=qdB4$n00 z9&In>j#6%*mXF8sk{t#b(<4ts%|Q;2#_o+*4p{L#_D$P!E`{{mXvU!r>G%cLC{=&{ zdbDB&Gv#b)Bw7(^pp7nysS_Bg>Ipq>VtOB6M+r5mJsNIks;(18xx6cU9eGS6dHd|q zG%YV)4x~-Fa^$*;@Z}OPuQy+tU1hclJm~)?!}T;eB*2zJNW(PxDr!`wtaG_A9A_j* z40qb3+_KVwBz9iErhT*jxnr^_;*x=&YGchHg>)TsXpes76c@b$@+G5qREukrtzlvj zn(VV)q*2Hv17`YBc`unGy{eoHJv;`YE(q;|-i;f%c-UFkWa_W-2=gTg (+J~jOAuIJuWVCI&HJNQ?i_%N3Ug|5@q zHUhmfGRzSLL0Co(*F!L1v2;@NTzP#@;TowV)m2xb^ne<4cd+ALIxdQ}R5ryA^wi^K zO~Zm#E`B$3{ZxF|TKY$Svo^!+3R5rox{I?mhu{p0-?k4sO=iY6JR5mbrf}a%f46b^ zek*1}vkm;TPDkl_u~~E48GL-5QE6a$qwmR(^U-J9Lkse}!oOrM`Em9Aa}Q3=I)7cc a7sN6@bz+8gd&9yXfv1}ny2NE~@_zt&!jduo literal 0 HcmV?d00001 diff --git a/images/addons/mailchimp-integration.png b/images/addons/mailchimp-integration.png new file mode 100644 index 0000000000000000000000000000000000000000..267278a0d7dafe3843fded37082aa0958cfcd9aa GIT binary patch literal 9543 zcmaKSbx>SSw=M26xCVy7-QC@SLl_(e34^-@g1Zgw5In&G1PdBm6M_YIcY-^Q-*@kK z@B8C@?^K=BQfsZfclYk@s;(2Qsji5PL5=|j2Zyb!B&Ypa4!yqVXo#=hDg;5+*MiJL z9_*pxY~$f=;bsjdZRHHH1}ZyRytmf2wy^Sb9kv#OgF^&Db-^BBwYMUc&W;=w|6n+L z99>@7aByOhJ}wrP_SPOii1mA@lQ`(KtqTN%T8V@7`PI18Tx6|np-O&k);fObx|V+S zmcmvbNeQ5skI1WlqqT 3aq9Hly!Eq z2J&-ov0HKpZ~+ClIk T%24yoZLL@+=3#!JR)3N!2f z6#m=RYfBtt>*3)d!pZ6F?akrM%i-+yo|9WxSoj|e9v=2r1iQPhlZS;5yOTTZzZK-H z-7VdqE*?;4C*VJd77%Ao4{^||r~eg#qs#xuI=TP%Fue{Kr;mjTCpQPzKOy}asHXP+ zhdMg`58B;B+xma~{eKF(>-xG_b81_=JA1lWzD}GC?LVemL}cBpEj*mvbe)|Y{ymDC zw$2{T?zYY@Kv^Arpgz>e%GulfUuZQo5oITL4+|$tYh^ic&?}z<3bhgulok{a65!+K z7nbMY=9cFZmgW`^=8=)*k(Lvb5#W~oH&@Qt($mq}$>ZN#tN-N+{73FTiQwq+YFW e&TBGCWr3-5o_`){t*fA)p%KXN%=o#FgvTK}(U{&&}F?)+2zpQL?l z{7>3jJH6(*+iOzqE(Jflt`#`Vx4QBoZGRMc7cwVj(%;bwhqYbS;w#70$#%?KjTQeX z^?5n`#r!U&3FZLgvKov7KWz22hwD8bPucMicvj5|;vpP<_Vf@T4pX2!?@fCgraJA2 zKkiHwi|#RC!g5t)zF+Cch48(cFOH^K)iiv)?TF!g>;4(a_qbzlJy|WEI(9yg^+B5K zv^!0?=xYj8`Q>JdO2|>bt1?;@cr*L0(MLhk35EcVprxS_7n|s4?=mw~ )k>_Ai~9Lie%@U!@|k!pIWcPSW=KMigt!JE&6*25Ttloz|cbph`BZ5 zd(svs>Y9JM-W{8oUuZA)JYN&<#^S<{=NjZ}K##VX1is!Mf7u&IE%e1C)4)*-r}XTj zGRxWA+|ccs(4a p1O*{ZW&?-x7P+kr-e`*W}0p<-*l4H9BlffK$i>XvRs4 zN&WCwlx_L_ayHQrq(f$smuJGV*`B5o-)72)Qe(}c%>=j|$~ v-W_@JxD~t^b4qKVezZw7hGW+FXF#mFUsIxiD zu%?SIs)EZgLZM@N@>>lX92`}hvYfQ8&%#k2IygnYb@0wg8OVkgEJas^Y>AJAAfttq zQw86HBcoS~7BW&@WKPsT9EXTZ?kYR+Nz>uGaYP8$4_h*pnL^OpyduPUx$2R;Iz7=F zFQz<}8}Zc)Da}2w*3i k#*l=VNa7;ojM=xt$6~`>n0EHdyj;rVAU5D~{e%JfZmZ;qK1l;P`59 z&DyG^qJ`7cp{l58GEJ`M`Hd6|-z4$ksiC3aD5!U}0Ip6Ecrw7U*zUVhqnz0jYHs>u z(n(moX3j1e22Y6=hT2pb5^BZACjJ|bdpew%Pbr#&_*!TchY3c>le~h41o;ZV0-oSG zRUSo(`9=0{A)|D+Cm&TepkWCotVGekx%NxPJqdI)F86M7KAXf~hWLi_OHP8A&;X)e zpz nrP#ttcOcf&_QGoOsirQ#;izs#sJ{jD{EUZ3CHrQD>7FXwy6S2I)%j_i zcjmOMf`||av)FdnT`6FB97l{nXpUp@WTIG+5QDS$x{on7E>5?RXr`g|;|LuVxVy2j zGFp$~`{Z`7;H;5hjaxL^?QWpIO-Y5HjIx$A)hI2}DB#SK+h_F&H_-X}Vb^T+; 9R-q4+t>&Q2FB9Gra$`;>D;s69~>o-Cz!jZA?l^ zV#Py2JKxSY^y-+O-OW9^%sTL*PlkldZK~HTKq=wSuB+PvSNr?7Wp7=+pSl&?Zbagm ze00%zS
P3aahdxOo_YT6 z=HmI#SKhZ2Xeuy^ak{m`gP%ZD6+vCUa*ILp&eC#|+lOdRP4Qdn>CJ@~Y=^3kx3EGH z;ONL+SSom+W=QixI|EizX&>~?!f!p#5?N=ms 6} zx&lLFI7^a}5`c3K0`a5U7`8`#%RN2L<&f`QWI09vP7Rb$2HT(2heuVT+xe5#6y-sZ zaIzV2g%d$?pQ@_Z)^IT0B9;@xi=*Rbb3@E6oE%$Gybnaku6lc0 R@& zaikY=0j&LyuKGzO2IiUnVb{C!r=dM}krYdzTzS8HYGb1 _#`-Vyp86nK!H)7saW+j9ICv5| zc&$`=n;%JeCOdmA);D2vl@$3Yn=`>-- Y{oDO-Q#7T{Bbd(iACnDdtk3Vii{fy>=!+G7XO&$bEq+N2;)nyFz_-2$QSBv zP*`^ig+h;^>wgcd;X*KFX0H8n2_p)hl#oAv>_Aw6L7(`{vq!{umWV+_C9nJ35JJud zIo3dd#!rvW=+(n~Gtt;TS!F}8LO=_n9UtijDv|1xZ7rv{6Xxu&tahfA5s9bi<%S^N zuv?DUa}>jhDgCr2IzWG!cs=Fbpi0NEz06>5RE{0Z!W?Hfzr8_I`Aos7MmjVTZS12m zwh@)Xf}naeo-15TJ%m@^@C|)bn+?#jEb5uaCi+Fx)W(x1$|_0 AjnVP*4tAqIkFm(8aFU4InQZZDaDX9)khLH^@=OvR4Kp)InEEf-7+ZUL zQwC=Ip-lbBGM1r_dPoR0N;z) ps6LL)0O~jb*dq3GOX?ib~N5n$6r` z!7oC660KM*>_!&;WKQU4HPF=D+_1OTQ1T}ZEsLKYC#$UNcoZYQS+T$xVBm BYpk>}UsZT~3laSm2^2je zO;_s>8X=5`*u<)Yff^EiHOwQ5SMNcy)07{xDoXChqcnt-mfWDEJhtjtN!bEuL(tLY zgVogqXp>0iiL|uB=h#Rs`T^W-yFY!p@X1BMO_U!9S3+6A%PtV>EHhm9x5C1Nx#Q__ zOwP52<_K3!__&Qc(?%f?N++q>n2;lW_ydX2FN)J!R0=3bm=g`&$h+3#E;*l=2xWXo z2;;Ta4peD#v5+t%?;{5nmIML8+Vu^FRlPSsb12nDCtjRk#u6hUflPNwKA&YrHAp6d z`j(cK{wz@@wQFd ;#7NL0(cF09~H~?HONUO(eRDT$y#a^ zCVDs{65QKLqQj(v8Yb)eJh*9w_pb#p6Cw+!ho`@P3a-Lb`qLeG?Cej-Z0CEm=!4?p zw?KD==cxBa>$*v%SiPK6E_~zj?Me6*@`Fx~VrCgsXxQI4)wAi;>|N>rMlamvLfMjl z!g`r=X=IBw;2Q=U9&NG~@l(xR%@zU|XT7W{`LE!`S&<78o|+77nV$AqZcR%agyQPz z P9A&?X)3+M$BxmRbS7BK#pJn+ zYYs+mqQ%ixJh~yzjNacIAR33VkayHN5q-+7x^q2U$$op5&KDmnj4XZa;W)+6EYr~S zdn)E;dk-nE(m@JKWQhjH^*eFZiR)&}?(dxJu$@D;uk<&_j!Ojhn|Eh#r_Sbx+)+a; zEc1WGRa8_66yf#-KJq &cM^9X30Gb ziW{M@$jC0urw47R_SzD-<|YB#d{P1mEItmA3VE)0KeR0z3y0(wi0~%<07VRmz{IAf z!)yTbE;M!&H}d(?DMsUkGT0}a1`DM-m< C%|71*%}%|8YJlmdhQhPdsU>7 z)l8xNnZn>Q%g+ YrsE$tns;O5f~XeEIkKLC{^L`;q_(MJ5&pzjIHWn$^)ZcCvHPgz2&c1^~U|8 zL+E{-SR7I|fl`LiQS{QNyCWkLt$Rmir@!h%y-(=&5I#xG*ERTn*auR=Y5zhV%>>J< zX!N?kqC5nEs@17GuQag;w(E^7O#=u!GcTe(r_1CNE0a(}C>>$_#8R{;=-j8L1&+=% zZu YlO~-AUR(7xna?7#}S3WTkXC2T69e9~7J;s8B1a`8eheJ1HK!|R% zeCx_BQo^%k%vsd+SK)7ucR;Gm4{k&leh!@A057lw=+FdbmtxCDqMyh+-OCfaoenpk z2gc=0$<>+GuCs8(6Zz&>{IJs|!5b&a`50&kmXaabBpVHhLN2AzHQ=-6>$lQI127nG z>x`6}iwp5%0m56a`p6|1MT)6?I9-aj7d{`54Rg~1GzJJM_szkdh2&;Hzu-xGm!jdn zA06o+5EvI&5Ty7Crff#C&ECr;dFhS=Hv6Tya(Y+>2t(F3zdHD*zXh7l&VGxGo=`=X zPR={_u^d%-2?$svCNy5`TrOk%QevnK9U$a|N4K@lnUp ~fMUAP$3(X_PaVF|De-Vz}sCqpyZgcZ}kwx@+d z?{>PVtRUkL1GP~IR3u2$Z_?snZhx@~^4RHxCARS3fq2iM1@;QZnf3s-;c6Cd6~I0h zT*g3p2vbgXTXWOE8zhN@_mpe!%6;WGjg4j%bew4TT;ag)^{!+{Qe#MfQWX!6hAjS? zVL?@Y6W(^;H&h)RZSo5XoI1%Q2s8>x<#lZSq*7k#IQ?R}Ecjt~*e4&=e}a)2?>x4# zla!1a{GFt^#=bd{OQXRd?!! ?37Susx`j6F~Oo+Y{%84()_XjI+nGZaYqXY zcRb9I0bJg~ ouY;4vH>V@b$N{gr{F*DcM(>%R!o z`-9GrAZZ-ugdkS8?>-Vqney&QRMdgz-5ekEfx;E%NyXvFupq}Y*bWMkkQhj$%1L7z zwEug35>8IP0xdquFxeK}h4MLabmUWSq_?9i-3@YrE}kJwrD7Lts}41B_F?aOx+Y;a zG2eA6IoyncNa)?Yq%ryBAEWfeCNjyfd707`QY1#a4zb1iG@uFDPqD-=Qc;e=O;RB9 z!|S2kH;G*%e0>unZ-)b0Jbn+^&cLtAG6vesr?~MBAZ+)2|I(Pub9CGx8d0@S#>EDT zW-?NpYb5}F&V28Dw^cp4HRBbvby}fNxgvJ{3(oL@v!pCncv=z_SVEU(G{BUng #T`M9E>gB0bqisV5Oxe8ZA4 z{AL4>(9IvcsfFRJu>oWo4eGFhv20xMH+Pn!?|8DDKGL+s-c}=!ybRh-L~jwq5(hkn zQ$|J%{HRZ!NBNr-84ARIaC08%MBgM|4UV+as-5xwvE&c*{NZ2B;Au5Y4;~${D|n+` zZ#!|i;&-?y8{Do789b+#&QxsLi~4qn}6ZoT3WfY4kLuDC$E@pq0zbyS!^YZ1;Hh z#k;z6*3ia=qjx>^lam$VuD56FiMUVGH7N|Z^^A7u(zP{@rD1eJ%DxgB4ADo*G5Ps_ zq;>Md1>cl>lR$)Wgc-~mR2xIfiP}#$d#O>^dkx&^5OE|_zF~xT^0PUfnbcbR4fEtz zqEGSy3Hu)qbF5BJ^UX9MM GsQbDg0LyO4PLhBmpn zU9r *_>XcYa;P$}=*pRtw&n9*f=-|Nii>lkwqnbb0rW*`pNKp5X8BQSy)?C8TO?ENM4h`aimiHkHSsgWk9Zk!8q|szM o2SR_>t7ap7cDaS3!>B%Kan>0=)`;QeIr3stgD*At z{n3Ms5PYXe5ccn~2;GXN{=#j`5`Byi`Y)f!LPOV>PCCU_GzZaVUf>h* |tB=t~xzKNHtk^9qm z@_{}Y0%h}$L3I2ZAr ~5u-7}>(mmW=|GQsK4N7jm)pE1V z7x~8UwTPksS`<5&IE@~eu`M6pa5lNOcf?b%Lo!-C@DwIj!4d73HLGp$#I(_>qD8#; zFnHyc`a$ykf}bdDCyyuDNHm`LlYD5iB4?HAAbbf=K9OWH34zFcS|*1PWFHONH#x_4 zFIR(*Nzv$iptz5%?=VhPYql _K6gJ?SWX6hUTY`k zi7r#v*;DspUzr|U-Q09tM%mG*gcV2#Gei*RNEd+XEr13CZ{@kcv{#BOIi%Nf_zh3H zpOcf5N3}+0sV5(=7B9PyCas+dk}M?Ei;9R9nFBZ4Zkb54RXP{u^8lgV`OCjG7iVGX zuEW&qU8-Ija3M!ZZz$WNp9#?=WMsu$g5Re!ke>_lwS;JP-uaD2fBGaF$tPbBTjUkQ zp)#Y=1?g1SC!y?XTUwz|;t8@^tUhKLfC>1|l)zWr4Ob(Jb}fE4mzA}WbyxUW&_!K$ zbRFCri;aDuqN=)$T$rel58MP*kaE>%)+ysk#IZ;-t@@jIZALUT*^ttukKU3al9rN% zz>G&bEUe*1njC9J|FnYei9kW@rd5BI+SiXlzKIry*_~~hoTpd-fWE$^2lD+J%9NK* z^I!s=8qFm&h|Wz#d2{JzXedH7SqL+$dr6InCG_rOIG!Sd-KfcamYjOh{f!x`nlt## zwpc#l_K$_$(U)6A12|fUtNX<~KW4fiyWZw7>2K@I?o}K^-3on%bcyi4RXIXd*-czG z+NTZNQDhd`^+-Q` TN9UV zY@D1p)vYsS84yq{eq8sx?kq#zkJ&prlcEzlMf|z?^5jTc++46`%BjjKweisBj%ZEU zqmC&AXC+u*%1s~X(U3Y6`+6_&1t+JAXFrwNa=qw|2_7 hu`J_}EWdM-KIb=_pwD*%O~)tsL84{-jjb7VzW8`O&~RUflIRTbSH_0V>P zA=#ns)P}!GQOxX&xBOPEa-Hz?Fn$9g$3*_owF1t&mRys+yjGCY+yJOV4*sUry`ipM z6~K!vtkJ*FTZDZLXO(tHpDyx!4qMK~WX&$4rJ*!^S5bT7w4d7^UiCq}B%fZ+@*I(T ztkS624X#99(Q@r6nFIxTt(dhS$&s)-G2t3?^Ys4B(BbxEOx;Lz9wEC7hQItl;lp#n z@y`1%8m!R{2o&Fy<7-=bN-n-QK3P03vqlt;y?tYQ%gdi=k3qA(*I U>veDfZr2;vC# zywf?~++bRxdoy(0K{Nkwf1ggPCh&Fx&6GWWV-^Zd&E)sI%-cHa!BbxwIR2%f@dal$ z6HD?l;S~1p*fsI -0Vg-Q zJO=LK;=-O0xvN8?8GdPE%n7x;X>pORto5B |DwtOcaO=gLz7}Or zm6(B;EGJZJ03A?;(y!1g4A8xKH|%~noR1YM!MVu2$R>B%P2_sRC(R1D+#@!dnVt0> z;|Sd;b~xL$;~ZD}g|*S%rrzvU__@BmK9!?0-&>CNlm!(Zo!4d@3x6Z57ZIa>JSMq| zu${_R#aY-|TVZf8-IWkP-^M1|s+Aa6+x9@>R@8DC*V%+bE%Ni(Z*+VPoU9gf;b@$y z4!gM5hoG&_UQEk#dk^<$mfiok?lQ^$t8R&p+^9y#$~rO`dG~B9l}bVrNE2w)<5 7G(eC%^9CblXFVJndnhKTRQ!AuSXdxl~-)DUcIRMOX}!{ZLvI_o=~l{z^I zWQOnzHa9b>dqqd$PM8z4*_~nV`9l1rrYY}j93b^%1w@a%IR5x!tZUisGYk|I<0YS& zs0%)SPChs|(2I`d?m6@3V2%GtiA1@Avq}fA{P@J7mG1^yDyQD*-hG&wdc9ze?TX2Y zpe3hv8z`XM#%NJh(q_HJYTF_HooI1us>y%ZJfoF6g?iI@A@*K+9C31-_9Jh20a0?l z6aJu%tkIqukT3?i-wH|)kvyCa(}ss1#cDN#U=gkVb}JFQFIwbD5p&$?^c>C{ueWaF z7`MGxCyTsM44y04(0Fu%-_Xm C5`|MfZw8XIznw1A9o~N4U6{L d3CuO8S{|;12qlb A3jhEB literal 0 HcmV?d00001 diff --git a/images/addons/swpm-custom-messages.png b/images/addons/swpm-custom-messages.png new file mode 100644 index 0000000000000000000000000000000000000000..cf2828f51cc27043e506395f6b27b5ba286190aa GIT binary patch literal 2202 zcmaJ@2~<;88V=%&DB@7gR0UgoL}`mH*;rD-QXnBDN+3hRDoaE1fE1D!lE)SSEg&c; zFoH#tMS{v&&>}ey1RN?tRV)VqOIV_a14R}UAt(s*V#Ur<=AHBIcE0cX|9k)MzVl8n z-CXpx7;QnJP Qr(3}I& ujoMxkuKiWPCV;UEkIgQ0w(9eTE@6%Fut zc4%))296;jgJFD{R1C7DZfveJoNLWPe`OEY#*z>L0SI$|SV4qPLW;FRzttrnW6d%a z4ZMZG;dbcXlJa3N0Wu^80ZR YIl)jAY==fN{cQ^Z(R*2; 7Y_7!tD~C}QmtnPCtNNx~oz zKxSD2-h3esijllUGZ-YgPy%y=T#!z&LnC|)pU)#%np3|dQ>_VB<~RZ#?`&a>v$Als zbjDc`apsOx3Vw}Cfw)luPzbMadB1bb*X3%AAP^yuDWI4i4f3dBNC3RuFp0l@F1U5| z-f?;B=Yn6Ci$#*bYRdX=WnNoCyrUVuH!U)GZ+lRPcwLN`+L-F+jzVd*F RmS66Kxe_(}t`JCt!M|P}oYDn#^bU Hy^K$F4?4#(G9TDP>T4vS6nM~b14P%(N z%>}SSo7M&$;3(>U8`BL}H|@VIB}|Nscbm=S4g;M%e_<6ngqf%;kM!twMkZ!5-D-V% z_q-Ut?oeJ9?by33*l^iF<(P6lLrM;8DA;FcP&=Y;Z@SHiG@n}FmYMw5Nxh1Zjw{{T z!9BZ1vlbGrmJTT9HtmNBn{67oQy(TK*t{Mt(cZ^`j+^$>+o;d%Tbci-3sJsO!8WK( ze46pyFY-wf({B3?0*?T+ViI-^cu@luQG!#sLx(?3WR)g!Ix9Vx1r;-!>kiQAjf#c| z<__(`DW^)^+$5tcg-!YAvU8YADTkP &2JC(UX#Ku0pQ~A~`E8RW$UlOp#eh%8Lvvn2SHjorSk6*Tx zIv7ZmlO{{-J~^3v>!lvu= A;cq+VTT2)^seW(WwKA%>H=#3PI#yefy<$%)K zw^`r#lyGAy5a7PQ$23G%-w)6}d~)Z)-q5W{W6adE*2#darYf{=oGvd7`}&QxjOxlq zt}TR`Wb6Klwxrp7aIGEx5h<^>SLwg)xnH^CUBA3|ep>SPlB`eUZev xH(l#C1k=s#anq$1{`8N}e%5?FyV?kTwyTdc@4-EoA4-|DxA>V&H z(v=^~m)}aU&lxtTjVl_;0e?2ytbVfrvFNHUzhtZ7__od;gYQu7zL49Cr}f*bx9yx? z477J425qz*Rm3dQlO|U3Y|Q;Ve)yq;m@@wd);@}onXGQyiNd`~OV0ZG(_ziu0^Qk- KQseY>(!T)qR-{w_ literal 0 HcmV?d00001 diff --git a/images/addons/swpm-form-builder.png b/images/addons/swpm-form-builder.png new file mode 100644 index 0000000000000000000000000000000000000000..fa281817f10e730f9c8ca2e9d8f8f6508d9a6e5b GIT binary patch literal 3243 zcmah~c|4SB8y;ChQHj#xZB(?F%?K0O3z=dF<@+#ZhGEQNW|*;NJLT9KvZbMHl{MQz z8C#1cQrVJ}l94U4G$}`Xqf?#lkJI-(zu)^l&;9(a>$>mfey;cT{%(8X*g9ENSr7=c z&dSmpBbxc5Crw6N^e$PWdq6a8Wm-5h?Wvwj7M4x`nc%4&1i*@n^&(&hSbU)0LxKSa zBo25xI5VBmHYgmG48g8qAZ#*C#0G&3jMy|RjznMr9t1CMiXr$zeG?e)#v6j2bkHy~ z&5S_wwhW>Z?1N4?;DSgveLUFc2w=cQi3G?5CKh0meJKnS+YtOs7bRM+ZbQMqHwcqt z2>z=mXS6L~Mx_$~9SBSd2Rj4<4#6RCZKSTQ?m++ngTtUO1Qd?Yg6pER5hxf8_&&g* zY;?RQ3S)lsdoEFA2qrR_G!zudVzD4BZ3va_1%>PD>#u4c5LzOH79)_t#Im(04D}xh z<^%?g?oDHQQz^izBG!ZI&ol&!D*fvSWZF+z3gdg5L=A(ou{0 nQde+v~vf1pKnwpZ5lEuZv+1c5mq9PiN=H=xT78Zub dC!=jYqn+DIhQxpU_d5)!CX>cqsv)2C1C>gogn!RODPg+gIaP*7J_ zm!F?saBy&6U*F4@FP}eu-rnAxk&)5d-2Cz5$LrUxU%GVZ!-o&4si|Z#`Rv)V&z?OC z2?;4KE^cgW?CtGMNl95)Sa|j7m9MXFT3T9DQ&Un>QhIv&`}gl(zkVGU7 lSEb7ydHFflRl)~#Eeot*>%VP$2dqFZ(k2qc|qWp3iYe(|3B#qEW`mS&Utm&a*w zfQ$@ZD>pJ691|HUJGc<~nlW*0+Gj<`>e{Qck8fAG7&f<*PtFt!ARjU$*M2qHuvTmr zAQ2s|sJO<|SVrcb0}Q)EYyZw!-eJybrJFX1P1+n)IU^b2(qrQ?I?Yz<_2D(0CDqRv z5w=zKnJh661C0J34z8W3Q^$^0MA_70B&`?;d=rZ7@YUv$Iulbf$w*tXU3O8`_L6bd zkAQ~v@^+6|WAqSASKyxcKHAL~6L8^lRPz?JE_0S%M;)&KS&}t(Ziq6thN|bPa$oyr z^;f2yDao|AodiquqlM)|r5=xnQ?bhV8q*%vD)i6lGmaN!km`?df!_6cn_ku!oQdgu zcWz|+WUhoCZLj~?X^*d5kM(wuwA70-d91?P)~}vbtwnH+2em!%VVj4PNSpWYVNUl` z)jnpGOlYK%Aq&xt#s2j#QtUz}tzJijIKBiX2G~eaH=Dt_(5lk&R_t`lhYn%swC$dU zk9}y6&Ze20Catx7 F54ibs5hOEM%D64h$C4j2iH( z)TH{Xz^nYuD$Q<(5SNzmi1EII+ZVyd7Pe`-4O|vnzPr9Q;_Ve2f(LY`^~ro9&XkyR zsUZ}U=9a?sd!V;C(s^beCgD9d&ASAs=hj5L?faL Spk7u{d>Q#cd(f9i~w|EtynW_{W;LiPNmnb)>8;l9o_% zJS2NDy(>#4`S5h_R%gaq6ToVm@oyC?H+jpUWSjm49QD+ONi!@v3)ws?ZtOppdJlw= z)N$(*=P@ J%TVw7oemA{;ti`s$k8@SagHOGXRH$t2V%1|@RQw5kkT6u7k!#= zprq#3tNT=Z+G}`1eJ9T<^?K$u!IAJo4;ADAt@1!es~w!VFNy(ibakuKoAB#hi7g<+ zLYuO;;K`N34y??aa!r-vF4^uUSWrJcJq_Y16hdc%>vH>cgyafXUbnU?d~$M5UnkCo za%LZ2SF+Z$3r^Z4X}Ix$!#lQ?2gs!C*74S7JGD*hq~pZL-}SUbk9f>Ds!1to`1%&X z7I0V~-loOnggih@Y75{lq{>Aqk@lV7jLSy8*^=Z=iE8jw#Mel}!{6U+y>lljdZ;vX z`^8tBZkIE=Ed!_vS1an{#01*!AMC7JD(c1vibjwmj&H$!dlN&~L@|Ntr{T!c@XoLs z20Qn!zjxIFapy+8E4w{TEMGO#{L#z>l8K5E1)6?rAQ12H bQ9gVR#w~+M9@I2 zT$vwTx<6R%iAX)_DE3wIfpw%9e%oTEFn;<~`(f=Pqd{C mHAy6DbuL>n{xQw)(|VbLZuw z; *MUa$b+}Bu1<0U$} z)hrHN+AR7uY8H3D*{V2PRK~Tro@^WjScI7Aaf0(htji;uvFA$4T=X~xWDq$mQl@z_ z4-(x+4T=wrG;q6&8`Ukwi>x_ISF`tjq%^C=1LFo7ZHRsIkJ)W}!)@~tyh^K-Gcw!7 zx0rNM<{gfxmuqG{=^avdmK%W26Pnd6_7_%%$0Iu=5;pA~e=E0_C_LfvVA!T)uRwRi z9g(?|lllA+?mT+pjbT?X@3+m-l(u2Gn7d`M^Un2co>T$apVXI-&`0t|(+7A5^-7$G z=jTRpT_%V;z|qM9(^iNx@>C3JKmfr=3MYHSnuc6(m(IaS%jUq9=F^d3ikX9pfkA)x zUcW(CNah((S^vsfez`wSdnCI8vAA6AJ8|3~O9JDiA|<@M4(oQSDGT5i*1C2CG_aQQ z=!G|O#Ez5IMxl)q7s(3AXoF(X?Wvn+_LD;=mnq81Gm8>x!7aI`#!f0 $z8sI9{kJc9_cRoxDC8F=M?1$q$4%=A!4+E&ty?($v`ez{*+} YDA6a|B`!hpI7l?CEKZo0nYu^*2QnwPpa1{> literal 0 HcmV?d00001 diff --git a/images/addons/swpm-login-redirection.png b/images/addons/swpm-login-redirection.png new file mode 100644 index 0000000000000000000000000000000000000000..69b535bc2e4e268fa9058703edd067ae2f4ef6b7 GIT binary patch literal 2996 zcmaJ@c|4T)AD{caNr^JXSu!(^vzd%*#u>6wWTKdPWJYtC83u_Y*Ahc&r6N%Z5nW5^ zB(l|ZsJ6|Kj$Ny4cx_wu)$to`wfo2R`@X)<^F3bg_xp3cp4U?t8sbO9&Bs9?5MqG8 zAWXmZ>7QK~xc=P?bG6ei#u}eU%}PbQMjNXFAzord97qq4#clw@z*uow(pAt60)f*d z;gOoi;2^F@A!EkQVwkBirJfCexOt>1V?~Leh8_oQkjQzAmuJp1=n^rH5#b!n4p#EP z1c`sT3S5~U5-v(l6mi514|lp-DpxNc12wVqRM|$knw!dFeAMOY_p{e52K^&MlgMNI z?NnrND4nlRfpljk+g`+WVbfgzCgAAg>gu|L?!X4vEVcs+aIgnlxsDE8Hk wNooxh}Qg2xRsw63(*jJ^H(LV;vm3-9~!1t?q zpSa?$`@;Szm!)@xHCxtymHG3PzISG~Uz%2b_|o>ET;J;|eN!8c9&Ls|U>ib%!hKMV zUJxh@0)@gzG=r8Qq=Toy(Lr3KlGbn+Mlr-jC@{;{88i nLDSFhd1j={ zT59DbQq}?5!8_#bCt!2t
nwdMz+wh|wkLD_Wy?G*+$v4A4cP#l4vDx*|hgw0(5 z#o$O=4?ziJOmG~z `I8AQc4S^s}2MD~vQ-6Lj_$ {eOGVa8t#$7inOhW8E_jI$f$eXiC|TOTjHWK<+VZCknk+{RJK z@` Ng#aguJi*3@!_BtSnHup_kRPlqhtwjwUj2$58?e|{-T?O|s5#LG+fO7r9@A`w^9 z{fi;2?HvDPaXiy(&!yz#+~&D3$q#k&mJe$`m}cy4T2T?T7>m4!R#xoybWX<=R1} z-Fl+^`!0-60dD0v%#6N^Ct166_jfMpakQy@UCU>LtSq*mv{uwBpI#jr)1BH_m4rA@ z@vE{qy7#7eh48ZPDhu=Cw2(t?eY%1*{bh!Yw|8=$Y!2?z-3va!e~=JStu<*9V~nQ> zd``=?)J^j&p5pBmcfW#Pj)e8U<5UAr`0KoL#dm=r#*uq1Z0oE3n+&Zjfjz&CL`C-3 zOx;;ksat);u1n|fB&+$tHVu1Zi=En~+NV8jp~0mX*Ic__yZeu&-2Jc>%ju1AMh<2e z*9;V5cZ2zF8={hKuC^O?59)j8<~ny=n(MOST1~9C>ia6}xb)zz>1Hipp0xKm|L!~N zv&^tOtnsrK3sDuk)2W@s=Zmq%qw=x#2s@*og741M2@gv F)b7PoA0Orr}&{cJ}(|LD2yZM;q8_ z@UJDpYW2~G`xXm2UJHR@;djl4Fb> p(SHR(17|G>$C)_wwmPwE!g5|v&ylnppH4?Dy^o$Ypcq4KD>lU zLC;XSJ#9_K@8d`ONq%c`ZT(-fCw4`G`gFXb<6sl+jZ`aV-lfr>o*AM?JJug|eHBPS zXDgN@=qBhi7}DLc_uR`K?{eAFlCvrbK6`2*A-@A6;P0O{NBFNhmx?jP+!}0qg}n)l zdpYoPnIk&glP>ia`u$O~pNt56IdFTl712e1G!WW8i5f#^$6wE^*QAlhuuyHAOUcNM zoO6_8W|PuKZ&E0K ;D%X*(kDS04e;}DM~&3_+y zd)1arLF}Da+d2d&YM^XG1bxcj&({0)A)ObLI;+4WryuSlDuyzSr Q+pS wG z&EbP5tnh5u^XNk}?{dzJyiFBGzqsN1vM*t<`(6)O+8f_#(KZZOysm$-IgRmO(|b-9 bo(*~MdzbU(>0c~n|DywZLIf?|>$m<7-S2P# literal 0 HcmV?d00001 diff --git a/images/addons/swpm-older-posts-protection.png b/images/addons/swpm-older-posts-protection.png new file mode 100644 index 0000000000000000000000000000000000000000..f694467a57ed699a7e46db8a01d2800b85341b6e GIT binary patch literal 5540 zcmaJ_2UJtrwxtV-NKug{0Z~wTuOWocdlMmaNFV`1fP`M8DMUI*M>>d9=^a6kDqWg1 z0Vzt8-r?hY_ulva`0u_m#yM;6J?5ON>~+o<`{1-R6mMRqzmA88cT-tOPUp&YUET4d zgjdfZ0#VK@Lx+_&!0Mu{v2GU5a6B*!Z3zb`qbzLTI&ce^hf_CP3J;GEfYdX<8mOyD zSfNoo7QZn(?kLAAHXfc7$lcMx$_|bNSi)_P4nWqeh7YU&Bn-& mRxjSL5HyysUsfAXqyf>%W3B zP}c%L(9UpxFb^NM6`u$nK!l%%Ur uMpfA4+pG;JGTSo z!Cwk;aEz5R(h-Y9I{ P#$ ztN(W>3iUS{gVllmJKp~&jM4LOg!AgaF=!WOtE-K(e(>9sqXfhmZh=KR>!H#1e^pTn zfySaS2(%*rqALt|hID|T-7tTk)zu}G9WYo62P?R;9FX;j&x1t5B>2H#u&}7Sh^&y1 z5I?`XkQi7@SXPu@o=;p yVPQd7 zSa@e==c>Ht=jV2Ic0)r$hK7dA%gfK6J=@ya%FD}p`SPWrqT=M_ D=6$h=@pEU*FfSUk?rrK7aoF z?%lik`g&DWRR{#)>FK$+xajBS=jG+4uCBhhx%unYui4qz@$vEa`1sDwP8AiE?(Xiv z!NKh8>~G(`1qKE}q0rvmUIYSBTwE+8BU4aNprD}8*x2~t!v|AS(}{@*5D3)X-fm-K zV{L7nk&)5U)8pvq*xcMaJUko|6XWgejm2Wi%gYT63^X(}^7HfGzke?$CpR!KU}0h5 z@9!TR9BgcC91sxD(9mFMX&Dw4=IreJ`t|FPk&*uX{^{vyD=Vw0sHpVx^p1{> ~ znz^}oWo0D{28)c0G&3^`4Gk?VElo*DsjjXrEG+Em>U#C+RZUGzP*Bj@w{IgNBG70w z27?h46ui8=WMgGwz{9)sR#^_L=l*p)_0WvQkT#g+A(1Rw5bL7= u#XJIiSwTEQOk0#SckR<%Y9fBto==}%lx=E6hS-K{-JC4Fd! z?Zc;)JE0X2CC+#)z*A}rf3fiD<0x@zXfMwgbCwL2<#X%Tb#rPMUzoDNJ9c!!2QE%8 zEq1Z4>vp4KxAGX+x+LUnWjT< kTOWT(F0h?0c9V@Ot0vyVt4?!6Bu>F!} zgxCHfW>+aHyshXU{k!|ZWm8XV*rORu5fAyoPA0K3YZfpxgqpGz_1L3$N!i{sZ3K`< zq@b$AlbP0~B4VK?ZdfT|`#Nk))BotHoFFvg1xXn-*)63Y!l7U*Gvc~a^{B}?k{?d^ zL=3bPFpV5hu!b!$;iQs@Wg>}3=Q8@?O#x^Ng7}VxOf?_CSwZz(hRl=k3V@3g ZaPpVA1s#F&njHjzVVi;lvWQB&UQ;o&L2S5;h;a_rtM9nf1p zRAS86A6gZ^feM pAFNp%a_(<-y@bBdcvg-xmL`SlnIjeJ~F&BC{M%ge@nZ}wmh>(5HRtH zYf T7S%3q%$fFxxt?i+_F;@VIwvFBhBV+Fm8Ne99SBO~ei^O*7;g+THS zzI5#eM`$Kt;cKcneHr-dyRRf16!6MV{22q`O(i68rPC0=1ebSFVB|Q>?nIG!&j?i3 z9xmy6c2LS9v~h0=XO;Be2_HRvOvD2+jBH2{WGLi7;^p98&1xa(t$^dhtqLOLZ6_?3 +0C|3EZlN1_T_< zf#kG`#J1NqJDMa1Ky^OLXZ@)mjfy|%C_MJy5qrUV4|X2Y=|ihJ%WUZ_!+~`;$-AcU zzQx-n *G7v+bM_-(RUk zpL*4=$mk3cAs$xo&WZXI63DYpb}=!#Xi5<~=TUnWR|!5&xiq>r{W+z3qAOFbiF|t( zR&ZuUb$Pc;y~A-GcP1I9 &NtO_X^-U_vhdar3%1tJSyZT80cY;;>K+ z7zD_FmI5K8F)Y{j-|otsX+_z&`pqN88IT?nzJ9>j2Y?F!SD#Lks0@3*{`Bde=)A?q zLA_O$sL5Eu=+aWW(IZz;ul{mHnG%UJJDKn3P;iye$*9g=6>l|&_}=vk7JyGGZXXBu z?g_;=o!*TXFC1>5FsApgJx3uGo?FycTx>t|d)Q9{U=| 6R836PG`VI;s&Uu-`U4^xJCm`jjW~D+a|c~;IdoRT9^dd0q2@_y6pF)o1?O( zj_Gs8N85c{1g5(U`acM%9a0mc2P8eVlU33zLzWn|vre~}Sed^&MNm*U6`)^o*7aP{ z0CPOj#=+%*%pb`$qM3W$9%~AkW?thS@?d~*RNspk !JB15PI%(Y`sNe zRoQRO9|_-ExfoqgDTznR94f-dZpQdqV<8NTGUwye+%_##D8Yqu*T-36)t?xAc @moKFzzP-mKm |L0wvg(F*D6@hw2QnUJl(b*^t!y8%oDQl zSTg-~?s>_|1N7m}zT8H{=L;jGJ4z0u uxJSitRV$ys9AC$ev6wUdOHxzsS>Ihx+3`}fN+X|iU~S%b3V>P* zK)4^=SRXc?CMarXcIQ`~qvYKh3gz zzn}2}_+)G;zJJvC1L-Z({z;w~yYitXfVO{2j^tGWG4Z>VzH@!272m4V9~qJ6z2GLY zc!S7<68Yk6udeWx(4&FRA=AMRuV;c^SBlGI@rkd|Xv%e;)W|dh^>q_AN-tW=4K%Rg zH*6V@(_wQY?I6ugY{W7o1}m{d#K=h<=}3_mkwikL1|aK5l^U~-+NE++J~Jy!%(X(E z* *<&&V~lW#uFov?;kGkTCn?kXKD4&*8bZQ@p(#v z9%LzncnoytNz#deRuoNx;_^uK9^&3nqs8=BDLc#kWn>uWIg}}JGR?G^fs3x0c}?6O zr~Fg18Fo@x7do<71vjqMDg_hbzEKRk;OJ|l%vH;3>Yz&tyo>y_DIt#5r*Q2n(?i|r zFctT5awf;QIX_1fRuSzpfCE0?43?BE)1 JCdt! zrqDHh^urndu_gJM^!N30&ZukNDvQ7 @oRffjptVVnM2+4rJX6J27m_%d@9s zp1NOODh8_InKC_&OLx<{T~~G5iUElH0-IVno{BPlyDM3hfZXN@Ip<%?t8D0I*@i&p zR4l+HnD`^1!N?Pn#vd8`B>j!()DHD1&MpE-x`sg%w@n*eXj2sV7{?e$(kT6@(Z>_* zD)&JrT6_j48X}Juj2JkoHJekPzw6Anak3av5j~`2Sl9xHd-kQsq(9hCe+*cro?Fgq zGksqCY`cSFoy^&&bB3TB^0o{`5GZwc3uaGv|5rG-^?BByYg?;YM>yBan%M3@SC*`8 zAOj=BsY^{KZBQ_Le8#ZsE3&Qgwk=lRB!B^c2uzgb5(-JF*zT70yi %$~Gf(?s- z;(8hY^_ekuITULgzD$^W&xg#Z?SbYuUZh0@ISpc17c#&JJGaa406FB3a%(&fWj3Wa zKL<0V&IpC-*9w$>mm{583%XYKeYwD)affNO)~@o}vG%W@$7eA#Ei)PVyn8#?5%#D| zH@CKS{m|IJH@KUvJ;|mB<)!a-K)0FciNH5YrYMkGXUn`wCiwjmt)(U)qnhEW7flft zsrCB7thciJlQr7*pCr0zxsUMAI%eKUMfH-UdcN09?Jj|&)K1x4; C}pQ8Fi^lK}(J@^W3s<*?yIHRz{X zoVt==21%{|J7U7RMhg|TAHDef#1dYFi~BSm3LNKt6=YPI^v`Z+nh^u+uA8#lBi+wt zj;#(5$E$-oFduxqW^Teb#$pT)u4~nOK-LjlCmB)ep+z8_Trc3ak02)UM-AydNQ$71 z=^dYzU{fMgFswOSXQHGg`j~c~T;q*2lGAJIWw6%Ugu1ZkB>x&hyX$6ML>K(PP8DA` zx+SOY%Ux@te2oI9=tbOOf?2qmg2`+W8R+OmPtS)? 2O?$9uxV$w3=F)maeQ2p0vbUS zr>Iyr?jKZ2t7lKlRAB7-G=-V7P7^k3J=vXX*>6A3HU59+<*U;(?A3u8J_+8P=E9=w TPq#k*{%@)*uOU|=YaZ}Fv1~8v literal 0 HcmV?d00001 diff --git a/images/addons/wp-user-import.png b/images/addons/wp-user-import.png new file mode 100644 index 0000000000000000000000000000000000000000..9a78550d2ad376f8c3ae6e352a3b7be67573331e GIT binary patch literal 3393 zcmaJ^dpy%^8($8YLrke0vn`~s&6(v8o5Rx3D8~+Clf^c+u@RDTOf@;gQ+g^ol1j;W z&Yn3`TFKc{gqlSXa_pT>&-?j2Z}0DO|L)W0`~6 zal(-+{Ih_f!oE<%R7W@{G3-1UZqxt<6Hg-oYzS08A{ax)A0gt1ctS*I3(*1q5CxOm zJsF F9SH8s@*8zA%%aD)L|-#|~_6lrLHL?FOFKB&+d zjSzsuq3wUz60R(vfec0n5)NlFnJ}gyj7mEK*EcseU)3-$&=Vr`=n)hKo~1{jZ~du& zCer {}iUXM}!dJI3k@IM)Ma2E@11bsSqTJM#M9yG @h4)?mQW!dMj{cAXahq#G}_M0*xb%QU*FEi*3i(* z-o)O}#6;iJ*51VKCl^ii4KV0+Qa#s_993r%gCeldZM1nnyN(O&BFp~6p zUrc_h_k&CLy)UM}<-&!|z*ociUt#{aCCr`G>93>}E`Fswks{1>nlPz%+;H3q0IZ31 z-Q#XYxYW_z-7T12h>JV1dpAanRysU9TwGinb@$cqhuOTmYr(-mFNUWY8XBgiruyE0 zE+{BKo~)0GI+mMz35`Z|c6O$g_RY`FXJn*LOiawoFP%PpI;V7GX=#Z-@Yji~nwgoQ z(`l72#s>!n-P~|I9 g(i{cUdDYjjHEse64-o;- Dl0?<&~9?8@!v`;pRSp^O+Xw#D>MCm5!mw`pyXtkNqw#&dbZozP>(q{Ne2E3%hn% zCnhGu#vUIV8)GuV-+i3x@9(2hsRea{tq~;xfdC%GiM{vA!NEQ-@Cc`CobzO=Vc=cU zz&M+I?rP&}zF^t@RBP>%&uXD%78X0de*LQ1X`u}O0Cg~E8+TT(p#Q_6UNcodi2AN8 zvSRXk6@hW*T&M82E}p&UvaLP$U3XOWbfW|)vl9Dx;`2oB%DBS|sc1(~nw7w+WhYj8 zo@a!OA@S;$TyW9}hk^fUNjumD57XCU=kg~LVgi%yS~}2^^QRxHZ$58!y&!qdGrqck zmEl4Uba7G_bJ}fBWaF#$(HkS4rtCLaonfunN*9lA=qe~|v`O5FmPq|WZCoMK%eV5S zI02Dqg!85Ckvu*?xqVWL+TSTCGMiy@E?V}YtO`|h#IK##8_rDC)^j10(5k;WOX80D z265GEq}x!M+k$-Aw2Fu_LRRWKiyb4+H_^1W%VqWt)1jJ*`H3}7kDE2d_D*fYLIUz0 z?Q&XD`}4}x+nH_E7cSc?RX#e!QY{wIJVf)-H#>E#ma4{$0o?I$;u>hW+$-y=6-z|h zjNW5U%RV+b<%^Qacyn3mVM!79kEJ-*7T7vSDDV_g% 3-~H*qAYjhPje!b; z@)wAt6XWI+^-n81K?afqP($BAB|#j69-*EN;@(JLi#tZ_pLg(qNJpf8ygRw&t%PFW z3lOga2Je_a-AK+j0E&B7(av@DaUm`2Qbi3wgZUXqq)v1zW?nSZ)s3q{Qo}SC cFHO`W2HO~6j(;dr)GlmK}`7hHehTz_# z^Rm;9DtQvXhG5Phw#2t4J@r$+co$py07lgIz+5GrK3W|h(~~JJ(G=%k$s08tuUWkD zBE+??xiJHQjT-eCq_D;=$WG^lKdTgfo=iO$C~01GFkbGQFJ3w4Y3&&!j)vuQTjO y@8gTJ*YV+9|A5gMin4Jk#Co=1S_T?YWnSV*E%( 7} z9luw}FUE=>HY@*9=3iCe_zR3N+VlE!DD?y*cbPj>y%1bTIh7SM)hs z-eK4S@maU1y*t0$9|^QjMQ^U#Bd4p1u6DO93X{`mR=v0ZXuWog{JawPW%!8Z_KlF^ za3z&>IhV`Qm_12rZ3OE)xzk(39#D@;DBFM*756`Ewou-cjglY)0DRA{q&w(rGF%ht zYwcgOY`(!VE2By`2z;~T6 #brrans_Cc9C&}t*eHa-|s zB#?F;VW&-=u^IA5o(xvK0!dh}{0UhE=PnSX)@@&S&K@C0+hm2e2PT=iTm#!X-Q$2I z+S@eN4?Ws?k6ZidG6(!;w$0Y{e&?W&)9oA%xUw@|n&$W@w$MAYF36`Mwkp8FNgJp{ zQvrd7eQfewHnT_gQZh;!LH^G2?6=go5o~1a{kF8ndqLI>m-6nsf;2=K9hLNwM`bk^ zq@k4FUcz4j0#%M3>Hw{Ou(pxyLhRO)vITC|Oo5#KOU6+5wl}1}M>DA}LLt*#Zx-9A zv{l59n60y37GG1%3!Vb0r=7hml8j9ce{Wf=rOj5b&1$0<) tyegDi>k3_&4q^>9-m3E-6zc< zI?ab;O8XL(2Q75gYr^W2Hf}SOGOlrcDRqJN B~%||5mRpY+80J>B7(@*8(WwpD>UW;B LQmAAx&c`g zxsHw3m=0LGx6YCt!<9i+I=pwzX3N+zMDQ>k*o%7B8$Z!=%nI$tY EfF`-baKH}Bb%xK@U%5p}`;N`i&nOu3aE}swl^2bUEOCsP$wSKSJ f@BJIzaS<7S@c}~xoiX>w)kh7+&KX@~dpPbtC!F>S literal 0 HcmV?d00001 diff --git a/images/index.html b/images/index.html new file mode 100644 index 0000000..e69de29 diff --git a/images/join-now-button-image.gif b/images/join-now-button-image.gif new file mode 100644 index 0000000000000000000000000000000000000000..6ef5bb87255e6a3d02c9fe33d0d6beb75a3c165a GIT binary patch literal 8204 zcmWla`#;l< oYSm zjov;1(J3F}<2SapwzjuppJ%;yzOyo%Z+7XfpvknXo7-D>ck%b?-;IrxziZ{?<;#Em z7S%RYe(Y-<9_=3E2`Y4dcC{ZdbrgK@5&U@duj_7aZ|}c<|B5?%US_}jHZvC&7bj>l zi3W3=GfkmnSAB1YPGwre yx4h!v;Zgs2U~O}AXCy_?b75iSch#rQ zHN9WzJ`a@l^j32}Fa2Jv?duoRn+OJ7gX2>_lt*=b9qa1q;?+ b2mQUNf;1_Y2N8m`+2)?ln~Pr<0MOFX@^k6eKaOL6 zzTMX3yMLc!jnCf^G@cdYDB8OOeygn7*w~n MfzqI;ic6PS&%g{XU z^YZL0uQ6D#xxBr-JwHGHXMKJC=ait{|Pu{WOE7$J@ zTiLk%;W(9dbH9A~l9ohUUtJvd)VyAE=2QRhTb+Z78*t$Nqxt{%|4jgB0RTbEo3R+2 zP_+1d`wmuSb0nyAHqET`O>3--uIp$=X;%9af=P^~nsE!wmx>j&=`73n@Pc@=E6uz- zw 7{;aqLS?B~2osh7Fk=W->p2aZaKzN;dMQU^rq5-GG_ z-`SzKJy!KK%Rf4vYFz(V|L*rV#>3}YpZVc?U+Jg1vy5HO|C;7C#|rkGZw%OgTSaLq zp1M8uD7@X&>Ac!piiv1=k?CFM#`gjNfb_A~^#)5rB*W_?xhkx*z25^6(Oy{17kYYt zB;pw<6e|DU%y=XrLTw^SIoV|*S}iwkB1SXWEOghv a8MN~N}`Pp3T`xI3NhGnGI6I^b_K6RX893zMW0CL_gCqn4q^(IMt!ytaFO*nvCP z%V>44G?V7swEsrbQ6B>)X7jR>UFY)iatr1PimK-33bS4deuNzIqF?MVs@moU%iP!o z&h3Ni9|IMF>7P4LC%=%@A?cG@>IENf^|n=9qm|`D?h#0^s*&Wb14X TBZoID=iz6up>y_b;?tx!4iAU ;*zS612ELws4r;s`I9N~wZd&W(hG>#sYNPHrh zHH2fUY5TBx8Xdz-%)=3wg|LoM+Fr{lj2iEuoV*(G03M_N!;>RVpt1LfyX)Wkd@ZzW zW!QyT{C8x(!T7;$*zlV$RQ+ja%V808sj2Mq5rJyRf$`A>JecT1%nyM}fFa^NdP_Aw z#0h+=dQMmmWJtau*i`+cQm>`Dkmc71qCB )vmR*P%-G?zu@uS zp2Rv*`(YksazCp@Q=?@;SAj_v-#)x&Rc?|Lt-a)@J{p!l`tj_Ng0GyA`zMlmk`g3l zlI$*WYA$EA dCj~qR4)huKCee8R-2Voj^I^kq+Z>#`QzU(eBc;7= z1Z31idq*pZoxKGGPw)^R`weo7oF?aP2JxU#LvwjFl+ilpFc>2D`3-=0mDzqQp#-11 zo=HHNcNojA&}AQno`|7h!TJxlNwku9%JeD6XbvtO;E0eYv8dao5>;Gfw5+G8#j7&k zuXN?3^?V4JW{lsanus`?f3S7_5hj$9gJ95_?%FaWd|PhHl_lcUWY`ky?&LqNo!!pI ze!t|#1obdt9&me%0Gzf?x7Hsckxux~UPW-)?T@8IveKIWuuYiIa2b_@})-3{#~e zr62Df7EL$TWS;l{3OHsy!69pT5CwixymP>fYh)K2xqqVV;nxTc1S~e(2ZL~Q9nXBF zd)?`9S8syfYL@?9U=LI=_tA|c%$DWdqpdzg_b>8A5MdqNOV*!6^!8@#@3{+a=*%JU zGa*ONuH?tjH0FVBiBEKSm?q FZQ9Qc zl)bu=mw|>G4sJlao^g=W_(J>XvxD!T!OsN8!^;R4LofNTQ~%Z9bwF9z)Fg9Yd)B8^ zq07 LSnO%E5&9B!0y&(L2|wr-EXRF=@^^33^`qJLc!fNP@GWw+{xC97`?r!6 zU+jLz;_HZD KC5Xt4 zg-ofTV#EPucC@=05BZH-a_1%q_bK8C()jr)9Tp+hpkuH2OoNe7nZ@_bk+sJ4p0`13 zAFgjX!f=2v|Inrd5BhaYek7_%+So}* 1kXoz#R<@sdQ$RR%>kkjlrLwRFLKrCG>b?bvgo9wREf+1qRAx#!Dx z$N`~H$(Jrh(tp{r4_bJDIl40MRI!lNFCzqQh9n*^9&2B-* $`}kuce`%D9(XAOKa|9aRj%Lc|6&EzF95?0M~^9V zo<@~FO^2U`%kA#;fHKobacZ|FgdY$&;(GxcF^xC2{;|4ot>=C~IU5%2Li5Dmz KHl{p`7L|jr368NeRn)TTSXWS{ArJd(|JWm9+;)W#r>d-|U@ZV?UDhRD z=j59t8yRg%2ZMyIT%eQsdm?%~mzGVwI893N-nE~4z5M2W{SV_Os2=Z+56-y+$d;HH zi!YR7lgk4ng^5R% yWgNg z#@1kLJ`JULd|ZLndJH&CzsbHKF4(RcTU+vO&=?<=q(ilfbmC3zoAD0RI`PAeKw-H& z-imAlCIRSkKL1t=U5v#)^1@22$5;37i5PTxlAsgz8p=X7OdS>3@?nkrVcu_@dOIGY z%7XlT{ppZWtrc|0z;N}a{)vUhOJTcG|E+d%>&4~A5AN-7!fg123l!x?*H%sfQh_z$ zD$m&7zv~1$K^dm7xl`cDu&n65@RQpd2HavaM1ci7K1&igPlou>MJxy?Qwq|7OB0@< z)xYrfry rm zRI`INnUB}#$gq8&HVLzb4YhL#+V;kYR3jt_@L)c4quN`z8gUVXTF!*s 87Vx)h>Gj* zYC0oy4hYlm2J&dwj2bU%E<}fl?1+RK+Jv#Z5h>CzBU*qmHQIv_R{KL`v=lyZCMXbv zJoc2P$UqqrgBGnL0#P^wSq xA9{!!G^i#r$5HqV8b&i> zebuC2&G@=dVKiDm>Q50?HTK> b2)Mur)hQ;dH~bVY4e`oTU?n15e?ruDMb;CP?gFR^QA1}kl)UTt z1$FwnL0E{7AC4Q^N((6DB9OyT*JpjL$#^R|o+S;xh 20XRdI+z~{fV!0$>#0a>{#aMvYgxROM z7#x?5gnME-S75Kyp9)(;=6W%Ac`7e>+5`qXCtu+z2?(2h7QXAb2N@^4;hRQEeY# zCkoj|C*IHZQlr9}^Sw&A2 Hi>M%^yq$R- zC!qQzfw)0(O5|`XTro+5k7Y67XQ@~|`v_DA2&X7F{I9>u*nXFhb{Fx>3B_b#c5E3> z)(&dWk>LQ5b_s?*6aKK~SyYz3GZ!l5mnnS>>V-i^648B>M=m6x4r_!856;H;FY%Cl zM-w%${(t%CG-|SpYo_dGT7epH02?z+L;Blhb*!O}u+pze!}q_U`wt_gfY*IQbR>ZO zPDSXtX3icx*`^C-(G;%(xNCkQ$4x-%P0UFOV+VsX_^ki~aE}SO|2E*JL?svsFuHl# zn1DEFjJUra> N*{ zvmhu&h!%rv=DpxV=0()z#c$@NxC%du$xJ>Qs$H9(;g|d?reK;8nm33D_X~bR1+4h! z-87-G&4L0#Va5Jk8^MJ` (8`6-*l2eGh9MY&s=DT>FiM!ydF}aL?%aG6*_=Ny0b2Xs z8XIJvn)m%M6~kP6kX7rI#74#e=-nXFpIfFi&s3(c=AyxMC&Um9A8#zC&4)7CrD8Z- zISa0D4z{eoTs2A~sHG-lB3Jnm`%=N)EXW0ZH4RsDfL>`g&m_(l^Ny95@`PU{=V{qM zE4g`z7NJm3 _oI)+%^Vo_hsKp|L#YfY%9 ksvaOpLxVzIDW42ncKUD&W4fshBt1H+^J~J zG<;11FrvISEWp3shfiXot;ncl9=1Xbps`S)q&gGQ`;pYT>#3lsj);pV_>_d{nXhlI zgBXL*tGv43TwwPCr@1iczJ*v8AQ2eKj1lLE*}wm8Q8!`HIAzcnG>2Vg)P%BY_y&na z0l}{gvc-hh&+J+>_66*0z2E+tm2ntdj6-7 2kL+yZkNxD{^De3J;TH4qhBL7fCq z&H-(n40JUC(f2`VFH86-1> 9-s=p;sJ3W zE>u9kOzsENh%L`PRGisvNvwZ=F0KViMkNti<9I-JA=GA}%!XG0v*;QtfKJZ0QtUb6 z H1x8*;S9naA227S`dZ24}EJi>w94x4We4Tua`C<_Y^&Rk%A zrw7xhkZCfil!_>1cRVlhG9q-dExUIt-W78Z=7pVwRD|Fw0!M1y&BwzmYQyR(KWrBm z5xOcgFk|xFg_b!|o>k8P46vI^XP`ydZH_jDceyw@0vEa)Wxp9NCTh5gf|PLRm0U#6 zxi|k_D0ZTwRam`mWs%?hcEkK3!f`ddmaQ}Z9o3DFq@qhfq%{#;C;;J7=Xzzn<@XWr z{bbZ5F7_1xlf=Rn64BNmvXtK!N5IT=e_YgL1 (JZ>=*veT;{ zFl3iHWM_a@<_;N=Mw{-9!nTH=-5b7g@7u41-XXzaS?+cL3WRCl&=vlWFNUMJJ)guc zU{3N;uJo_P=eQTiMU589R~g)u#gUUd)Y=XxmOj=TnDAn;xTCvUGG?@jIrP>KV#VfJ z(=mPNV{peB9m*Kaei&i(Nm6Ur@9bFoBC~+kt+>Q2Bk)pKV}zrk+Kdqn;XC|*h$2O! zA#P7L7Z=0&9zwyakr0jXpja`)_{5}%2Xe%rrhtl?EM#`1qaE11AV0|Y6CKlJysbRk zRBQ5d<2Zx|?@b>+v(#^ARfl6h>?6nH7kTA&6DjncCM6R>^WY&O#+45}L!I_6K?hP^ z>8||POH KXL45Sk guM&E zES9ha7w1Ws@%F$lc__{p*!y&*{NHSj*04XRu9Pwrwi}n6F!%BVE6g4@jvpxxnzJ)N zb{lqlUh1E0tjzbA%uH{!bewPk7h@dfnLXftgko1VDx_rkauTCQYhgVf-4O(Tzh600 z0jf@i2z}?{-$GQ_t&WTEimwn89{LWcu8W58pw6msm+cA{9avN0{($f&zAP7N|8Y6Z zs__67QsEDPg2#xw*AP%-Rz7g~0C~cQx*~l4*TN3?n2mB}AkUCc52 OqYkwr!>p|S| s`SfT1^%@fp zaR6MBUqqjEtXTk-#eS8U&><#VXxL)$FJiA1Z3e7=1XU+*}AW!a1oEd2{qQb(0zEq6R`8jY8`Er4&crU zDENmp!kNFo-=u{l3?}_Oj*&4KqYKOVg)RUmq uBcU{N!|`+Cdk!UQpS{9&=@Hn$sKOC@380 z&bEE(TygU0mpnz2Gu#NYUG}5~>Grd8+2&6Z-drSHPsE4JSgPnoC#mI_V &or!B0Y@;UU~T)dB5}fLE9VQY#O&TQJ6rv-6_y8@yzX0 zY@R-)GOSx2!Q~tcRbsn0I1_)U-kS<`8VCrKv;JW}FT9vb{QQ%Cy0I B@6?4hi#qJs4#H38nRMgasZ4~kPrsyTkOH-OsyreN2 zk*uGAwODzfrTdp;LdvU$VCwHz>h0VNb{bbQPjbr&l +|1TFONE9bO6U*! z_Fq&-_DZZDw@M}%>_~k{_@5tgBVSIIr8SC`p!}UXbWNY2E;Y8(v~(hh4ESd~|H; z+Duc%8R3+NeK3Vx(5LN=yEO;wANA|STu5zT_ghQ$Kt2ePY9}Mjtu``W*k<#>E1@#S z#n0)fSZ2`<-%)>)froBDo)|orO+YEF^mt#u6PK++6`=}yZ3F)}tPbhCad8?C&oVQG zh5Tvwa6Lvn!e=68$HD7+;=wlf#3Afd&G)!V&r&Aij*M9o@!ixgr- 05kpHD_Bh6N &A<~ z|3=)Zcm5lFFc|1P=D)VHHlpjI_~iINijmKRiKB@;Oo-)->}VBb%D{4M8aJjNwJXjR zomLNv(Hl^tydE&$XZQd*U>+J{oY3W}FZ7W9IB0XeS`f7Td+_n&e_LyU$H2gSq=}D- zLK(r(lRyxOwUHo3y+An;_9CK)yuB+#1WHUhWUCoid$v~)0tsUUOer~q8SB$T``|$* zC{8$sbz_+}a)fTS6aJo$iTo5fQd=(sDY4E*L6u<04w%GQk|{;V3EOg$bS%onRC$0E znyld@S?r=+vOqxDP2P}hSU*LKxDa<2HcP7~% zBkCZJ=xJeFei*C2V*^%>psz1cMVh8#jt2Yax*>#wfu5{!dqAuPaBd(D$61kZ!W}Y|Bp 7_Dl~3EH*T1vx7%uvr516`?u@!a)i>*P+Q`i|eOCyP>8Z-=TB6D$lpYQ;M zi%`8Rv%;E2Z(g!`=&YBX-qDW!kA0pVaXDK_Mcv$&4naU+uzlz9MfA8(q~%jwl#KCN z9p1&1E(uXc&a^O@0`;AQWyz|zx?_#7N7Eos1B&aV^v%n8r>_)T%5l9??02Qe`AXrr zR>*@0R^0A~{X*AUNh0k;r0y-A&rO7z?IG$w#T26eGz-TSDWWb)yvg&wf|Yk~juj=5 zSfMKJPI!Nt7S*ezvA5lC?%uNL5dKeqI-bHgm{NMV>zWj_yThn4fCh8E^|vf_u}Jhp z?3306lC&1@lpBw2Jv4AjL1Hfydm0Gsn7UeB_w(NUz%9G>CJNk$$dQU-G0IvGSHWD) zo+vU3!LdoTpL0AO75h6ZI9u0!{psP|u;svyu&y6G6@2ij8~zQM 27(+q3p 4GE@AtA?{@>}?{-A%%^Q )?MJsUps+{6lK(#}4mSze*R`%Wslh z$s@h&32@e9+V;3PKS=$y?X2H!`|&}pZu(EBc+a0V5!nn`Y<2f-eKH4|Zo?6q 6O{pqu^lwZXc^$^ru;tVYq>z`b;Sp6=NNNH j|S z_rJ>Gz 0v@6o#LZTq$u;g{X~)h}MNPn5uz*xN;|07cTq>t_A%K>duV|!G%Ad3WA9ryFwvR z0;$;EOIF%QEH)wyK_zXtGsn(wF>(dEn(ehdJ+IU>H$V#__yT(bKbMaIAm+!mWh` zVGJqupxJEl_I1U{*LxukU$j(Ybh)-)GIKnKqw{?eIeBw*MwI{{KX;5F(E8eCYcTLZ z$6&Xfo=S6eHp>tQqYiR^ab2@BN5*VtU%Eej5?E{LY;>4}ln8+_hPyX%Y<^r~qyCN) zrEBCaUZvOTu^-Qtw@F!}WHK3AuO5;9^Ub;MRTXQMTCFxBRqM~<*b>CXpf-l=DUM#c zLVKm^dv`NOsZ;_1^q*mjMZ6mV6B|_0OdUSNulCxmPy5~)i=s3d4gRNCEFuE2TXgTq zC%t~IORZYvU;taccQ`*^;6kB*h~iKLAwj`6sytoV_OC#>{DywNkFk~mLBNOibqe`B z!_-LHyD=8-JYLZ#3^j@(U0q#E>O^6rVdNBQ7)E|3ai4Knx&5H3Ob!BEPFyF*YVw^Y Z=Pz#P{3!of ^X_+~x3MG{V zsS2qTnQ06R6}L+JPV*iy;Ba}rPDzzVzF9L}RP|EXhNtXLBJAE;^DGvW%DDc%di!F9 zg3Ip?AL8k3W2?W! s;_N^Ikwlh?)h?OSyCTUk>lF)(nx%mlh9!q>+tIX_n~F(p4KRj(qq0O(W( z1{?c|g2d$P)DnfH)bz|eTc!8A_bVx6rr0WloBA5~7C5J7WO`H;r3P2|g(O#HCtIc{ z+1qj1R9FEG$W1LtRH(?!$t$+1uvG$^YXxM3g!Ppaz)DK8ZIvQ?0~DO|i&7O#^i1>& zbX_Yl%Z!xlxD;%PQqrt~T-=~W0C~1b86_nJR{Hwo<>h+i#(Mch>H3D2mX`VkM*2oZ zx =P7{9OiaozEwNQn0?I2u4F`oj-1NL+ zXdnQCLmwogZ>VRWkF2~TGYyAwkPJdONV8QyW<_dFgiB^_YF;tW3ARdxW?-`rl98B_ zAakrda}(23!!uJ#GJu-EdJw`MshQ~+Kwkh2(+35XJ~Xs|fv*n_eNYh49*kN-QW%d0 z*JyB&6apkE9!*`N!9`LCkfeAtbq%fH;zG3FQuBa~wsK(Wj6uWTBCuu0Qtadz!T|&| z94Q?P49wF!T^vI!PS;M3^@&bpXuJP@&gIKx+16gC{ZBW1RM*X%vo`0NhVTr|!ve_? zdV1bF9yEFMgm=um`E1jUqZV`3if3BfH97ZUS= OJv8DwzAzP$hP z<#|in`Qpjbf2sK2+x`8WqGjL{yB!4&PaZsYFt0f-{`{sKujQ9(>z;jiso%xLwTVH3 zL4-k(|C=I9!x6W~AJx~z?6e7pjorL_%a$u=R)$P_`0(MinJ+uv9Q@WN$iT%Y!eD80 zqx~5}K-jdU=aVL`h@GUu(!^L{v)SOK(ThBjn>P*}UpLbyjg#Sfe9_IEZH`AjKWJ5* zV-eKublvX8oZSk3XT|@T7(G~5d!&r}Kl9>?88!?7la{+p-ptT%)pc-n=!J|>k7Mt| zgBxy6TFOwpZ~y;~%CWKXb9Yy4Y-N%Ojtl#@b%IF2kCj3ziXs)}F)T9Fxf6T*O3|5% zzpLVJXBn*6`|+{vYwq^AntZ(tW(6OvoIJPW<{ej2Rp)ni_U&D~ef|4oy|I}V--9em z(rlY%3+>w*E-4l7H}Pt 6;EpFdBb6bY5MU{1ST>g(pwon!Iu2&~#&ipgI zx~gttc*NVczwbUwh^eX HQ#xPb#lcJ9cLJj}*hD z|9)%dzLoxVBBgEX?cBcA;rsvWxR#b|nq?{`F21d@Hm |c?XPTau`s}Owxwu$XZ#uVFH*4K~`L0C{Dm*o>11-MHuK4Y1 zUKUyMM0Vpd$z$82wkIq5IK^0ATF8*vb>`Uw9q%3TT#A|;7qYf8FuZ@i_{QxoC7V_S zIDa;CF3EA4(mKEXvBE0Db5{b6{(pX8wY6~Xonm%ApV-}XwZ)m4n)Xw@4kj2l2(aX| zIGL=~4|BU_>hf>#oB20cx*mOwE4z40DqE!O+@9N4t5$AWq; O#rcjR(H@ zsI5J*^G>{K*z5A@SFfMDg&F5@AD_JPW#nS@mCt^7J+JVOT=n_z^ nXQCOuzpA=f8i`?|*vz>u>AX*ABZMpMUe6q2v6mPrsPj z&oOtLJNNn Y2I<>|XH|HivjcRp}* zp1<+wSJK|cSKj|*Xg*VP;2C?zIfmx5$6tTr>bY>{{SSr-mv|>!I`sT&*_qe+EAQ-o z^L^2c_kJ7h9eeTR>({SSFTeTl`>(~Cdpr{^@lU+;@83U$wsQ>a=YIV7!O(Q(%a9zcA86cimP^pC1a-gIi7%cXQ^PG40%?Lgh)Ow$rKOMR5qEA z%$##E8#f(jYEe!-qj8{c!K7IXjtwyp4Q`G-LMd!KLBS1f?84F;Oe_}^3eIL>;t;Ty LtR^zsfx#L8GeaSC literal 0 HcmV?d00001 diff --git a/images/prev.gif b/images/prev.gif new file mode 100644 index 0000000000000000000000000000000000000000..7ec2d6c9c6ae8279bc10ae89c2cbae1a658429ff GIT binary patch literal 599 zcmZ?wbhEHb nXQCOuzpA=f8i`?|*vz>u>AX*ABZMpMUe6q2v6mPrsPj z&oOtLJNNn Y2I<>|XH|HivjcRp}* zp1<+wSJK|cSKj|*Xg*VP;2C?zIfmx5$6tTr>bY>{{SSr-mv|>!I`sT&*_qe+EAQ-o z^L^2c_kJ7h9eeTR>({SSFTeTl`>(~Cdpr{^@lU+;@83U$wsQ>a=YIV7!O(Q(%ak< z&77iY$d$n@?%>EcJ&WCfD=pPsNQ^ryJUu5fM20QZ)FVaGi!U;PgH=9*Cs@ikK7c1X zTRoG}GDF)nCE6wJG_NNEqd%XPm4KUG;Au8dhIB=?G&3J19sbm`P)$x|6$3vFeRH{3 z344t%J3 e~m}__zlKyZGaQvu-{Y@lazgmrHmw-o-7{uM4jU04z`f z#y-&A+zjFB<0bF1M 04CHIuYkq}_>lZv!Nj?X?Wyub==tMa0)6~3 zK0cTKilUW=PoPhLhmS8*&qft$NAPy@2@d#!+T0vr>>Uv3;_ZqzMrpx7et81H4WVS9 zrg~P*Kph2F(oj^?SJ6c&!gcl4&MKeQ(A9t|8T`eI@^K}3;k^U@;&uBsuf{*}?iGTU zFQ^%X_a_A5-3 wl#AuPLy0_J)5K zE%@Q@vd4RaweAlVwS~UX2LO;bYK%II37J@aK%hOqlR7qE2l~w4ix#I)n2a(cj<5=# z@JCShFIktZlvYr|*XZbXz2|She|9)qV)dvz1|#$$o(Gz5#NXn~&%BT2;K76Uj5{?a z-~L*Cn&Naqb#gMGKQ!mluU})^l>z<->pn%~Y&QN5)(O|ySzK6Ld^~Ai>zl_Ube)6l zOBbgrQl+Vcyo1z1Y9D3yyj|sH(>3j0)gje4)%5C qKo-&uXG)6=gNoficW;F*W3sYrVNm s(qu6$H4Ek z2ElfvXY~Gh+<_J@j_B_(LAB?7n-(UHtk!fel7DPHJAchbk1c=+vM;xb{)uv)zPJES z=5adYWZ2;Tih-fOf=7|vt@sD3w0E!Vz?2+#D$8(|(b@i9hXW9AdNMft1prp&fz2#h zqDwQ;BBQ+#9_ROch-PCq*?HzS@~Aa$%#FdMn!81 DBb&huh2`hprv!_@!FD zI`@v8la8717C#>S#W5FHq`zJm5Lj18pc?9O+LvxKzd+{NW7`)tu> Mv zVmw2eu9aR|_uaP}U?G I>~qPlk AOb$LimGK=cg7`)!m6_Lh{W#Isu-9;S!gKI1e^8;Lyx5;3J{ z(oB9PrQfE7 V>w~VdiI3d7$`pcH5B3>4+U+4ZXIR za)=Q1;J5$O<&x!Nppc>yGqpn`W0Y`)p+oWdW`B!Os_5W#RB1K1cGXZA+n={@%Qjsm zbiu|} iWl>`1Jc&i(}&TH zBiIXt&Df)*B=PAgS{-kA4VZ#h6|S`T((WZ@)44h;pLJxOSG0~NA-1z0n>hs5)catM zQeED2tF#f3`_iWtl3;^ntyayRC6D+R`T$FbMpTO|#-&inVouwmnj=>C7j5Le^%i%v zwzcWe@9v*inN $$G9qpSJ2$Vl^BE{*gJZ&RWcaQ?7st7g5J z;9!31>-&6Q#gp?P=7v`$H;i3fph5ympUX_-Ry0fcnvZKpY(z OspV6=kBJR`7{0#&H8RZNp;7YgV@D+a5Tg^kb?v_PDwnKU-?-eA%=QVZxLD z5ld!g%T1)3!_q=Stj%>YQp01yMD9)HOHJp }~pj(y 42NS8=uhd3T4>pEfF*z4jhA$;iUN zt78*vCUq&+S6uR#J+6^fE{EP2+ycwPq~Jsn9p-}` bK`(x9O zZZ*MfOS?!ZQ3Sr8pRc?<)`gP%J^}w83+Dn|Y<&o)410$CoZ{BH%()gaS^Z`sgTX{! zQj3t+m+OAe$aN%TeS6hJB!`Q;3ubyq=Th`w@rS~q#-(#3Lmt1Vi{)vG*^%TIK;B7* zw6kNRYbn`qPJuSdnGI=Ed|r~1q3yVU a5Hx9kQlzYc|B7NfURS#d`YC1lbVwZNU7}1%r;NQxDnj6@>&ffo_G1W|m2vs9`4( zAzGTHPP2XOu0YaBXXtotR^08XOAe(um8~M8T7JV*9?}B45p>R(qVQabD`u9a5_qRD z9-%7SIDytPn{qn9>n({;Gsl;3FKBSvetv%{g^Ub%?j{r3NHcIP+WPYLV|(fG<)>d~ z&oA`_9cYny&Tskg(v5qv?8 }mHD+E5h55cQ&=fsmA~~lg#CHf zQd8~as6s-+j+0M7#6b7-^m*QxW6@+yKF*_H&FEwh2uq$}A3C 15cPFL|-!h z$@I`z5+vEfk)i8db$4tSsh@Y;heQxJA1(TYIu$(nq||%&-3G7euv$jP?-Y{^otf PAzIt)1|k}#-c@_SvM; NReSUQP*m;2CmS&4E= zuDbXo63(U09H^&0JhZfiwz}xV5XO{lrKJ0d`nnY#d$ZvJ&0MlBSzWlE?=u$yb|c}z z<}vJNIUd#zcC?(okc%F;|EtF7!uzf#1doc|=XIYImTP0$gWsE-3SZ&jW>XQarqy<% z*IuEm-g21R5t2GC#?%rnlcp{c!d>;D-C1c*&KubT$O!$Y7!*rj+jm}dO}1M8p4EV2 zm&)!-bdrwBF<$X2tsAzcTer{|!q%N^G5p{bD9S6te9y*cfvT~6MlNk}iF8tLGEh*? z`RWnwcRc%rv(%;$;~DKO$oEwX#-?2ZJnRU`iWRGp&zl18m<)+}+CC*lXHzL5=Z;<_ zr`_dzh0I+nc6hh=b;Oq0vx%C04l5fH`N8X8w<;mxb<(-^2|?8(wy+3RJx490e7i#{ zzjJ?Sn~eKed38NMV%a``I7&{e8F}e^b-qJs3~jnI!+#F4kK;F9+J$Ii+gqM^eB6hB zEPYtmdOBq$Z}v@Z$|YT<4l8jp7uy382h(;k>|Z>-oZK0uMSa_m$sZNYu90n^)Rd6d zhJ2sqQU5`cUd)`VUoozF;CZHt*YVP;Hm2*2PL__l3@OaL5Em2%gn<*;RTfWw20s|~ zzOM6>_TunFzn07VTP2-jhZ|RP;9UG%wAI+}FZ;B9=Z(%!D}MJcW}g^eHB2v}`5Wjn zVevZ2CqQMJgEpg+Z2f1Hv{=&?sWh6s)# cq z9n5=vs?(gUY$Q_P57TCD+++{%c~E&k&fL{{D1nT$>eiLk7oKiK%EYPV)c045uXR?@ zZaf3Ko8z`Pxtm`!?Jyq-y36p8ONE0!q)M8_hWjfcEJr-nJz|cqkfWC-s*OCaw0C@= zFuV)qf<~>0>u+T%hP$|{OL7Q!uK@7-`P}N`mE<{GcJZq^#LLfk^;1dTN3G&ECd_oM z8D6p6!&qj0kvqz}KTQToadF@Ca$PQpSa;5(2g|!%Pph`$U}ZLZ zLhx#x>Zu31LR}TK0tpj$pGoo?RSe#Bw%|8T)+CnTcGzQ-j*ooAg;MU<@Qxi#AB!3o z+s9I(TH2VI{g`|?aB4LyJFl13T)DJqcUVBQwKn8Oe#JpnG?5nK>)y_q?;dB;`yHM- z{#Yqh?2atS`vnqC!3HE2Rv-d;$U=}cE2W^*tr-%fuh6@mdNDaT{gX?=9Y^^<0fIL6 zbxBbWRKid;bi0#JAa-nZwpJ1~eT?J{jY}qvq`!25>M5&IB>Y%fkl#>Ayw~ts^Ocym zwlFTp!)L|9derLW9WiEyB(hr%nZI>IKqvC`lMz!6P=DTR&h1YH%D;Y2-1iV`&Cv4H zdU`^H**~0%LN=>u4DWP-`l1r6Q}^{g{%z|zopMZ|eR=U2E&Y;hWB+R;Cek0$dXqeI zSYITd#mHaJm_EmEq*&hf21(;{6V%&VsrIEGJ(s*z^Y&xR(*&1Z5pzfBxMnpQ7Hf$y zObj a=RJuM1aWg`RD)Spa9(pGxemT=28IOYEAIrli zPGyUd*a;7IIAsDZolg-OzgQ9h#@JwA{_V>Z2fIkh@Hy@@qg7{hMGE5n>SOMpPZPgQ zZ0xi6yYt_8Zcz^mVoSHuFP&uv(NTL^DTN@Qt*DxP?zD1Z+v8!hym&{my9~ zo3!w~7{Qn3-Cv&eXr}JulEOoap&zA9hws!WwxA{W9)v1Vu>741A=qv?=38Wsnc`3U z?Xbgcp+$p*M;0=h1>4~lp`={p;RM{N0Xn~&nF(q?H}+PuTx&no>EhkFEE^DXEV+&D z&W*=lxo?ggD#!CD=;8L$YD|;cxR=~32<0arVyx%`F^<`5)$zzqa;mZG X=IQ)Kerch?F`kwP3+lUTD5exfc;bln_I1Q+zs+k z3o1@;^SJRkg0hCeKbmvMy-t2n=8h@izUeI^KG<`TE*@5(nQdMOr(Cq2y~T)k=ni4H zLhI&{g2Ezu(SiwN){(0yRag8kfNGg?}Z@%p^>bznV0qNU5fiGc;T0w#c14(AFP zL7&{Dr=f?z*-U2l)JTogZV7Si)8A%53JHauy1OU!Z@CtQr;_ty< zD}Rz0%`L(wP{5a8;NHfj@a62i^RrhpPQ-=_Y(C{vj GYnjd)^~?a*vI@7 &y3Y$Uq ga3>jl>)mSbxam|?jlHMi zb7`}f*F$hhdWL~vbemF49rdZxrTX(Th!c?O(lU{r{x61N98&7|8btiG7Q)8PPuRUm z)Y6AjB$Xv0dI;6?C-v7APmFXCn2Ue@^2TZceSTgX6@eALasgT*gI(Gd$5@9%X<}L5 z<#RqDTB|!%`&Flh0(pU1jXMLKmVI$Kge8WCsiOt))O2Bb5U1Dj)4ro1%cW$$rcqK7 z CeJkjNQJ-)8D1q=h9i~X1RkU(r5@GF2Q<-1$%U^3ffqfickbvF%$%?>c#^?m zW0}gZB8}QoEgduKnE`d7j#xLz?gvtaPxiBva6b6D?<+U`fLz5K04yBpPAk>ARrD0| zxcVTAwCIhTPxVyD>-XURU>M2n)Aw|w)TkoSJE-Crt!N*MG?%#}ZgzVnM7S#$0JdKi z!?|ByG>i9e(_lr5WL9D{-W~Xu_Dl>Ikfohlevt02-t10LJ;RFTx}-mw=RftqX%&D2 zlKJ JP?Sd9f)c5RU;ekuo}IR(4OvK7Kn ! zLmHc#OLRg(ojX%5%)=eEMVT=@Q-y}0A}ab)*!BUro*uEor%aV^fm004cPnASbZG!H zf}|C1Rr4EdSczsP$f}y}1BwN)$McgIBUply^ya%G9J*28SWQ<_L1l^GXm=e?yuEbS z8OnaJT6M*qY7TV1zq*l*{%j|a`nA+8n9TbwfE9>Go)G{q+XQ&IjrOz7*!QvkN*B=? zM&C~j2dHsvG$LNxtFQr~d83QnH+ox<6R~`UN;h{0ol*b_cczAeRZFW+A8WpNYFKaY zY}mfY#DV3wXXDukWZD5Q(Gskuxy$44EP%f*QFlc3$~lx6j^BuAeZ=JLsKqDjzDTpd z^_^v_^jU2C83l`NU+P()^9|A>d9w#j8Os}!=#GHQ3xZVlobRpTQp?CvWU5U0S*Iv~ zB8^MmieGm+?52DF2ge@UQS+B69Y}17OjSD<2tFWW581WzkO3qUcc(h!;f5K6=L9oT zq|@N_mV^9841)Mz91?)qA>`H~&iboTj8Ra4q7epU-z3vSULOzg`^AKur=Wan77hTE zGY-=|{;FK(a#@fE-5zwjR*fBKB^~$^;{O}j|0f*$|JW8lJo~?Pj=w 7oszyI;h%1$|Pnp7Ji%ow=blN)a^G56YsGr z*tsxu$gCp$yY_B)yt;dRpRe#L_I>7z;A*DGEq2f{D}aI137nb@tFn7|f in6tdfsBhcA z(lw|A$bK*ip}AT0N8SfFc$21 ^JD9sn&xg `;G;`1 zhjvycx*Pzj@wZe- Ujzj@`Tul5Y$gL+ zz~**@d#}h|pKVusty(HM-675G$F!#mb$TQKXGni igjJ-%}{lI_4?gfC~?_rSnq`C4FB3`624UE>O{@si!;dna-%YE0$BlmP1%I^3RFd z)uw|;Glu`ZH4635W_Z$JF+4Ys0M-YV H7UQ!KMO{9qH~M-{E231hMT{rU&Q2_Cw%OYLTt0i-wrN~?bM{-UefWeAPJXu zeqyjaIA$GNH0iE%88iwL#mya8eDqx1Ek~RwZpTT>EBT8mzECobUjOd1V6QI-IaXS! zJX^HFUn;wAN#Pkr4H*JOC* !p|2PPM1@S*1{@&r?zYrgE_s@yH2mkx|;y=Y2>RM)9 z3+B}O!xB)cs!Im(Ix!@x*>1+EfPc^4Zv{Q1MB?S+S2rm?rVJG!ppw}xYcCbbBKQ8i zk`>76bha}*bnTNk9c7^fPJ|X1IVTf660YVH^(80RfqM(o_2^- cA$Wx zFLmizPGXMhI~Kt>d >FcQN88jksrsWU #r^B5kbKoH}Ak0%KhQV}3O6oe*PDHjcQw$M-oRTd%o*>4tLw z0BAP&(;6lKjPQR9{%hsmB(tN#tpiM;D8m-{#C-s}vt8wmL(uF*A1>U!Z0gzfqR;R5 znyB&2wb=eq HVL)RB5MJfO4Wtcl`F6 zjCa~sdB2Je2(XWQLnQF&&-=Yl%8u_JM`CVl6f;7phCnBq?FG4jioRb+e>JWO7KdeQ z*9IFPkhfG8?qqopy28n_*VlEhiSI0KPM#SL>VR4$?FZ^|VY!z~)hS1?c|BdLv{W7@ zpjZhDRD8^-kPtFE$oegVVF!M*++K)L-{KT5LPy^d=nCbL@vMMUb~sL_4O!Y|Lw#FL z3DpOrI7sgzq#&qyYn147usKg)_X(VOO=1Ul-I~CM422TH7-?2m@P#9vo9_^L0hx9m z+-b*5{|=-~cNEpJoWgzpkYcWuwa)AB9^xohVQ~<2fh8Pb%9WWE8SD^&Je(+Q>Jm=J z-d~=t-yf-#FKjca=(JzkB==a&`%o)#RFQ4=bl|UO#xY>xX73Cm{o7N0rH3zE(J8VO zhwja1enPxdO(Y5cSpHC{iCQrc_iK(zWDc3YFZHfpnlBRy5dgBxs%9#yMKe1-{0O&^ zdwKGgulC>#O{eZ*9-#Dw*$l$8Yb{^;S4_Pe>o-J!s{#Pg+^pMeBV7~b2r~N6@u);r zRbeNd!B1!43hLUKa=SbnCk|768k*1DjiUCX_W9Fw9J><$K;?Kf;rD&U84_aQgsO%` zb=U==yu$7)3MBKn%ma~ $7>#{40i2@1|1VT+&6>Zlb^#93JG#Y7=W*MG5)qm zpblf(K-lsHMNnkTgsyYrmJ=g$Wofnn03wib F z%o9oe`O*VrgYA#LCmVosE7AYyW|}0>@#@szL1Z>(V!RS2xh!cRs9p^_F}h9%n=%b^ zexqvx?_d*04R{@=1CoLtxJeIQEoB7Kk6Q2`gNh^F3k%PO2DdTdGsx;(^UVB4HreR! z9s3`-6NqBvwx?3>Q3bvtv|zVmFkPzMs*^)8=P)EDTFmDk&7P7jhNxrVADd+4>Cq15 z37ciG3H8pBRwS$7jmDH4h%kMLlN~z;zar-Gt7(aLC!LEL0ap3a0X`0eXzR- imK~;{r> P@@!qAVqqS-n%rZ zg7gxK^co>Ff!uIB=X~FNo_lV7?95(!tuQqAXb;vb8riixKI!p_qN;$cgo1Vh-^a;dvQ>}~aJAuwO}E?W=@2`QJO zp^2x7mZlsO;VJ|<*AYUwx)H5ONI;4xHwe_()|1P|*4`1W0Nk!^1adjT6oAH(S|VC* z%C-)U5BxlA4g9nXp?=O#Sr}09E*A(TM|9w7>j~jPxw^oSawrAhpMK?t?Q^j(kn2ws zPiF<-@1RVybh(rf9=2SPLL!1t5h)QaDN!L&aS3T@={sCvBBCO~B4Wa#VuGU5a^hlg zA|hOW{eZ;YJYaTm`e4<+`Xb&b03AF%-Q RtD VGHp@ zco-rOE`QIWt^>jofpkE)aVZ-}av3|qVF(}OpW0ela_VrTCj<_)RR=2oiS|N{jxaeT zS!EFw5sCY `RT;37s_K1JrTY?p+kz2LFIQW*=ij!l|JW-3tL-@u zT-}I0gKa$=y=`Ht9tc;iKL;%5`0ugE{;R*gY+?T%i_*W^3KN47K3~@VTIRn+MC_b5 z{{b!W<{#MG!iiY-Ac8t8j!T8O!=CDD8mjn}oPxprE@1Frc|`00!9Kd`8-b8pR8i=B z^VhFm1j4titu0mm;_?{#cnFx=KKFC?RIxvpKqytUiS+@y^~Xb0z$zsKg7Gcx$*CzI zkCA$GuXTL?+v1@;Jia$Z`*5OYad9zo2=A9YFq+{(C<%y;iD@34Sv)0F)->^Q-x?U( zoA_EvIGra9#D~}83BOvW##$lP@lCBg9mfP^gGlC>?r?puimKI<{JdLAmSvw_79SGy z)l|b0i?h}VNnh)F`iBUoMW-vRrwtLkO^FcyOu|~Wr@9bft*mn43_JVd@N3eCrirs6 zm-UU!VM6b6X~5*x$yzotvG~nCfsmP=Gd}peb?x+_l`p=~2bMYu?Huphm|kdpZ>3_r z*%dAsSO?WrSJQPM5cbzIAe-}zf!12aW@cdE1HwQ|oXz8Ge{<{yL+*Rwh99OI!Zc2H z=2li$cUNXoW(cp eZ zB}3H2Mskcf>*(N9_e=RvD Vmhxm z^~!McHxH`KIx68+hlR%`Gxi@*f&3L~h0#cKMet;O3SvqUIOKeTdzs9(QAXlQit)R9 zY|lrtzxbS9-xVQ$t&gCo^V|t8=G$=zvG|Nbj5#qVL!Y!p%yfj&>7+m$bToHkQz{e? zVb4^9#4e8%i#!fk-W~N1xN2(ra>}W7#czhaM=^qZTGFDt?=>Cnrh$V2xs{(Z*$<4s zzz^3lJ7dxB!Zl+TY5}q)SBA}N98g=Y6H9hNn%>SD!uLXt(biOEaaiUwZfw1I{K}R3 zl}zV-Yl?xu;gmaJUz9$7io2*oNIc6plJaad;=U^OaJ{L$l=-V*aNOX?ua{>J`xB>{ zT)(N`EUvRZjAm4LZ_~N+=#j&V9p-A=Y>(1ta-pzh@-U3Xl=<`EO*Z9e#hFdo$9efI z817*=rB4=KqQ{dc*x`6O$?U2xDUXk|#}Ioky;)h`&z8BI=J$Ei^g=GQ_TTAbhACG_ zi;QYuxXz-LZko0)xaV_E*d|USOBmEXH8q