Browse Source

plugin simple membership

frontend 3 years ago
commit
083deb5a60
100 changed files with 11928 additions and 0 deletions
  1. 211 0
      simple-membership/classes/admin-includes/class.swpm-payment-buttons-list-table.php
  2. 95 0
      simple-membership/classes/admin-includes/class.swpm-payments-admin-menu.php
  3. 224 0
      simple-membership/classes/admin-includes/class.swpm-payments-list-table.php
  4. 172 0
      simple-membership/classes/class-swpm-member-subscriptions.php
  5. 804 0
      simple-membership/classes/class.simple-wp-membership.php
  6. 298 0
      simple-membership/classes/class.swpm-access-control.php
  7. 186 0
      simple-membership/classes/class.swpm-admin-registration.php
  8. 49 0
      simple-membership/classes/class.swpm-ajax.php
  9. 9 0
      simple-membership/classes/class.swpm-auth-permission-collection.php
  10. 402 0
      simple-membership/classes/class.swpm-auth.php
  11. 140 0
      simple-membership/classes/class.swpm-category-list.php
  12. 82 0
      simple-membership/classes/class.swpm-comment-form-related.php
  13. 89 0
      simple-membership/classes/class.swpm-cronjob.php
  14. 305 0
      simple-membership/classes/class.swpm-form.php
  15. 7 0
      simple-membership/classes/class.swpm-front-form.php
  16. 526 0
      simple-membership/classes/class.swpm-front-registration.php
  17. 214 0
      simple-membership/classes/class.swpm-init-time-tasks.php
  18. 284 0
      simple-membership/classes/class.swpm-installation.php
  19. 129 0
      simple-membership/classes/class.swpm-level-form.php
  20. 154 0
      simple-membership/classes/class.swpm-log.php
  21. 780 0
      simple-membership/classes/class.swpm-members.php
  22. 93 0
      simple-membership/classes/class.swpm-membership-level-custom.php
  23. 116 0
      simple-membership/classes/class.swpm-membership-level.php
  24. 331 0
      simple-membership/classes/class.swpm-membership-levels.php
  25. 35 0
      simple-membership/classes/class.swpm-messages.php
  26. 10 0
      simple-membership/classes/class.swpm-notification-bus.php
  27. 49 0
      simple-membership/classes/class.swpm-permission-collection.php
  28. 65 0
      simple-membership/classes/class.swpm-permission.php
  29. 240 0
      simple-membership/classes/class.swpm-post-list.php
  30. 326 0
      simple-membership/classes/class.swpm-protection-base.php
  31. 70 0
      simple-membership/classes/class.swpm-protection.php
  32. 127 0
      simple-membership/classes/class.swpm-registration.php
  33. 110 0
      simple-membership/classes/class.swpm-self-action-handler.php
  34. 0 0
      simple-membership/classes/class.swpm-session.php
  35. 1245 0
      simple-membership/classes/class.swpm-settings.php
  36. 87 0
      simple-membership/classes/class.swpm-transactions.php
  37. 47 0
      simple-membership/classes/class.swpm-transfer.php
  38. 295 0
      simple-membership/classes/class.swpm-utils-member.php
  39. 39 0
      simple-membership/classes/class.swpm-utils-membership-level.php
  40. 791 0
      simple-membership/classes/class.swpm-utils-misc.php
  41. 42 0
      simple-membership/classes/class.swpm-utils-template.php
  42. 571 0
      simple-membership/classes/class.swpm-utils.php
  43. 165 0
      simple-membership/classes/class.swpm-wp-loaded-tasks.php
  44. 7 0
      simple-membership/classes/common/class.swpm-list-table.php
  45. 0 0
      simple-membership/classes/index.html
  46. 226 0
      simple-membership/classes/shortcode-related/class.swpm-shortcodes-handler.php
  47. 0 0
      simple-membership/css/images/index.html
  48. BIN
      simple-membership/css/images/ui-icons_444444_256x240.png
  49. BIN
      simple-membership/css/images/ui-icons_555555_256x240.png
  50. BIN
      simple-membership/css/images/ui-icons_777620_256x240.png
  51. BIN
      simple-membership/css/images/ui-icons_777777_256x240.png
  52. BIN
      simple-membership/css/images/ui-icons_cc0000_256x240.png
  53. BIN
      simple-membership/css/images/ui-icons_ffffff_256x240.png
  54. 0 0
      simple-membership/css/index.html
  55. 0 0
      simple-membership/css/jquery-ui.min.css
  56. 34 0
      simple-membership/css/swpm.addons.listing.css
  57. 167 0
      simple-membership/css/swpm.common.css
  58. 181 0
      simple-membership/css/validationEngine.jquery.css
  59. BIN
      simple-membership/images/addons/2fa-addon-icon.png
  60. BIN
      simple-membership/images/addons/affiliate-platform-integration.png
  61. BIN
      simple-membership/images/addons/affiliates-manager-integration.png
  62. BIN
      simple-membership/images/addons/custom-post-type-protection-enhanced.png
  63. BIN
      simple-membership/images/addons/email-notification-and-broadcast-addon.png
  64. BIN
      simple-membership/images/addons/form-shortcode-generator.png
  65. BIN
      simple-membership/images/addons/full-page-protection-addon.png
  66. BIN
      simple-membership/images/addons/google-first-click-free-addon.png
  67. BIN
      simple-membership/images/addons/google-recaptcha-addon.png
  68. BIN
      simple-membership/images/addons/idevaffiliate-integration.png
  69. BIN
      simple-membership/images/addons/mailchimp-integration.png
  70. BIN
      simple-membership/images/addons/show-member-info.png
  71. BIN
      simple-membership/images/addons/swpm-aweber-integration-addon.png
  72. BIN
      simple-membership/images/addons/swpm-bbpress-integration.png
  73. BIN
      simple-membership/images/addons/swpm-bulk-member-importer-from-csv-addon.png
  74. BIN
      simple-membership/images/addons/swpm-convertkit-integration-addon.png
  75. BIN
      simple-membership/images/addons/swpm-custom-messages.png
  76. BIN
      simple-membership/images/addons/swpm-data-exporter-addon.png
  77. BIN
      simple-membership/images/addons/swpm-form-builder.png
  78. BIN
      simple-membership/images/addons/swpm-login-redirection.png
  79. BIN
      simple-membership/images/addons/swpm-member-directory-listing-addon.png
  80. BIN
      simple-membership/images/addons/swpm-member-payments-addon.png
  81. BIN
      simple-membership/images/addons/swpm-misc-shortcodes-addon.png
  82. BIN
      simple-membership/images/addons/swpm-older-posts-protection.png
  83. BIN
      simple-membership/images/addons/swpm-partial-protection-addon.png
  84. BIN
      simple-membership/images/addons/swpm-woocommerce-addon.png
  85. BIN
      simple-membership/images/addons/wp-user-import.png
  86. 0 0
      simple-membership/images/index.html
  87. BIN
      simple-membership/images/join-now-button-image.gif
  88. BIN
      simple-membership/images/logo.png
  89. BIN
      simple-membership/images/logo2.png
  90. BIN
      simple-membership/images/next.gif
  91. BIN
      simple-membership/images/prev.gif
  92. BIN
      simple-membership/images/restricted-icon.png
  93. BIN
      simple-membership/images/simple-membership-content-protection-usage.png
  94. 0 0
      simple-membership/index.html
  95. 0 0
      simple-membership/ipn/index.html
  96. 143 0
      simple-membership/ipn/swpm-braintree-buy-now-ipn.php
  97. 410 0
      simple-membership/ipn/swpm-smart-checkout-ipn.php
  98. 155 0
      simple-membership/ipn/swpm-stripe-buy-now-ipn.php
  99. 381 0
      simple-membership/ipn/swpm-stripe-sca-buy-now-ipn.php
  100. 210 0
      simple-membership/ipn/swpm-stripe-sca-subscription-ipn.php

+ 211 - 0
simple-membership/classes/admin-includes/class.swpm-payment-buttons-list-table.php

@@ -0,0 +1,211 @@
+<?php
+
+if (!class_exists('WP_List_Table')){
+    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
+}
+
+class SwpmPaymentButtonsListTable extends WP_List_Table {
+
+    private $per_page;
+
+    function __construct() {
+        global $status, $page;
+
+        //Set parent defaults
+        parent::__construct(array(
+            'singular' => 'payment button', //singular name of the listed records
+            'plural' => 'payment buttons', //plural name of the listed records
+            'ajax' => false //does this table support ajax?
+        ));
+
+        $this->per_page = 50;
+    }
+
+    function column_default($item, $column_name) {
+        //We need to read the values from our CPT and feed the column value for the given column name manually.
+
+        switch ($column_name) {
+            case 'title':
+                return get_the_title($item['ID']);
+                break;
+            case 'membership_level':
+                return get_post_meta($item['ID'], 'membership_level_id', true);
+                break;
+            case 'button_type':
+                $button_type = get_post_meta($item['ID'], 'button_type', true);
+                $button_name=SwpmMiscUtils::get_button_type_name($button_type);
+                return $button_name;
+                break;
+            case 'button_shortcode':
+                $level_id = get_post_meta($item['ID'], 'membership_level_id', true);
+                if(!SwpmUtils::membership_level_id_exists($level_id)){
+                    //This membership level doesn't exist. Show an error instead of the shortcode.
+                    $shortcode = 'Error! The membership level you specified in this button does not exist. You may have deleted this level. Edit this button and use a valid membership level.';
+                } else {
+                    $shortcode = '[swpm_payment_button id='.$item['ID'].']';
+                }
+                return $shortcode;
+                break;            
+        }
+    }
+
+    function column_ID($item) {
+
+        $button_type = get_post_meta($item['ID'], 'button_type', true);
+        //Build row actions
+        $actions = array(
+            'edit' => sprintf('<a href="admin.php?page=simple_wp_membership_payments&tab=edit_button&button_id=%s&button_type=%s">Edit</a>', $item['ID'], $button_type),
+            'delete' => sprintf('<a href="admin.php?page=simple_wp_membership_payments&tab=payment_buttons&action=delete_payment_btn&button_id=%s" onclick="return confirm(\'Are you sure you want to delete this record?\')">Delete</a>', $item['ID']),
+        );
+
+        //Return the refid column contents
+        return $item['ID'] . $this->row_actions($actions);
+    }
+
+    function column_cb($item) {
+        return sprintf(
+                '<input type="checkbox" name="%1$s[]" value="%2$s" />',
+                /* $1%s */ $this->_args['singular'], //Let's reuse singular label (affiliate)
+                /* $2%s */ $item['ID'] //The value of the checkbox should be the record's key/id
+        );
+    }
+
+    function get_columns() {
+        $columns = array(
+            'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
+            'ID' => SwpmUtils::_('Payment Button ID'),
+            'title' => SwpmUtils::_('Payment Button Title'),
+            'membership_level' => SwpmUtils::_('Membership Level ID'),
+            'button_type' => SwpmUtils::_('Button Type'),
+            'button_shortcode' => SwpmUtils::_('Button Shortcode'),
+        );
+        return $columns;
+    }
+
+    function get_sortable_columns() {
+        $sortable_columns = array();
+//        $sortable_columns = array(
+//            'ID' => array('ID', false), //true means its already sorted
+//        );
+        return $sortable_columns;
+    }
+
+    function get_bulk_actions() {
+        $actions = array(
+            'delete' => SwpmUtils::_('Delete')
+        );
+        return $actions;
+    }
+
+    function process_bulk_action() {
+        //Detect when a bulk action is being triggered...
+        if ('delete' === $this->current_action()) {
+            $records_to_delete = array_map( 'sanitize_text_field', $_REQUEST['paymentbutton'] );
+            if (empty($records_to_delete)) {
+                echo '<div id="message" class="updated fade"><p>Error! You need to select multiple records to perform a bulk action!</p></div>';
+                return;
+            }
+
+            foreach ($records_to_delete as $record_id) {
+                if(!is_numeric($record_id)){
+                    wp_die('Error! ID must be a numeric number.');
+                }
+                wp_delete_post( $record_id );
+            }
+            echo '<div id="message" class="updated fade"><p>Selected records deleted successfully!</p></div>';
+        }
+    }
+
+    function process_delete_action() {
+
+        if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'delete_payment_btn') { //Delete link was clicked for a row in list table
+            $record_id = sanitize_text_field($_REQUEST['button_id']);
+            if(!is_numeric($record_id)){
+                wp_die('Error! ID must be a numeric number.');
+            }
+            wp_delete_post( $record_id );
+            $success_msg = '<div id="message" class="updated"><p>';
+            $success_msg .= SwpmUtils::_('The selected entry was deleted!');
+            $success_msg .= '</p></div>';
+            echo $success_msg;
+        }
+    }
+
+    /**
+     * Retrieve the current page number
+     */
+    function get_paged() {
+        return isset($_GET['paged']) ? absint($_GET['paged']) : 1;
+    }
+
+    /**
+     * Retrieve the total number of CPT items
+     */
+    function get_total_items() {
+        $counts = wp_count_posts('swpm_payment_button');
+        $total = 0;
+        foreach ($counts as $count)
+            $total += $count;
+
+        return $total;
+    }
+
+    function payment_buttons_data() {
+        $data = array();
+        $cpt_args = array(
+            'post_type' => 'swpm_payment_button',
+            'post_status' => 'publish',
+            'posts_per_page' => $this->per_page,
+            'paged' => $this->get_paged()
+        );
+
+        //TODO - Do search and sort stuff (see example code)
+        
+        //Retrieve all the CPT items
+        $items = get_posts($cpt_args);
+        if ($items) {
+            foreach ($items as $item) {
+
+                $membership_level = get_post_meta($item->ID, 'membership_level_id', true);
+                $data[] = array(
+                    'ID' => $item->ID,
+                    'title' => get_the_title($item->ID),
+                    'membership_level' => $membership_level,
+                );
+            }
+        }
+
+        return $data;
+    }
+
+    function prepare_items() {
+
+        // Lets decide how many records per page to show
+        $per_page = $this->per_page;
+
+        $columns = $this->get_columns();
+        $hidden = array();
+        $sortable = $this->get_sortable_columns();
+
+        $this->_column_headers = array($columns, $hidden, $sortable);
+
+        $this->process_delete_action();
+        $this->process_bulk_action();
+
+        // Pagination requirement
+        $current_page = $this->get_pagenum();
+        $total_items = $this->get_total_items();
+
+        // Now we add our *sorted* data to the items property, where it can be used by the rest of the class.
+        $data = $this->payment_buttons_data();
+        $this->items = $data;
+
+        //pagination requirement
+        $this->set_pagination_args(array(
+            'total_items' => $total_items, //WE have to calculate the total number of items
+            'per_page' => $per_page, //WE have to determine how many items to show on a page
+            'total_pages' => ceil($total_items / $per_page)   //WE have to calculate the total number of pages
+        ));
+    }
+
+}

+ 95 - 0
simple-membership/classes/admin-includes/class.swpm-payments-admin-menu.php

@@ -0,0 +1,95 @@
+<?php
+
+class SwpmPaymentsAdminMenu {
+
+    function __construct() {
+
+    }
+
+    function handle_main_payments_admin_menu() {
+        do_action('swpm_payments_menu_start');
+
+        //Check current_user_can() or die.
+        SwpmMiscUtils::check_user_permission_and_is_admin('Main Payments Admin Menu');
+
+        $output = '';
+        $tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : '';
+        $selected = $tab;
+        ?>
+
+
+        <div class="wrap swpm-admin-menu-wrap"><!-- start wrap -->
+
+            <h1><?php echo SwpmUtils::_('Simple Membership::Payments') ?></h1><!-- page title -->
+
+            <!-- start nav menu tabs -->
+            <h2 class="nav-tab-wrapper">
+                <a class="nav-tab <?php echo ($tab == '') ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership_payments"><?php SwpmUtils::e('Transactions'); ?></a>
+                <a class="nav-tab <?php echo ($tab == 'payment_buttons') ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership_payments&tab=payment_buttons"><?php SwpmUtils::e('Manage Payment Buttons'); ?></a>
+                <a class="nav-tab <?php echo ($tab == 'create_new_button') ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership_payments&tab=create_new_button"><?php SwpmUtils::e('Create New Button'); ?></a>
+                <?php
+                if ($tab == 'edit_button') {//Only show the "edit button" tab when a button is being edited.
+                    echo '<a class="nav-tab nav-tab-active" href="#">Edit Button</a>';
+                }
+
+                //Trigger hooks that allows an extension to add extra nav tabs in the payments menu.
+                do_action ('swpm_payments_menu_nav_tabs', $selected);
+
+                $menu_tabs = apply_filters('swpm_payments_menu_additional_menu_tabs_array', array());
+                foreach ($menu_tabs as $menu_action => $title){
+                    ?>
+                    <a class="nav-tab <?php echo ($selected == $menu_action) ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership_payments&tab=<?php echo $menu_action; ?>" ><?php SwpmUtils::e($title); ?></a>
+                    <?php
+                }
+
+                ?>
+            </h2>
+            <!-- end nav menu tabs -->
+
+            <?php
+
+            do_action('swpm_payments_menu_after_nav_tabs');
+
+            //Allows an addon to completely override the body section of the payments admin menu for a given action.
+            $output = apply_filters('swpm_payments_menu_body_override', '', $tab);
+            if (!empty($output)) {
+                //An addon has overriden the body of this page for the given tab/action. So no need to do anything in core.
+                echo $output;
+                echo '</div>';//<!-- end of wrap -->
+                return;
+            }
+
+            echo '<div id="poststuff"><div id="post-body">';
+
+            //TODO - move most of the following includes to functions of this class instead.
+
+            //Switch case for the various different tabs handled by the core plugin.
+            switch ($tab) {
+                case 'payment_buttons':
+                    include_once(SIMPLE_WP_MEMBERSHIP_PATH . '/views/payments/admin_payment_buttons.php');
+                    break;
+                case 'create_new_button':
+                    include_once(SIMPLE_WP_MEMBERSHIP_PATH . '/views/payments/admin_create_payment_buttons.php');
+                    break;
+                case 'edit_button':
+                    include_once(SIMPLE_WP_MEMBERSHIP_PATH . '/views/payments/admin_edit_payment_buttons.php');
+                    break;
+                case 'all_txns':
+                    include_once(SIMPLE_WP_MEMBERSHIP_PATH . '/views/payments/admin_all_payment_transactions.php');
+                    break;
+                case 'add_new_txn':
+                    include_once(SIMPLE_WP_MEMBERSHIP_PATH . '/views/payments/admin_add_edit_transaction_manually.php');
+                    swpm_handle_add_new_txn_manually();
+                    break;
+                default:
+                    include_once(SIMPLE_WP_MEMBERSHIP_PATH . '/views/payments/admin_all_payment_transactions.php');
+                    break;
+            }
+
+            echo '</div></div>'; //<!-- end of post-body -->
+
+        echo '</div>'; //<!-- end of .wrap -->
+    }
+
+}
+

+ 224 - 0
simple-membership/classes/admin-includes/class.swpm-payments-list-table.php

@@ -0,0 +1,224 @@
+<?php
+
+if ( ! class_exists( 'WP_List_Table' ) ) {
+	require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
+}
+
+class SWPMPaymentsListTable extends WP_List_Table {
+
+	public function __construct() {
+		global $status, $page;
+
+		// Set parent defaults
+		parent::__construct(
+			array(
+				'singular' => 'transaction', // singular name of the listed records
+				'plural'   => 'transactions', // plural name of the listed records
+				'ajax'     => false, // does this table support ajax?
+			)
+		);
+	}
+
+	function column_default( $item, $column_name ) {
+		$val = $item[ $column_name ];
+		switch ( $column_name ) {
+			case 'payment_amount':
+				$val = SwpmMiscUtils::format_money( $val );
+				$val = apply_filters( 'swpm_transactions_page_amount_display', $val, $item );
+				break;
+			default:
+				break;
+		}
+		return $val;
+	}
+
+	function column_id( $item ) {
+
+		// Build row actions
+		$actions = array(
+			/* 'edit' => sprintf('<a href="admin.php?page=simple_wp_membership_payments&edit_txn=%s">Edit</a>', $item['id']),//TODO - Will be implemented in a future date */
+			'delete' => sprintf( '<a href="admin.php?page=simple_wp_membership_payments&action=delete_txn&id=%s" onclick="return confirm(\'Are you sure you want to delete this record?\')">Delete</a>', $item['id'] ),
+		);
+
+		// Return the refid column contents
+		return $item['id'] . $this->row_actions( $actions );
+	}
+
+	function column_member_profile( $item ) {
+		global $wpdb;
+		$member_id    = $item['member_id'];
+		$subscr_id    = $item['subscr_id'];
+		$column_value = '';
+
+		if ( empty( $member_id ) ) {// Lets try to get the member id using unique reference
+			if ( ! empty( $subscr_id ) ) {
+				$resultset = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}swpm_members_tbl where subscr_id=%s", $subscr_id ), OBJECT );
+				if ( $resultset ) {
+					// Found a record
+					$member_id = $resultset->member_id;
+				}
+			}
+		}
+
+		if ( ! empty( $member_id ) ) {
+			$profile_page = 'admin.php?page=simple_wp_membership&member_action=edit&member_id=' . $member_id;
+			$column_value = '<a href="' . $profile_page . '">' . SwpmUtils::_( 'View Profile' ) . '</a>';
+		} else {
+			$column_value = '';
+		}
+		return $column_value;
+	}
+
+	function column_cb( $item ) {
+		return sprintf(
+			'<input type="checkbox" name="%1$s[]" value="%2$s" />',
+			/* $1%s */ $this->_args['singular'], // Let's reuse singular label (affiliate)
+			/* $2%s */ $item['id'] // The value of the checkbox should be the record's key/id
+		);
+	}
+
+	function get_columns() {
+		$columns = array(
+			'cb'               => '<input type="checkbox" />', // Render a checkbox instead of text
+			'id'               => SwpmUtils::_( 'Row ID' ),
+			'email'            => SwpmUtils::_( 'Email Address' ),
+			'first_name'       => SwpmUtils::_( 'First Name' ),
+			'last_name'        => SwpmUtils::_( 'Last Name' ),
+			'member_profile'   => SwpmUtils::_( 'Member Profile' ),
+			'txn_date'         => SwpmUtils::_( 'Date' ),
+			'txn_id'           => SwpmUtils::_( 'Transaction ID' ),
+			'subscr_id'        => SwpmUtils::_( 'Subscriber ID' ),
+			'payment_amount'   => SwpmUtils::_( 'Amount' ),
+			'membership_level' => SwpmUtils::_( 'Membership Level' ),
+                        'status' => SwpmUtils::_( 'Status/Note' ),
+		);
+		return $columns;
+	}
+
+	function get_sortable_columns() {
+		$sortable_columns = array(
+			'id'               => array( 'id', false ), // true means its already sorted
+			'membership_level' => array( 'membership_level', false ),
+			'last_name'        => array( 'last_name', false ),
+			'txn_date'         => array( 'txn_date', false ),
+		);
+		return $sortable_columns;
+	}
+
+	function get_bulk_actions() {
+		$actions = array(
+			'delete' => SwpmUtils::_( 'Delete' ),
+		);
+		return $actions;
+	}
+
+	function process_bulk_action() {
+		// Detect when a bulk action is being triggered...
+		if ( 'delete' === $this->current_action() ) {
+			$records_to_delete = array_map( 'sanitize_text_field', $_GET['transaction'] );
+			if ( empty( $records_to_delete ) ) {
+				echo '<div id="message" class="updated fade"><p>Error! You need to select multiple records to perform a bulk action!</p></div>';
+				return;
+			}
+			foreach ( $records_to_delete as $record_id ) {
+				if ( ! is_numeric( $record_id ) ) {
+					wp_die( 'Error! ID must be numeric.' );
+				}
+				global $wpdb;
+				$wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'swpm_payments_tbl WHERE id = %d', $record_id ) );
+			}
+			echo '<div id="message" class="updated fade"><p>Selected records deleted successfully!</p></div>';
+		}
+	}
+
+	function delete_record( $record_id ) {
+		global $wpdb;
+		$wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'swpm_payments_tbl WHERE id = %d', $record_id ) );
+		// also delete record from swpm_transactions CPT
+		$trans = get_posts(
+			array(
+				'meta_key'       => 'db_row_id',
+				'meta_value'     => $record_id,
+				'posts_per_page' => 1,
+				'offset'         => 0,
+				'post_type'      => 'swpm_transactions',
+			)
+		);
+		wp_reset_postdata();
+		if ( empty( $trans ) ) {
+			return;
+		}
+		$trans = $trans[0];
+		wp_delete_post( $trans->ID, true );
+
+	}
+
+	function prepare_items() {
+		global $wpdb;
+
+		// Lets decide how many records per page to show
+		$per_page = apply_filters( 'swpm_transactions_menu_items_per_page', 50 );
+
+		$columns  = $this->get_columns();
+		$hidden   = array();
+		$sortable = $this->get_sortable_columns();
+
+		$this->_column_headers = array( $columns, $hidden, $sortable );
+
+		$this->process_bulk_action();
+
+		// This checks for sorting input. Read and sanitize the inputs
+		$orderby_column = isset( $_GET['orderby'] ) ? sanitize_text_field( $_GET['orderby'] ) : '';
+		$sort_order     = isset( $_GET['order'] ) ? sanitize_text_field( $_GET['order'] ) : '';
+		if ( empty( $orderby_column ) ) {
+			$orderby_column = 'id';
+			$sort_order     = 'DESC';
+		}
+		$orderby_column = SwpmUtils::sanitize_value_by_array( $orderby_column, $sortable );
+		$sort_order     = SwpmUtils::sanitize_value_by_array(
+			$sort_order,
+			array(
+				'DESC' => '1',
+				'ASC'  => '1',
+			)
+		);
+
+		// pagination requirement
+		$current_page = $this->get_pagenum();
+
+		$search_term = filter_input( INPUT_POST, 'swpm_txn_search', FILTER_SANITIZE_STRING );
+		$search_term = trim( $search_term );
+
+		if ( $search_term ) {// Only load the searched records.
+			$like          = $wpdb->esc_like( $search_term );
+			$like          = '%' . $like . '%';
+			$prepare_query = $wpdb->prepare( "SELECT * FROM  {$wpdb->prefix}swpm_payments_tbl WHERE `email` LIKE %s OR `txn_id` LIKE %s OR `first_name` LIKE %s OR `last_name` LIKE %s", $like, $like, $like, $like );
+			$data          = $wpdb->get_results( $prepare_query, ARRAY_A );
+			$total_items   = count( $data );
+		} else { // Load all data in an optimized way (so it is only loading data for the current page)
+			$query       = "SELECT COUNT(*) FROM {$wpdb->prefix}swpm_payments_tbl";
+			$total_items = $wpdb->get_var( $query );
+
+			// pagination requirement
+			$query = "SELECT * FROM {$wpdb->prefix}swpm_payments_tbl ORDER BY $orderby_column $sort_order";
+
+			$offset = ( $current_page - 1 ) * $per_page;
+			$query .= ' LIMIT ' . (int) $offset . ',' . (int) $per_page;
+
+			$data = $wpdb->get_results( $query, ARRAY_A );
+		}
+
+		// Now we add our *sorted* data to the items property, where it can be used by the rest of the class.
+		$this->items = $data;
+
+		// pagination requirement
+		$this->set_pagination_args(
+			array(
+				'total_items' => $total_items, // WE have to calculate the total number of items
+				'per_page'    => $per_page, // WE have to determine how many items to show on a page
+				'total_pages' => ceil( $total_items / $per_page ),   // WE have to calculate the total number of pages
+			)
+		);
+	}
+
+}

+ 172 - 0
simple-membership/classes/class-swpm-member-subscriptions.php

@@ -0,0 +1,172 @@
+<?php
+
+class SWPM_Member_Subscriptions {
+
+	private $active_statuses   = array( 'trialing', 'active' );
+	private $active_subs_count = 0;
+	private $subs_count        = 0;
+	private $subs              = array();
+	private $member_id;
+
+	public function __construct( $member_id ) {
+
+		$this->member_id = $member_id;
+
+		$subscr_id = SwpmMemberUtils::get_member_field_by_id( $member_id, 'subscr_id' );
+
+		$query_args = array(
+			'post_type'  => 'swpm_transactions',
+			'meta_query' => array(
+				'relation' => 'AND',
+				array(
+					'relation' => 'OR',
+					array(
+						'key'     => 'member_id',
+						'value'   => $member_id,
+						'compare' => '=',
+					),
+					array(
+						'key'     => 'subscr_id',
+						'value'   => $subscr_id,
+						'compare' => '=',
+					),
+				),
+				array(
+					'key'     => 'gateway',
+					'value'   => 'stripe-sca-subs',
+					'compare' => '=',
+				),
+			),
+		);
+
+		$found_subs = new WP_Query( $query_args );
+
+		$this->subs_count = $found_subs->post_count;
+
+		foreach ( $found_subs->posts as $found_sub ) {
+			$sub            = array();
+			$post_id        = $found_sub->ID;
+			$sub['post_id'] = $post_id;
+			$sub_id         = get_post_meta( $post_id, 'subscr_id', true );
+
+			$sub['sub_id'] = $sub_id;
+
+			$status = get_post_meta( $post_id, 'subscr_status', true );
+
+			$sub['status'] = $status;
+
+			if ( $this->is_active( $status ) ) {
+				$this->active_subs_count++;
+			}
+
+			$cancel_token = get_post_meta( $post_id, 'subscr_cancel_token', true );
+
+			if ( empty( $cancel_token ) ) {
+				$cancel_token = md5( $post_id . $sub_id . uniqid() );
+				update_post_meta( $post_id, 'subscr_cancel_token', $cancel_token );
+			}
+
+			$sub['cancel_token'] = $cancel_token;
+
+			$is_live        = get_post_meta( $post_id, 'is_live', true );
+			$is_live        = empty( $is_live ) ? false : true;
+			$sub['is_live'] = $is_live;
+
+			$sub['payment_button_id'] = get_post_meta( $post_id, 'payment_button_id', true );
+
+			$this->subs[ $sub_id ] = $sub;
+		}
+
+		$this->recheck_status_if_needed();
+
+	}
+
+	public function get_active_subs_count() {
+		return $this->active_subs_count;
+	}
+
+	public function is_active( $status ) {
+		return  in_array( $status, $this->active_statuses, true );
+	}
+
+	private function recheck_status_if_needed() {
+		foreach ( $this->subs as $sub_id => $sub ) {
+			if ( ! empty( $sub['status'] ) ) {
+				continue;
+			}
+			try {
+				$api_keys = SwpmMiscUtils::get_stripe_api_keys_from_payment_button( $sub['payment_button_id'], $sub['is_live'] );
+
+				SwpmMiscUtils::load_stripe_lib();
+
+				\Stripe\Stripe::setApiKey( $api_keys['secret'] );
+
+				$stripe_sub = \Stripe\Subscription::retrieve( $sub_id );
+
+				$this->subs[ $sub_id ]['status'] = $stripe_sub['status'];
+
+				if ( $this->is_active( $stripe_sub['status'] ) ) {
+					$this->active_subs_count++;
+				}
+
+				update_post_meta( $sub['post_id'], 'subscr_status', $stripe_sub['status'] );
+			} catch ( \Exception $e ) {
+				return false;
+			}
+		}
+	}
+
+	public function get_stripe_subs_cancel_url( $args, $sub_id = false ) {
+		if ( empty( $this->active_subs_count ) ) {
+			return SwpmUtils::_( 'No active subscriptions' );
+		}
+		if ( false === $sub_id ) {
+			$sub_id = array_key_first( $this->subs );
+		}
+		$sub = $this->subs[ $sub_id ];
+
+		$token = $sub['cancel_token'];
+
+		$nonce = wp_nonce_field( $token, 'swpm_cancel_sub_nonce', false, false );
+
+                $anchor_text = isset( $args['anchor_text'] ) ? $args['anchor_text'] : SwpmUtils::_( 'Cancel Subscription' );
+		$out = '<form method="POST">%s<input type="hidden" name="swpm_cancel_sub_token" value="%s"></input>
+		<button type="submit" name="swpm_do_cancel_sub" value="1" onclick="return confirm(\'' . esc_js( SwpmUtils::_( 'Are you sure that you want to cancel the subscription?' ) ) . '\');">' . $anchor_text . '</button></form>';
+
+		$out = sprintf( $out, $nonce, $token );
+
+		return $out;
+	}
+
+	public function find_by_token( $token ) {
+		foreach ( $this->subs as $sub_id => $sub ) {
+			if ( $sub['cancel_token'] === $token ) {
+				return $sub;
+			}
+		}
+	}
+
+	public function cancel( $sub_id ) {
+		$sub = $this->subs[ $sub_id ];
+
+		try {
+			$api_keys = SwpmMiscUtils::get_stripe_api_keys_from_payment_button( $sub['payment_button_id'], $sub['is_live'] );
+
+			SwpmMiscUtils::load_stripe_lib();
+
+			\Stripe\Stripe::setApiKey( $api_keys['secret'] );
+
+			$stripe_sub = \Stripe\Subscription::retrieve( $sub_id );
+
+			if ( $this->is_active( $stripe_sub['status'] ) ) {
+				$stripe_sub->cancel();
+			}
+
+			update_post_meta( $sub['post_id'], 'subscr_status', $stripe_sub['status'] );
+		} catch ( \Exception $e ) {
+			return $e->getMessage();
+		}
+		return true;
+	}
+
+}

+ 804 - 0
simple-membership/classes/class.simple-wp-membership.php

@@ -0,0 +1,804 @@
+<?php
+
+include_once('class.swpm-utils-misc.php');
+include_once('class.swpm-utils.php');
+include_once('class.swpm-utils-member.php');
+include_once('class.swpm-utils-membership-level.php');
+include_once('class.swpm-utils-template.php');
+include_once('class.swpm-init-time-tasks.php');
+include_once('class.swpm-wp-loaded-tasks.php');
+include_once('class.swpm-self-action-handler.php');
+include_once('class.swpm-comment-form-related.php');
+include_once('class.swpm-settings.php');
+include_once('class.swpm-protection.php');
+include_once('class.swpm-permission.php');
+include_once('class.swpm-auth.php');
+include_once('class.swpm-access-control.php');
+include_once('class.swpm-form.php');
+include_once('class.swpm-transfer.php');
+include_once('class.swpm-front-form.php');
+include_once('class.swpm-level-form.php');
+include_once('class.swpm-membership-levels.php');
+include_once('class.swpm-log.php');
+include_once('class.swpm-messages.php');
+include_once('class.swpm-ajax.php');
+include_once('class.swpm-registration.php');
+include_once('class.swpm-front-registration.php');
+include_once('class.swpm-admin-registration.php');
+include_once('class.swpm-membership-level.php');
+include_once('class.swpm-membership-level-custom.php');
+include_once('class.swpm-permission-collection.php');
+include_once('class.swpm-auth-permission-collection.php');
+include_once('class.swpm-transactions.php');
+include_once('shortcode-related/class.swpm-shortcodes-handler.php');
+include_once('class-swpm-member-subscriptions.php');
+
+class SimpleWpMembership {
+
+    public function __construct() {
+
+        new SwpmShortcodesHandler(); //Tackle the shortcode definitions and implementation.
+        new SwpmSelfActionHandler(); //Tackle the self action hook handling.
+
+        add_action('admin_menu', array(&$this, 'menu'));
+        add_action('init', array(&$this, 'init_hook'));
+        add_action('wp_loaded', array(&$this, 'handle_wp_loaded_tasks'));
+
+        add_filter('the_content', array(&$this, 'filter_content'), 20, 1);
+        add_filter('widget_text', 'do_shortcode');
+        add_filter('show_admin_bar', array(&$this, 'hide_adminbar'));
+        add_filter('comment_text', array(&$this, 'filter_comment'));
+        add_filter('comment_form_defaults', array('SwpmCommentFormRelated', 'customize_comment_fields'));
+        add_filter('wp_get_attachment_url', array(&$this, 'filter_attachment_url'), 10, 2);
+        add_filter('wp_get_attachment_metadata', array(&$this, 'filter_attachment'), 10, 2);
+        add_filter('attachment_fields_to_save', array(&$this, 'save_attachment_extra'), 10, 2);
+
+        //TODO - refactor these shortcodes into the shortcodes handler class
+        add_shortcode("swpm_registration_form", array(&$this, 'registration_form'));
+        add_shortcode('swpm_profile_form', array(&$this, 'profile_form'));
+        add_shortcode('swpm_login_form', array(&$this, 'login'));
+        add_shortcode('swpm_reset_form', array(&$this, 'reset'));
+
+        add_action('wp_head', array(&$this, 'wp_head_callback'));
+        add_action('save_post', array(&$this, 'save_postdata'));
+        add_action('admin_notices', array(&$this, 'do_admin_notices'));
+        add_action('wp_enqueue_scripts', array(&$this, 'front_library'));
+        add_action('load-toplevel_page_simple_wp_membership', array(&$this, 'admin_library'));
+        add_action('load-wp-membership_page_simple_wp_membership_levels', array(&$this, 'admin_library'));
+
+        add_action('wp_login', array(&$this, 'wp_login_hook_handler'), 10, 2);
+        add_action('wp_authenticate', array(&$this, 'wp_authenticate_handler'), 1, 2);
+        add_action('wp_logout', array(&$this, 'wp_logout'));
+        add_action('swpm_logout', array(&$this, 'swpm_do_user_logout'));
+        add_action('user_register', array(&$this, 'swpm_handle_wp_user_registration'));
+        add_action('profile_update', array(&$this, 'sync_with_wp_profile'), 10, 2);
+
+        //AJAX hooks
+        add_action('wp_ajax_swpm_validate_email', 'SwpmAjax::validate_email_ajax');
+        add_action('wp_ajax_nopriv_swpm_validate_email', 'SwpmAjax::validate_email_ajax');
+        add_action('wp_ajax_swpm_validate_user_name', 'SwpmAjax::validate_user_name_ajax');
+        add_action('wp_ajax_nopriv_swpm_validate_user_name', 'SwpmAjax::validate_user_name_ajax');
+
+        //init is too early for settings api.
+        add_action('admin_init', array(&$this, 'admin_init_hook'));
+        add_action('plugins_loaded', array(&$this, "plugins_loaded"));
+        add_action('password_reset', array(&$this, 'wp_password_reset_hook'), 10, 2);
+    }
+
+    public function wp_head_callback() {
+        //This function is triggered by the wp_head action hook
+        //Check if members only commenting is allowed then customize the form accordingly
+        SwpmCommentFormRelated::customize_comment_form();
+
+        //Other wp_head related tasks go here.
+    }
+
+    function wp_password_reset_hook($user, $pass) {
+        $swpm_user = SwpmMemberUtils::get_user_by_user_name($user->user_login);
+
+        //Check if SWPM user entry exists
+        if (empty($swpm_user)) {
+            SwpmLog::log_auth_debug("wp_password_reset_hook() - SWPM user not found for username: '" . $user->user_login ."'. This is OK, assuming that this user was created directly in WP Users menu (not using SWPM).", true);
+            return;
+        }
+
+        $swpm_id = $swpm_user->member_id;
+        if (!empty($swpm_id)) {
+            $password_hash = SwpmUtils::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 = SwpmAccessControl::get_instance();
+        if (has_post_thumbnail($post_id)) {
+            return $content;
+        }
+
+        $post = get_post($post_id);
+        if ($acl->can_i_read_post($post)) {
+            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 (isset($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 = SwpmAccessControl::get_instance();
+        if (has_post_thumbnail($post_id)) {
+            return $content;
+        }
+
+        $post = get_post($post_id);
+        if ($acl->can_i_read_post($post)) {
+            return $content;
+        }
+
+        return SwpmUtils::get_restricted_image_url();
+    }
+
+    public function admin_init_hook() {
+        //This hook is triggered in the wp-admin side only.
+
+        $this->common_library(); //Load the common JS libraries and Styles
+        $swpm_settings_obj = SwpmSettings::get_instance();
+
+        //Check if the "Disable Access to WP Dashboard" option is enabled.
+        $disable_wp_dashboard_for_non_admins = $swpm_settings_obj->get_value('disable-access-to-wp-dashboard');
+        if ($disable_wp_dashboard_for_non_admins) {
+            //This option is enabled
+            if ((defined('DOING_AJAX') && DOING_AJAX)) {
+                //This is an ajax request. Don't do the disable dashboard check for ajax.
+            } else {
+                //Not an ajax request. Do the check.
+                if (!current_user_can('administrator')) {
+                    //This is a non-admin user. Do not show the wp dashboard.
+                    $message = '<p>' . SwpmUtils::_('The admin of this site does not allow users to access the wp dashboard.') . '</p>';
+                    $message .= '<p>' . SwpmUtils::_('Go back to the home page by ') . '<a href="' . SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL . '">' . SwpmUtils::_('clicking here') . '</a>.' . '</p>';
+                    wp_die($message);
+                }
+            }
+        }
+
+        //Initialize the settings menu hooks.
+        $swpm_settings_obj->init_config_hooks();
+        $addon_saved = filter_input(INPUT_POST, 'swpm-addon-settings');
+        if (!empty($addon_saved) && current_user_can('manage_options')) {
+            check_admin_referer('swpm_addon_settings_section', 'swpm_addon_settings_section_save_settings');
+            do_action('swpm_addon_settings_save');
+        }
+    }
+
+    public function hide_adminbar() {
+
+        //Never show admin toolbar if the user is not even logged in
+        if (!is_user_logged_in()) {
+            return false;
+        }
+
+        //Show admin toolbar to admin only feature is enabled.
+        $show_to_admin = SwpmSettings::get_instance()->get_value('show-adminbar-admin-only');
+        if ($show_to_admin) {
+            if (current_user_can('administrator')) {
+                //This is an admin user so show the tooldbar
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        //Hide admin toolbar if the hide adminbar feature is enabled
+        $hide = SwpmSettings::get_instance()->get_value('hide-adminbar');
+        return $hide ? FALSE : TRUE;
+    }
+
+    public function shutdown() {
+        SwpmLog::writeall();
+    }
+
+    public static function swpm_login($username, $pass, $rememberme = true) {
+        if (is_user_logged_in()) {
+            $current_user = wp_get_current_user();
+            SwpmLog::log_auth_debug("static function swpm_login(). User is logged in. WP Username: " . $current_user->user_login, true);
+            if ($current_user->user_login == $username) {
+                return;
+            }
+        }
+        SwpmLog::log_auth_debug("Trying wp_signon() with username: " . $username, true);
+
+        add_filter('wordfence_ls_require_captcha', '__return_false');//For Wordfence plugin's captcha compatibility
+
+        $user_obj = wp_signon(array('user_login' => $username, 'user_password' => $pass, 'remember' => $rememberme), is_ssl());
+        if ($user_obj instanceof WP_User) {
+            wp_set_current_user($user_obj->ID, $user_obj->user_login);
+            SwpmLog::log_auth_debug("Setting current WP user to: " . $user_obj->user_login, true);
+        } else {
+            SwpmLog::log_auth_debug("wp_signon() failed for the corresponding WP user account.", false);
+            if (is_wp_error($user_obj)) {
+                //SwpmLog::log_auth_debug("Error Message: ". $user_obj->get_error_message(), false);
+                $force_wp_user_sync = SwpmSettings::get_instance()->get_value('force-wp-user-sync');
+                if (!empty($force_wp_user_sync)) {
+                    //Force WP user login sync is enabled. Show error and exit out since the WP user login failed.
+                    $error_msg = SwpmUtils::_("Error! This site has the force WP user login feature enabled in the settings. We could not find a WP user record for the given username: ") . $username;
+                    $error_msg .= "<br /><br />" . SwpmUtils::_("This error is triggered when a member account doesn't have a corresponding WP user account. So the plugin fails to log the user into the WP User system.");
+                    $error_msg .= "<br /><br />" . SwpmUtils::_("Contact the site admin and request them to check your username in the WP Users menu to see what happened with the WP user entry of your account.");
+                    $error_msg .= "<br /><br />" . SwpmUtils::_("The site admin can disable the Force WP User Synchronization feature in the settings to disable this feature and this error will go away.");
+                    $error_msg .= "<br /><br />" . SwpmUtils::_("You can use the back button of your browser to go back to the site.");
+                    wp_die($error_msg);
+                }
+            }
+        }
+
+        $proceed_after_auth = apply_filters('swpm_login_auth_completed_filter', true);
+
+        if (!$proceed_after_auth) {
+            $auth = SwpmAuth::get_instance();
+            $auth->logout();
+            return;
+        }
+
+        SwpmLog::log_auth_debug("Triggering swpm_after_login hook.", true);
+        do_action('swpm_after_login');
+        if (!SwpmUtils::is_ajax()) {
+            $redirect_url = apply_filters('swpm_after_login_redirect_url', SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL);
+            wp_redirect($redirect_url);
+            exit(0);
+        }
+    }
+
+    public function swpm_do_user_logout() {
+        if (is_user_logged_in()) {
+            wp_logout();
+            wp_set_current_user(0);
+        }
+    }
+
+    /* This function can be used to authenticate a member using currently logged in wp user. */
+    public function set_current_user_handler() {
+        $auth = SwpmAuth::get_instance();
+        if ($auth->is_logged_in()) {
+            return;
+        }
+        $user = wp_get_current_user();
+        if (empty($user) || $user->ID === 0) {
+            return false;
+        }
+        SwpmLog::log_auth_debug('set_current_user action. Attempting to login user ' . $user->user_login, true);
+        //remove hook in order for it to not be called several times in the process
+        remove_action('set_current_user', array($this, 'set_current_user_handler'));
+        $auth->login_to_swpm_using_wp_user($user);
+    }
+
+    /* Used to log the user into SWPM system using the wp_login hook. Some social plugins use this hook to handle the login */
+    public function wp_login_hook_handler($user_login, $user){
+        SwpmLog::log_auth_debug('wp_login hook triggered. Username: ' . $user_login, true);
+        $auth = SwpmAuth::get_instance();
+        if ($auth->is_logged_in()) {
+            //User is already logged-in. Nothing to do.
+            return;
+        }
+        $auth->login_to_swpm_using_wp_user($user);
+    }
+
+    public function wp_authenticate_handler($username, $password) {
+
+        $auth = SwpmAuth::get_instance();
+        if (($auth->is_logged_in() && ($auth->userData->user_name == $username))) {
+            SwpmLog::log_auth_debug('wp_authenticate action. User with username: ' . $username . ' is already logged in.', true);
+            return;
+        }
+        if (!empty($username)) {
+            SwpmLog::log_auth_debug('wp_authenticate action. Handling login for username: ' . $username, true);
+            $auth->login($username, $password, true);
+        } else {
+            //empty username can mean some plugin trying to login WP user using its own methods.
+            //Let's add hook for set_current_user action and let it handle the login if needed.
+            SwpmLog::log_auth_debug('wp_authenticate action. Empty username provided. Adding set_current_username hook to catch potential login attempt.', true);
+            add_action('set_current_user', array($this, 'set_current_user_handler'));
+        }
+    }
+
+    public function login() {
+        ob_start();
+        $auth = SwpmAuth::get_instance();
+        if ($auth->is_logged_in()) {
+            //Load the template for logged-in member
+            SwpmUtilsTemplate::swpm_load_template('loggedin.php', false);
+        } else {
+            //Load the login widget template
+            SwpmUtilsTemplate::swpm_load_template('login.php', false);
+        }
+        return ob_get_clean();
+    }
+
+    public function wp_logout() {
+        $auth = SwpmAuth::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']));
+    }
+
+    function swpm_handle_wp_user_registration($user_id) {
+
+        $swpm_settings_obj = SwpmSettings::get_instance();
+        $enable_auto_create_swpm_members = $swpm_settings_obj->get_value('enable-auto-create-swpm-members');
+        $default_level = $swpm_settings_obj->get_value('auto-create-default-membership-level');
+        $default_ac_status = $swpm_settings_obj->get_value('auto-create-default-account-status');
+
+        if (empty($enable_auto_create_swpm_members)) {
+            return;
+        }
+        if (empty($default_level)) {
+            return;
+        }
+
+        $user_info = get_userdata($user_id);
+        if (SwpmMemberUtils::get_user_by_user_name($user_info->user_login)) {
+            SwpmLog::log_simple_debug("swpm_handle_wp_user_registration() - SWPM member account with this username already exists! No new account will be created for this user.", false);
+            return;
+        }
+        if (SwpmMemberUtils::get_user_by_email($user_info->user_email)) {
+            SwpmLog::log_simple_debug("swpm_handle_wp_user_registration() - SWPM member account with this email already exists! No new account will be created for this user.", false);
+            return;
+        }
+        $fields = array();
+        $fields['user_name'] = $user_info->user_login;
+        $fields['password'] = $user_info->user_pass;
+        $fields['email'] = $user_info->user_email;
+        $fields['first_name'] = $user_info->first_name;
+        $fields['last_name'] = $user_info->last_name;
+        $fields['membership_level'] = $default_level;
+        $fields['member_since'] = SwpmUtils::get_current_date_in_wp_zone();
+        $fields['account_state'] = $default_ac_status;
+        $fields['subscription_starts'] = SwpmUtils::get_current_date_in_wp_zone();
+        SwpmMemberUtils::create_swpm_member_entry_from_array_data($fields);
+    }
+
+    public function reset() {
+        $succeeded = $this->notices();
+        if ($succeeded) {
+            return '';
+        }
+        ob_start();
+        //Load the forgot password template
+        SwpmUtilsTemplate::swpm_load_template('forgot_password.php', false);
+        return ob_get_clean();
+    }
+
+    public function profile_form() {
+        $auth = SwpmAuth::get_instance();
+        $this->notices();
+        if ($auth->is_logged_in()) {
+            $out = apply_filters('swpm_profile_form_override', '');
+            if (!empty($out)) {
+                return $out;
+            }
+            ob_start();
+            //Load the edit profile template
+            SwpmUtilsTemplate::swpm_load_template('edit.php', false);
+            return ob_get_clean();
+        }
+        return SwpmUtils::_('You are not logged in.');
+    }
+
+    /* If any message/notice was set during the execution then this function will output that message */
+
+    public function notices() {
+        $message = SwpmTransfer::get_instance()->get('status');
+        $succeeded = false;
+        if (empty($message)) {
+            return false;
+        }
+        if ($message['succeeded']) {
+            echo "<div id='swpm_message' class='swpm_success'>";
+            $succeeded = true;
+        } else {
+            echo "<div id='swpm_message' class='swpm_error'>";
+        }
+        echo $message['message'];
+        $extra = isset($message['extra']) ? $message['extra'] : array();
+        if (is_string($extra)) {
+            echo $extra;
+        } else if (is_array($extra)) {
+            echo '<ul>';
+            foreach ($extra as $key => $value) {
+                echo '<li>' . $value . '</li>';
+            }
+            echo '</ul>';
+        }
+        echo "</div>";
+        if (isset($message['pass_reset_sent'])) {
+            $succeeded = true;
+        }
+        return $succeeded;
+    }
+
+    /*
+     * This function is hooked to WordPress's admin_notices action hook
+     * It is used to show any plugin specific notices/warnings in the admin interface
+     */
+
+    public function do_admin_notices() {
+        $this->notices(); //Show any execution specific notices in the admin interface.
+        //Show any other general warnings/notices to the admin.
+        if (SwpmMiscUtils::is_swpm_admin_page()) {
+            //we are in an admin page for SWPM plugin.
+
+            $msg = '';
+            //Show notice if running in sandbox mode.
+            $settings = SwpmSettings::get_instance();
+            $sandbox_enabled = $settings->get_value('enable-sandbox-testing');
+            if ($sandbox_enabled) {
+                $msg .= '<p>' . SwpmUtils::_('You have the sandbox payment mode enabled in plugin settings. Make sure to turn off the sandbox mode when you want to do live transactions.') . '</p>';
+            }
+
+            if (!empty($msg)) {//Show warning messages if any.
+                echo '<div id="message" class="error">';
+                echo $msg;
+                echo '</div>';
+            }
+        }
+    }
+
+    public function meta_box() {
+        if (function_exists('add_meta_box')) {
+            $post_types = get_post_types();
+            foreach ($post_types as $post_type => $post_type) {
+                add_meta_box('swpm_sectionid', __('Simple WP Membership Protection', 'simple-membership'), array(&$this, 'inner_custom_box'), $post_type, 'advanced');
+            }
+        } else {//older version doesn't have custom post type so modification isn't needed.
+            add_action('dbx_post_advanced', array(&$this, 'show_old_custom_box'));
+            add_action('dbx_page_advanced', array(&$this, 'show_old_custom_box'));
+        }
+    }
+
+    public function show_old_custom_box() {
+        echo '<div class="dbx-b-ox-wrapper">' . "\n";
+        echo '<fieldset id="swpm_fieldsetid" class="dbx-box">' . "\n";
+        echo '<div class="dbx-h-andle-wrapper"><h3 class="dbx-handle">' .
+        __('Simple Membership Protection options', 'simple-membership') . "</h3></div>";
+        echo '<div class="dbx-c-ontent-wrapper"><div class="dbx-content">';
+        // output editing form
+        $this->inner_custom_box();
+        // end wrapper
+        echo "</div></div></fieldset></div>\n";
+    }
+
+    public function inner_custom_box() {
+        global $post, $wpdb;
+        $id = $post->ID;
+        $protection_obj = SwpmProtection::get_instance();
+        $is_protected = $protection_obj->is_protected($id);
+
+        //Nonce input
+        echo '<input type="hidden" name="swpm_post_protection_box_nonce" value="' . wp_create_nonce('swpm_post_protection_box_nonce_action') . '" />';
+
+        // The actual fields for data entry
+        echo '<h4>' . __("Do you want to protect this content?", 'simple-membership') . '</h4>';
+        echo '<input type="radio" ' . ((!$is_protected) ? 'checked' : "") . '  name="swpm_protect_post" value="1" /> ' . SwpmUtils::_('No, Do not protect this content.') . '<br/>';
+        echo '<input type="radio" ' . (($is_protected) ? 'checked' : "") . '  name="swpm_protect_post" value="2" /> ' . SwpmUtils::_('Yes, Protect this content.') . '<br/>';
+        echo $protection_obj->get_last_message();
+
+        echo '<h4>' . __("Select the membership level that can access this content:", 'simple-membership') . "</h4>";
+        $query = "SELECT * FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE  id !=1 ";
+        $levels = $wpdb->get_results($query, ARRAY_A);
+        foreach ($levels as $level) {
+            echo '<input type="checkbox" ' . (SwpmPermission::get_instance($level['id'])->is_permitted($id) ? "checked='checked'" : "") .
+            ' name="swpm_protection_level[' . $level['id'] . ']" value="' . $level['id'] . '" /> ' . $level['alias'] . "<br/>";
+        }
+    }
+
+    public function save_postdata($post_id) {
+        global $wpdb;
+        $post_type = filter_input(INPUT_POST, 'post_type');
+        $swpm_protect_post = filter_input(INPUT_POST, 'swpm_protect_post');
+
+        if (wp_is_post_revision($post_id)) {
+            return;
+        }
+        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
+            return $post_id;
+        }
+
+        //Check nonce
+        $swpm_post_protection_box_nonce = filter_input(INPUT_POST, 'swpm_post_protection_box_nonce');
+        if (!wp_verify_nonce($swpm_post_protection_box_nonce, 'swpm_post_protection_box_nonce_action')) {
+            //Nonce check failed.
+            return $post_id;
+        }
+
+        if ('page' == $post_type) {
+            if (!current_user_can('edit_page', $post_id)) {
+                return $post_id;
+            }
+        } else {
+            if (!current_user_can('edit_post', $post_id)) {
+                return $post_id;
+            }
+        }
+        if (empty($swpm_protect_post)) {
+            return;
+        }
+        // OK, we're authenticated: we need to find and save the data
+        $isprotected = ($swpm_protect_post == 2);
+        $args = array('swpm_protection_level' => array(
+                'filter' => FILTER_VALIDATE_INT,
+                'flags' => FILTER_REQUIRE_ARRAY,
+        ));
+        $swpm_protection_level = filter_input_array(INPUT_POST, $args);
+        $swpm_protection_level = $swpm_protection_level['swpm_protection_level'];
+        if (!empty($post_type)) {
+            if ($isprotected) {
+                SwpmProtection::get_instance()->apply(array($post_id), $post_type);
+            } else {
+                SwpmProtection::get_instance()->remove(array($post_id), $post_type);
+            }
+            SwpmProtection::get_instance()->save();
+            $query = "SELECT id FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE  id !=1 ";
+            $level_ids = $wpdb->get_col($query);
+            foreach ($level_ids as $level) {
+                if (isset($swpm_protection_level[$level])) {
+                    SwpmPermission::get_instance($level)->apply(array($post_id), $post_type)->save();
+                } else {
+                    SwpmPermission::get_instance($level)->remove(array($post_id), $post_type)->save();
+                }
+            }
+        }
+        $enable_protection = array();
+        $enable_protection['protect'] = $swpm_protect_post;
+        $enable_protection['level'] = $swpm_protection_level;
+        return $enable_protection;
+    }
+
+    public function filter_comment($content) {
+        if (is_admin()) {
+            //Do not apply filtering for admin side viewing
+            return $content;
+        }
+
+        $acl = SwpmAccessControl::get_instance();
+        global $comment;
+        return $acl->filter_comment($comment, $content);
+    }
+
+    public function filter_content($content) {
+        if (is_preview() || is_admin()) {
+            //If the user is logged-in as an admin user then do not apply filtering for admin side viewing or preview page viewing.
+            if ( current_user_can('administrator') ){
+                //The user is logged in as admin in this browser.
+                return $content;
+            }
+        }
+        $acl = SwpmAccessControl::get_instance();
+        global $post;
+        return $acl->filter_post($post, $content);
+    }
+
+    public function init_hook() {
+        $init_tasks = new SwpmInitTimeTasks();
+        $init_tasks->do_init_tasks();
+    }
+
+    public function handle_wp_loaded_tasks() {
+        $wp_loaded_tasks = new SwpmWpLoadedTasks();
+        $wp_loaded_tasks->do_wp_loaded_tasks();
+    }
+
+    public function admin_library() {
+        //Only loaded on selective swpm admin menu page rendering.
+        $this->common_library();
+        wp_enqueue_script('password-strength-meter');
+        wp_enqueue_script('swpm.password-meter', SIMPLE_WP_MEMBERSHIP_URL . '/js/swpm.password-meter.js', array('jquery'), SIMPLE_WP_MEMBERSHIP_VER);
+        //jQuery UI style
+        wp_register_style('swpm-jquery-ui', SIMPLE_WP_MEMBERSHIP_URL . '/css/jquery-ui.min.css', array(), SIMPLE_WP_MEMBERSHIP_VER);
+        wp_enqueue_style('swpm-jquery-ui');
+        wp_enqueue_script('jquery-ui-datepicker');
+        $settings = array('statusChangeEmailHead' => SwpmSettings::get_instance()->get_value('account-change-email-subject'),
+            'statusChangeEmailBody' => SwpmSettings::get_instance()->get_value('account-change-email-body'));
+        wp_localize_script('swpm.password-meter', 'SwpmSettings', $settings);
+    }
+
+    public function front_library() {
+        $this->common_library();
+    }
+
+    private function common_library() {
+        wp_enqueue_script('jquery');
+        wp_enqueue_style('swpm.common', SIMPLE_WP_MEMBERSHIP_URL . '/css/swpm.common.css', array(), SIMPLE_WP_MEMBERSHIP_VER);
+
+        //In order to not clog WP with scripts and styles we're only using with forms, let's just register those for now
+        //Scripts will be queued when forms are actually displayed
+        wp_register_style('validationEngine.jquery', SIMPLE_WP_MEMBERSHIP_URL . '/css/validationEngine.jquery.css', array(), SIMPLE_WP_MEMBERSHIP_VER);
+        wp_register_script('jquery.validationEngine', SIMPLE_WP_MEMBERSHIP_URL . '/js/jquery.validationEngine.js', array('jquery'), SIMPLE_WP_MEMBERSHIP_VER);
+        wp_register_script('jquery.validationEngine-en', SIMPLE_WP_MEMBERSHIP_URL . '/js/jquery.validationEngine-en.js', array('jquery'), SIMPLE_WP_MEMBERSHIP_VER);
+        wp_register_script('swpm.validationEngine-localization', SIMPLE_WP_MEMBERSHIP_URL . '/js/swpm.validationEngine-localization.js', array('jquery'), SIMPLE_WP_MEMBERSHIP_VER);
+    }
+
+    public static function enqueue_validation_scripts($add_params = array()) {
+        //Localization for jquery.validationEngine
+        //This array will be merged with $.validationEngineLanguage.allRules object from jquery.validationEngine-en.js file
+        $loc_data = array(
+            'ajaxUserCall' => array(
+                'url' => admin_url('admin-ajax.php'),
+                'alertTextLoad' => '* ' . SwpmUtils::_('Validating, please wait'),
+            ),
+            'ajaxEmailCall' => array(
+                'url' => admin_url('admin-ajax.php'),
+                'alertTextLoad' => '* ' . SwpmUtils::_('Validating, please wait'),
+            ),
+            'email' => array(
+                'alertText' => '* ' . SwpmUtils::_('Invalid email address'),
+            ),
+            'required' => array(
+                'alertText' => '* ' . SwpmUtils::_('This field is required'),
+            ),
+            'strongPass' => array(
+                'alertText' => '* ' . SwpmUtils::_('Password must contain at least:').'<br>'.SwpmUtils::_('- a digit').'<br>'.SwpmUtils::_('- an uppercase letter').'<br>'.SwpmUtils::_('- a lowercase letter'),
+            ),
+            'SWPMUserName' => array(
+                'alertText' => '* ' . SwpmUtils::_('Invalid Username').'<br>'.SwpmUtils::_('Usernames can only contain: letters, numbers and .-_*@'),
+            ),
+            'minSize' => array(
+                'alertText' => '* ' . SwpmUtils::_('Minimum '),
+                'alertText2' => SwpmUtils::_(' characters required'),
+            ),
+            'noapostrophe' => array(
+                'alertText' => '* ' . SwpmUtils::_('Apostrophe character is not allowed'),
+            ),
+        );
+
+        $nonce=wp_create_nonce( 'swpm-rego-form-ajax-nonce' );
+
+        if ($add_params) {
+            // Additional parameters should be added to the array, replacing existing ones
+            if (isset($add_params['ajaxEmailCall'])) {
+                if (isset($add_params['ajaxEmailCall']['extraData'])) {
+                    $add_params['ajaxEmailCall']['extraData'].='&nonce='.$nonce;
+                }
+            }
+            $loc_data = array_replace_recursive($add_params, $loc_data);
+        }
+
+        wp_localize_script('swpm.validationEngine-localization', 'swpm_validationEngine_localization', $loc_data);
+
+        wp_localize_script('jquery.validationEngine-en', 'swpmRegForm', array('nonce' => $nonce));
+
+        wp_enqueue_style('validationEngine.jquery');
+        wp_enqueue_script('jquery.validationEngine');
+        wp_enqueue_script('jquery.validationEngine-en');
+        wp_enqueue_script('swpm.validationEngine-localization');
+    }
+
+    public function registration_form($atts) {
+        $succeeded = $this->notices();
+        if ($succeeded) {
+            return;
+        }
+        $is_free = SwpmSettings::get_instance()->get_value('enable-free-membership');
+        $free_level = absint(SwpmSettings::get_instance()->get_value('free-membership-id'));
+        $level = isset($atts['level']) ? absint($atts['level']) : ($is_free ? $free_level : null);
+        return SwpmFrontRegistration::get_instance()->regigstration_ui($level);
+    }
+
+    public function menu() {
+        $menu_parent_slug = 'simple_wp_membership';
+
+        add_menu_page(__("WP Membership", 'simple-membership'), __("WP Membership", 'simple-membership'), SWPM_MANAGEMENT_PERMISSION, $menu_parent_slug, array(&$this, "admin_members_menu"), 'dashicons-id');
+        add_submenu_page($menu_parent_slug, __("Members", 'simple-membership'), __('Members', 'simple-membership'), SWPM_MANAGEMENT_PERMISSION, 'simple_wp_membership', array(&$this, "admin_members_menu"));
+        add_submenu_page($menu_parent_slug, __("Membership Levels", 'simple-membership'), __("Membership Levels", 'simple-membership'), SWPM_MANAGEMENT_PERMISSION, 'simple_wp_membership_levels', array(&$this, "admin_membership_levels_menu"));
+        add_submenu_page($menu_parent_slug, __("Settings", 'simple-membership'), __("Settings", 'simple-membership'), SWPM_MANAGEMENT_PERMISSION, 'simple_wp_membership_settings', array(&$this, "admin_settings_menu"));
+        add_submenu_page($menu_parent_slug, __("Payments", 'simple-membership'), __("Payments", 'simple-membership'), SWPM_MANAGEMENT_PERMISSION, 'simple_wp_membership_payments', array(&$this, "admin_payments_menu"));
+        add_submenu_page($menu_parent_slug, __("Add-ons", 'simple-membership'), __("Add-ons", 'simple-membership'), SWPM_MANAGEMENT_PERMISSION, 'simple_wp_membership_addons', array(&$this, "admin_add_ons_menu"));
+
+        do_action('swpm_after_main_admin_menu', $menu_parent_slug);
+
+        $this->meta_box();
+    }
+
+    /* Render the members menu in admin dashboard */
+
+    public function admin_members_menu() {
+        include_once(SIMPLE_WP_MEMBERSHIP_PATH . 'classes/class.swpm-members.php');
+        $members = new SwpmMembers();
+        $members->handle_main_members_admin_menu();
+    }
+
+    /* Render the membership levels menu in admin dashboard */
+
+    public function admin_membership_levels_menu() {
+        include_once(SIMPLE_WP_MEMBERSHIP_PATH . 'classes/class.swpm-membership-levels.php');
+        $levels = new SwpmMembershipLevels();
+        $levels->handle_main_membership_level_admin_menu();
+    }
+
+    /* Render the settings menu in admin dashboard */
+
+    public function admin_settings_menu() {
+        $settings = SwpmSettings::get_instance();
+        $settings->handle_main_settings_admin_menu();
+    }
+
+    public function admin_payments_menu() {
+        include_once(SIMPLE_WP_MEMBERSHIP_PATH . 'classes/admin-includes/class.swpm-payments-admin-menu.php');
+        $payments_admin = new SwpmPaymentsAdminMenu();
+        $payments_admin->handle_main_payments_admin_menu();
+    }
+
+    public function admin_add_ons_menu() {
+        include(SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_add_ons_page.php');
+    }
+
+    public function plugins_loaded() {
+        //Runs when plugins_loaded action gets fired
+        if (is_admin()) {
+            //Check and run DB upgrade operation (if needed)
+            if (get_option('swpm_db_version') != SIMPLE_WP_MEMBERSHIP_DB_VER) {
+                include_once('class.swpm-installation.php');
+                SwpmInstallation::run_safe_installer();
+            }
+        }
+    }
+
+    public static function activate() {
+        wp_schedule_event(time(), 'daily', 'swpm_account_status_event');
+        wp_schedule_event(time(), 'daily', 'swpm_delete_pending_account_event');
+        include_once('class.swpm-installation.php');
+        SwpmInstallation::run_safe_installer();
+    }
+
+    public static function deactivate() {
+        wp_clear_scheduled_hook('swpm_account_status_event');
+        wp_clear_scheduled_hook('swpm_delete_pending_account_event');
+    }
+
+}

+ 298 - 0
simple-membership/classes/class.swpm-access-control.php

@@ -0,0 +1,298 @@
+<?php
+
+class SwpmAccessControl {
+    
+    private $lastError;
+    private $moretags;
+    private static $_this;
+    
+    private function __construct(){
+        $this->lastError = '';
+        $this->moretags  = array();
+    }
+    
+    public static function get_instance(){
+        self::$_this = empty(self::$_this)? new SwpmAccessControl():self::$_this;
+        return self::$_this;
+    }
+
+    public function can_i_read_post($post){
+        if (!is_a($post, 'WP_Post')) {
+            //This is not a WP_Post object. So we don't want to handle it in our plugin.
+            return true;
+        }
+        
+        $id = $post->ID;
+        $this->lastError = '';
+        $auth = SwpmAuth::get_instance();
+        
+        //$protect_everything = SwpmSettings::get_instance()->get_value('protect-everything');
+        //if(!empty($protect_everything)){ 
+            //Protect everything is enabled.
+            //TODO - This feature is not implemented yet.
+        //}
+        
+        //Check if this is a protected post.
+        $protected = SwpmProtection::get_instance();
+        if (!$protected->is_protected($id)){ 
+            //This is a totally unprotected post. So everyone has access to it.
+            return true;
+        }
+        
+        /*** At this point, we have a protected post. So we need to check if this user can view this post. ***/
+        
+        //Check if the user is logged in.
+        if(!$auth->is_logged_in()){
+            //This user is not logged into the site. No access to this protected post.
+            $text = SwpmUtils::_('You need to login to view this content. ') . SwpmMiscUtils::get_login_link();
+            $error_msg = '<div class="swpm-post-not-logged-in-msg">'.$text.'</div>';
+            $this->lastError = apply_filters('swpm_not_logged_in_post_msg', $error_msg);
+            return false;            
+        }
+
+        //Check if the account is expired
+        if ($auth->is_expired_account()){
+            //This user's account is expired. No access to this post. Show account expiry message.
+            $text = SwpmUtils::_('Your account has expired. ') .  SwpmMiscUtils::get_renewal_link();
+            $error_msg = '<div class="swpm-post-account-expired-msg swpm-yellow-box">'.$text.'</div>';
+            $this->lastError = apply_filters('swpm_account_expired_msg', $error_msg);
+            return false;                        
+        }
+        
+        //Check older post protection addon settings (if being used on this site).
+        $protect_older_posts = apply_filters('swpm_should_protect_older_post', false, $id);
+        if ($protect_older_posts){
+            //This post falls under the older post protection condition. No access to it.
+            $text = SwpmUtils::_('This content can only be viewed by members who joined on or before ') . SwpmUtils::get_formatted_and_translated_date_according_to_wp_settings($post->post_date);
+            $error_msg = '<div class="swpm-post-older-post-msg">'.$text.'</div>';
+            $this->lastError = apply_filters ('swpm_restricted_post_msg_older_post', $error_msg);
+            return false;
+        }
+        
+        //Check if this user's membership level has access to this post
+        $permission = SwpmPermission::get_instance($auth->get('membership_level'));
+        if($permission->is_permitted($id)) {
+            //This user's membership level has access to it. Show this post to this user.
+            return true;
+        } else {
+            //User's level DOES NOT have access to this post.
+            $text = SwpmUtils::_('This content is not permitted for your membership level.');
+            $error_msg = '<div class="swpm-post-no-access-msg">'.$text.'</div>';
+            $this->lastError = apply_filters ('swpm_restricted_post_msg', $error_msg);
+            return false;
+        }
+        
+        //All checks have passed. Show this post to the user.
+        return true;
+    }
+    
+    public function can_i_read_comment($comment){
+        if (!is_a($comment, 'WP_Comment')) {
+            //This is not a valid WP_Comment object. So we don't want to handle it in our plugin.
+            return true;
+        }
+
+        $id = $comment->comment_ID;
+        $post_id = $comment->comment_post_ID;
+        $post = get_post($post_id);
+        $this->lastError = '';
+        $auth = SwpmAuth::get_instance();
+        
+        //Check if everything protected settings is on.
+        //$protect_everything = SwpmSettings::get_instance()->get_value('protect-everything');
+        //if(!empty($protect_everything)){ 
+            //Everything is protected by default.
+            //TODO - This feature is currently not implemented.
+        //}
+        
+        //Check if the post (that this comment belongs to) is protected.
+        $protected = SwpmProtection::get_instance();
+        if (!$protected->is_protected($post_id)){ 
+            //The post of this comment is not protected. So this is an unprotected comment. Show it to everyone.
+            return true;
+        }
+        
+        /*** At this point, we have a protected comment. So we need to check if this user can view this comment. ***/
+        
+        //Check if the user is logged-in as a member.
+        if(!$auth->is_logged_in()){
+            //User is not logged-in. Not allowed to see this protected comment.
+            $error_msg = '<div class="swpm-comment-not-logged-in">' . SwpmUtils::_("You need to login to view this content. ") . '</div>';
+            $this->lastError = apply_filters('swpm_not_logged_in_comment_msg', $error_msg);
+            return false;            
+        }
+
+        //Check if member account is expired.
+        if ($auth->is_expired_account()){
+            //This user's account is expired. Not allowed to see this comment. Show account expiry notice also.
+            $text = SwpmUtils::_('Your account has expired. ') .  SwpmMiscUtils::get_renewal_link();
+            $error_msg = '<div class="swpm-comment-account-expired-msg swpm-yellow-box">'.$text.'</div>';
+            $this->lastError = apply_filters('swpm_account_expired_msg', $error_msg);
+            return false;                        
+        }
+        
+        //Check if older post protection addon is active and protection according to it's settings.
+        $protect_older_posts = apply_filters('swpm_should_protect_older_post', false, $post_id);
+        if ($protect_older_posts){
+            //This comment is protected due to the older post protection addon settings configuration.
+            $text = SwpmUtils::_('This content can only be viewed by members who joined on or before ') . SwpmUtils::get_formatted_and_translated_date_according_to_wp_settings($post->post_date);
+            $error_msg = '<div class="swpm-comment-older-post-msg">'.$text.'</div>';
+            $this->lastError = apply_filters ('swpm_restricted_comment_older_post', $error_msg);
+            return false;
+        }
+        
+        //Check if this member can view this comment based on his membership level
+        $permission = SwpmPermission::get_instance($auth->get('membership_level'));
+        if(!$permission->is_permitted($post_id)) {
+            //This member's membership level doesn't have access to this comment's post. Not allowed to see this comment.
+            $error_msg = '<div class="swpm-comment-no-access-msg">' . SwpmUtils::_('This content is not permitted for your membership level.').'</div>';
+            $this->lastError = apply_filters ('swpm_restricted_comment_msg', $error_msg);
+            return false;
+        }
+        
+        //All checks have passed at this stage. Show this comment to this user.
+        return true;
+    }
+
+    public function filter_post($post,$content){
+        if (!is_a($post, 'WP_Post')) {
+            //This is not a WP_Post object. So we don't want to handle it in our plugin.
+            return $content;
+            //return SwpmUtils::_('Error! $post is not a valid WP_Post object.');
+        }
+        
+        if(self::expired_user_has_access_to_this_page()) {
+            return $content;//An expired user is viewing this page and it is a system page, so allow access.
+        }
+        
+        if(SwpmUtils::is_first_click_free($content)) {
+            return $content;//First click free is true, so allow access.
+        }
+
+        if($this->can_i_read_post($post)) {
+            return $content;//This member has access to this post, so allow access.
+        } 
+
+        //Check and apply more tag protection.
+        $more_tag_protection_value = $this->check_and_apply_more_tag_protection($post, $content);
+        if(!empty($more_tag_protection_value)){
+            //More tag protection was found in the post. Return the modified $content.
+            return $more_tag_protection_value;
+        }
+        
+        //Return whatever the result is from calling the earlier protection check functions.
+        return $this->lastError;
+    }
+    
+    public function check_and_apply_more_tag_protection($post, $content){
+        //More tag protection is checked after all the OTHER protections have alrady been checked. 
+        //So if a valid logged-in member is accessing a post he has access to then this code won't execute.
+        
+        //Check if more tag protection is enabled.
+        $moretag = SwpmSettings::get_instance()->get_value('enable-moretag');
+        if (empty($moretag)){
+            //More tag protection is disabled in this site. So return empty string.
+            return '';
+        } else {
+            //More tag protection is enabled in this site. Need to check the post segments to see if there is content after more tag.
+            $post_segments = explode( '<!--more-->', $post->post_content);
+            if (count($post_segments) >= 2){
+                //There is content after the more tag.
+                $auth = SwpmAuth::get_instance();
+                if(!$auth->is_logged_in()){
+                    //User is not logged-in. Need to show the login message after the more tag.
+                    $text = SwpmUtils::_("You need to login to view the rest of the content. ") . SwpmMiscUtils::get_login_link();
+                    $error_msg = '<div class="swpm-more-tag-not-logged-in swpm-margin-top-10">' . $text . '</div>';
+                    $more_tag_check_msg = apply_filters('swpm_not_logged_in_more_tag_msg', $error_msg);
+                } else {
+                    //The user is logged in. 
+                    //Lets check if the user's account is expired.
+                    if ($auth->is_expired_account()){
+                        //This user's account is expired. Not allowed to see this post. Show account expiry notice also.
+                        $text = SwpmUtils::_('Your account has expired. ') .  SwpmMiscUtils::get_renewal_link();
+                        $error_msg = '<div class="swpm-more-tag-account-expired-msg swpm-yellow-box">'.$text.'</div>';
+                        $more_tag_check_msg = apply_filters('swpm_account_expired_more_tag_msg', $error_msg);
+                    } else {
+                        //At this stage, the user does not have permission to view the content after the more tag.
+                        $text = SwpmUtils::_(" The rest of the content is not permitted for your membership level.");
+                        $error_msg = '<div class="swpm-more-tag-restricted-msg swpm-margin-top-10">' . $text . '</div>';
+                        $more_tag_check_msg = apply_filters ('swpm_restricted_more_tag_msg', $error_msg);
+                    }
+                }
+
+                $filtered_before_more_content = SwpmMiscUtils::format_raw_content_for_front_end_display($post_segments[0]);
+                $new_post_content = $filtered_before_more_content . $more_tag_check_msg;
+                return $new_post_content;  
+                
+            }//End of segment count condition check.
+        }//End of more tag enabled condition check.
+        
+        //More tag protection not applicable for this post. Return empty string.
+        return '';
+    }
+    
+    public function filter_comment($comment, $content){
+        if($this->can_i_read_comment($comment)) {
+            //This user has access to this comment.
+            return $content;
+        }
+        return $this->lastError;
+    }
+    
+    public function why(){
+        return $this->lastError;
+    }
+    
+    /*
+     * This function checks if the current user is an expired user and has access to the system page content (if the current URL is a system page).
+     */
+    public static function expired_user_has_access_to_this_page(){
+        $auth = SwpmAuth::get_instance();
+        
+        //Check if the user is logged-into the site.
+        if(!$auth->is_logged_in()){
+            //Anonymous user. No access. No need to check anything else.
+            return false;
+        }
+        
+        //Check if account is expired.
+        if (!$auth->is_expired_account()){
+            //This users account is not expired. No need to check anything else.
+            return false;
+        }
+        
+        /*** We have a expired member. Lets check if he is viewing a page that is a core system used URL. ***/
+        if (self::is_current_url_a_system_page()){ 
+            //Allow this expired user to view this post/page content since this is a core system page.
+            return true;
+        }
+        
+        //Not a system used page. So the expired user has no access to this page.
+        return false;
+    }
+    
+    /*
+     * This function checks if the current page being viewed is one of the system used URLs
+     */
+    public static function is_current_url_a_system_page(){
+        $current_page_url = SwpmMiscUtils::get_current_page_url();
+        
+        //Check if the current page is the membership renewal page.
+        $renewal_url = SwpmSettings::get_instance()->get_value('renewal-page-url');        
+        if (empty($renewal_url)) {return false;}
+        if (SwpmMiscUtils::compare_url($renewal_url, $current_page_url)) {return true;}
+
+        //Check if the current page is the membership logn page.
+        $login_page_url = SwpmSettings::get_instance()->get_value('login-page-url');
+        if (empty($login_page_url)) {return false;}
+        if (SwpmMiscUtils::compare_url($login_page_url, $current_page_url)) {return true;}
+
+        //Check if the current page is the membership join page.
+        $registration_page_url = SwpmSettings::get_instance()->get_value('registration-page-url');
+        if (empty($registration_page_url)) {return false;}
+        if (SwpmMiscUtils::compare_url($registration_page_url, $current_page_url)) {return true;}
+        
+        return false;
+    }
+    
+}

+ 186 - 0
simple-membership/classes/class.swpm-admin-registration.php

@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * Description of BAdminRegistration
+ *
+ */
+class SwpmAdminRegistration extends SwpmRegistration {
+
+	public static function get_instance() {
+		self::$_intance = empty( self::$_intance ) ? new SwpmAdminRegistration() : self::$_intance;
+		return self::$_intance;
+	}
+
+	public function show_form() {
+
+	}
+
+	public function register_admin_end() {
+		//Check we are on the admin end and user has management permission
+		SwpmMiscUtils::check_user_permission_and_is_admin( 'member creation by admin' );
+
+		//Check nonce
+		if ( ! isset( $_POST['_wpnonce_create_swpmuser_admin_end'] ) || ! wp_verify_nonce( $_POST['_wpnonce_create_swpmuser_admin_end'], 'create_swpmuser_admin_end' ) ) {
+			//Nonce check failed.
+			wp_die( SwpmUtils::_( 'Error! Nonce verification failed for user registration from admin end.' ) );
+		}
+
+		global $wpdb;
+		$member = SwpmTransfer::$default_fields;
+		$form   = new SwpmForm( $member );
+		if ( $form->is_valid() ) {
+			$member_info = $form->get_sanitized_member_form_data();
+
+                        //First, check if email or username belongs to an existing admin user. Bail if it does.
+                        SwpmMemberUtils::check_and_die_if_email_belongs_to_admin_user($member_info['email']);
+                        SwpmMemberUtils::check_and_die_if_username_belongs_to_admin_user($member_info['user_name']);
+
+			$account_status = SwpmSettings::get_instance()->get_value( 'default-account-status', 'active' );
+			$member_info['account_state'] = $account_status;
+			$plain_password = $member_info['plain_password'];
+			unset( $member_info['plain_password'] );
+                        //Create SWPM member entry
+			$wpdb->insert( $wpdb->prefix . 'swpm_members_tbl', $member_info );
+
+			//Register to WordPress
+			$query = $wpdb->prepare( 'SELECT role FROM ' . $wpdb->prefix . 'swpm_membership_tbl WHERE id = %d', $member_info['membership_level'] );
+			$wp_user_info = array();
+			$wp_user_info['user_nicename'] = implode( '-', explode( ' ', $member_info['user_name'] ) );
+			$wp_user_info['display_name']  = $member_info['user_name'];
+			$wp_user_info['user_email']    = $member_info['email'];
+			$wp_user_info['nickname']      = $member_info['user_name'];
+			if ( isset( $member_info['first_name'] ) ) {
+				$wp_user_info['first_name'] = $member_info['first_name'];
+			}
+			if ( isset( $member_info['last_name'] ) ) {
+				$wp_user_info['last_name'] = $member_info['last_name'];
+			}
+			$wp_user_info['user_login']      = $member_info['user_name'];
+			$wp_user_info['password']        = $plain_password;
+			$wp_user_info['role']            = $wpdb->get_var( $query );
+			$wp_user_info['user_registered'] = date( 'Y-m-d H:i:s' );
+			SwpmUtils::create_wp_user( $wp_user_info );
+			//End register to WordPress
+
+			//Send notification
+			$send_notification             = SwpmSettings::get_instance()->get_value( 'enable-notification-after-manual-user-add' );
+			$member_info['plain_password'] = $plain_password;
+			$this->member_info             = $member_info;
+			if ( ! empty( $send_notification ) ) {
+				$this->send_reg_email();
+			}
+
+			//Trigger action hook
+			do_action( 'swpm_admin_end_registration_complete_user_data', $member_info );
+
+			//Save success message
+			$message = array(
+				'succeeded' => true,
+				'message'   => '<p>' . SwpmUtils::_( 'Member record added successfully.' ) . '</p>',
+			);
+			SwpmTransfer::get_instance()->set( 'status', $message );
+			wp_redirect( 'admin.php?page=simple_wp_membership' );
+			exit( 0 );
+		}
+		$message = array(
+			'succeeded' => false,
+			'message'   => SwpmUtils::_( 'Please correct the following:' ),
+			'extra'     => $form->get_errors(),
+		);
+		SwpmTransfer::get_instance()->set( 'status', $message );
+	}
+
+	public function edit_admin_end( $id ) {
+		//Check we are on the admin end and user has management permission
+		SwpmMiscUtils::check_user_permission_and_is_admin( 'member edit by admin' );
+
+		//Check nonce
+		if ( ! isset( $_POST['_wpnonce_edit_swpmuser_admin_end'] ) || ! wp_verify_nonce( $_POST['_wpnonce_edit_swpmuser_admin_end'], 'edit_swpmuser_admin_end' ) ) {
+			//Nonce check failed.
+			wp_die( SwpmUtils::_( 'Error! Nonce verification failed for user edit from admin end.' ) );
+		}
+
+		global $wpdb;
+		$query  = $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'swpm_members_tbl WHERE member_id = %d', $id );
+		$member = $wpdb->get_row( $query, ARRAY_A );
+		// let's get previous membership level
+		$prev_level = false;
+		if ( $member ) {
+			$prev_level = $member['membership_level'];
+		}
+		$email_address = $member['email'];
+		$user_name     = $member['user_name'];
+		unset( $member['member_id'] );
+		unset( $member['user_name'] );
+		$form = new SwpmForm( $member );
+		if ( $form->is_valid() ) {
+			$member         = $form->get_sanitized_member_form_data();
+			$plain_password = isset( $member['plain_password'] ) ? $member['plain_password'] : '';
+			SwpmUtils::update_wp_user( $user_name, $member );
+			unset( $member['plain_password'] );
+			$wpdb->update( $wpdb->prefix . 'swpm_members_tbl', $member, array( 'member_id' => $id ) );
+			// set previous membership level
+			$member['prev_membership_level'] = $prev_level;
+			$member['member_id']             = $id;
+
+			 //Trigger action hook
+			do_action( 'swpm_admin_end_edit_complete_user_data', $member );
+
+			if ( $member['prev_membership_level'] != $member['membership_level'] ) {
+				do_action(
+					'swpm_membership_level_changed',
+					array(
+						'member_id'  => $id,
+						'from_level' => $member['prev_membership_level'],
+						'to_level'   => $member['membership_level'],
+					)
+				);
+			}
+
+			//Set messages
+			$message = array(
+				'succeeded' => true,
+				'message'   => '<p>Member profile updated successfully.</p>',
+			);
+			$error   = apply_filters( 'swpm_admin_edit_custom_fields', array(), $member + array( 'member_id' => $id ) );
+			if ( ! empty( $error ) ) {
+				$message = array(
+					'succeeded' => false,
+					'message'   => SwpmUtils::_( 'Please correct the following:' ),
+					'extra'     => $error,
+				);
+				SwpmTransfer::get_instance()->set( 'status', $message );
+				return;
+			}
+			SwpmTransfer::get_instance()->set( 'status', $message );
+			$send_notification = filter_input( INPUT_POST, 'account_status_change' );
+			if ( ! empty( $send_notification ) ) {
+				$settings     = SwpmSettings::get_instance();
+				$from_address = $settings->get_value( 'email-from' );
+				$headers      = 'From: ' . $from_address . "\r\n";
+				$subject      = filter_input( INPUT_POST, 'notificationmailhead' );
+				$body         = filter_input( INPUT_POST, 'notificationmailbody' );
+				$settings->set_value( 'account-change-email-body', $body )->set_value( 'account-change-email-subject', $subject )->save();
+				$member['login_link'] = $settings->get_value( 'login-page-url' );
+				$member['user_name']  = $user_name;
+				$member['password']   = empty( $plain_password ) ? SwpmUtils::_( 'Your current password' ) : $plain_password;
+				$values               = array_values( $member );
+				$keys                 = array_map( 'swpm_enclose_var', array_keys( $member ) );
+				$body                 = html_entity_decode( str_replace( $keys, $values, $body ) );
+				$subject              = apply_filters( 'swpm_email_account_status_change_subject', $subject );
+				$body                 = apply_filters( 'swpm_email_account_status_change_body', $body );
+				SwpmMiscUtils::mail( $email_address, $subject, $body, $headers );
+				SwpmLog::log_simple_debug( 'Notify email sent (after profile edit from admin side). Email sent to: ' . $email_address, true );
+			}
+			wp_redirect( 'admin.php?page=simple_wp_membership' );
+			exit( 0 );
+		}
+		$message = array(
+			'succeeded' => false,
+			'message'   => SwpmUtils::_( 'Please correct the following:' ),
+			'extra'     => $form->get_errors(),
+		);
+		SwpmTransfer::get_instance()->set( 'status', $message );
+	}
+
+}

+ 49 - 0
simple-membership/classes/class.swpm-ajax.php

@@ -0,0 +1,49 @@
+<?php
+/**
+ * Handles various AJAX calls
+ */
+
+class SwpmAjax {
+
+    public static function validate_email_ajax() {
+        global $wpdb;
+        $field_value = filter_input(INPUT_GET, 'fieldValue');
+        $field_id = filter_input(INPUT_GET, 'fieldId');
+        $member_id = filter_input(INPUT_GET, 'member_id');
+        if (!check_ajax_referer( 'swpm-rego-form-ajax-nonce', 'nonce', false )) {
+            echo '[ "' . $field_id .  '",false, "'.SwpmUtils::_('Nonce check failed. Please reload page.').'" ]' ;
+            exit;
+        }
+        if (!is_email($field_value)){
+            echo '[ "' . $field_id .  '",false, "'.SwpmUtils::_('Invalid Email Address').'" ]' ;
+            exit;
+        }
+        $table = $wpdb->prefix . "swpm_members_tbl";
+        $query = $wpdb->prepare("SELECT member_id FROM $table WHERE email = %s AND user_name != ''", $field_value);
+        $db_id = $wpdb->get_var($query) ;
+        $exists = ($db_id > 0) && $db_id != $member_id;
+        echo '[ "' . $field_id . (($exists) ? '",false, "&chi;&nbsp;'.SwpmUtils::_('Already taken').'"]' : '",true, "&radic;&nbsp;'.SwpmUtils::_('Available'). '"]');
+        exit;
+    }
+
+    public static function validate_user_name_ajax() {
+        global $wpdb;
+        $field_value = filter_input(INPUT_GET, 'fieldValue');
+        $field_id = filter_input(INPUT_GET, 'fieldId');
+        if (!check_ajax_referer( 'swpm-rego-form-ajax-nonce', 'nonce', false )) {
+            echo '[ "' . $field_id .  '",false, "'.SwpmUtils::_('Nonce check failed. Please reload page.').'" ]' ;
+            exit;
+        }
+        if (!SwpmMemberUtils::is_valid_user_name($field_value)){
+            echo '[ "' . $field_id . '",false,"&chi;&nbsp;'. SwpmUtils::_('Name contains invalid character'). '"]';
+            exit;
+        }
+        $table = $wpdb->prefix . "swpm_members_tbl";
+        $query = $wpdb->prepare("SELECT COUNT(*) FROM $table WHERE user_name = %s", $field_value);
+        $exists = $wpdb->get_var($query) > 0;
+        echo '[ "' . $field_id . (($exists) ? '",false,"&chi;&nbsp;'. SwpmUtils::_('Already taken'). '"]' :
+            '",true,"&radic;&nbsp;'.SwpmUtils::_('Available'). '"]');
+        exit;
+    }
+
+}

+ 9 - 0
simple-membership/classes/class.swpm-auth-permission-collection.php

@@ -0,0 +1,9 @@
+<?php
+/**
+ * Description of BAuthPermissionCollection
+ *
+ * @author nur
+ */
+class SwpmAuthPermissionCollection extends SwpmPermissionCollection{
+//put your code here
+}

+ 402 - 0
simple-membership/classes/class.swpm-auth.php

@@ -0,0 +1,402 @@
+<?php
+
+class SwpmAuth {
+
+	public $protected;
+	public $permitted;
+	private $isLoggedIn;
+	private $lastStatusMsg;
+	private static $_this;
+	public $userData;
+
+	private function __construct() {
+		//check if we need to display custom message on the login form
+		$custom_msg = filter_input( INPUT_COOKIE, 'swpm-login-form-custom-msg', FILTER_SANITIZE_STRING );
+		if ( ! empty( $custom_msg ) ) {
+			$this->lastStatusMsg = $custom_msg;
+			//let's 'unset' the cookie
+			setcookie( 'swpm-login-form-custom-msg', '', time() - 3600, COOKIEPATH, COOKIE_DOMAIN );
+		}
+		$this->isLoggedIn = false;
+		$this->userData   = null;
+		$this->protected  = SwpmProtection::get_instance();
+	}
+
+	private function init() {
+		$valid = $this->validate();
+		//SwpmLog::log_auth_debug("init:". ($valid? "valid": "invalid"), true);
+		if ( ! $valid ) {
+			$this->authenticate();
+		}
+	}
+
+	public static function get_instance() {
+		if ( empty( self::$_this ) ) {
+			self::$_this = new SwpmAuth();
+			self::$_this->init();
+		}
+		return self::$_this;
+	}
+
+	private function authenticate( $user = null, $pass = null ) {
+		global $wpdb;
+		$swpm_password  = empty( $pass ) ? filter_input( INPUT_POST, 'swpm_password' ) : $pass;
+		$swpm_user_name = empty( $user ) ? apply_filters( 'swpm_user_name', filter_input( INPUT_POST, 'swpm_user_name' ) ) : $user;
+
+		if ( ! empty( $swpm_user_name ) && ! empty( $swpm_password ) ) {
+			//SWPM member login request.
+			//Trigger action hook that can be used to check stuff before the login request is processed by the plugin.
+			$args = array(
+				'username' => $swpm_user_name,
+				'password' => $swpm_password,
+			);
+			do_action( 'swpm_before_login_request_is_processed', $args );
+
+			//First, lets make sure this user is not already logged into the site as an "Admin" user. We don't want to override that admin login session.
+			if ( current_user_can( 'administrator' ) ) {
+				//This user is logged in as ADMIN then trying to do another login as a member. Stop the login request processing (we don't want to override your admin login session).
+				$wp_profile_page = SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL . '/wp-admin/profile.php';
+				$error_msg       = '';
+				$error_msg      .= '<p>' . SwpmUtils::_( 'Warning! Simple Membership plugin cannot process this login request to prevent you from getting logged out of WP Admin accidentally.' ) . '</p>';
+				$error_msg      .= '<p><a href="' . $wp_profile_page . '" target="_blank">' . SwpmUtils::_( 'Click here' ) . '</a>' . SwpmUtils::_( ' to see the profile you are currently logged into in this browser.' ) . '</p>';
+				$error_msg      .= '<p>' . SwpmUtils::_( 'You are logged into the site as an ADMIN user in this browser. First, logout from WP Admin then you will be able to log in as a normal member.' ) . '</p>';
+				$error_msg      .= '<p>' . SwpmUtils::_( 'Alternatively, you can use a different browser (where you are not logged-in as ADMIN) to test the membership login.' ) . '</p>';
+				$error_msg      .= '<p>' . SwpmUtils::_( 'Your normal visitors or members will never see this message. This message is ONLY for ADMIN user.' ) . '</p>';
+				wp_die( $error_msg );
+			}
+
+			//If captcha is present and validation failed, it returns an error string. If validation succeeds, it returns an empty string.
+			$captcha_validation_output = apply_filters( 'swpm_validate_login_form_submission', '' );
+			if ( ! empty( $captcha_validation_output ) ) {
+				$this->lastStatusMsg = SwpmUtils::_( 'Captcha validation failed on login form.' );
+				return;
+			}
+
+			if ( is_email( $swpm_user_name ) ) {//User is trying to log-in using an email address
+				$email    = sanitize_email( $swpm_user_name );
+				$query    = $wpdb->prepare( 'SELECT user_name FROM ' . $wpdb->prefix . 'swpm_members_tbl WHERE email = %s', $email );
+				$username = $wpdb->get_var( $query );
+				if ( $username ) {//Found a user record
+					$swpm_user_name = $username; //Grab the usrename value so it can be used in the authentication process.
+					SwpmLog::log_auth_debug( 'Authentication request using email address: ' . $email . ', Found a user record with username: ' . $swpm_user_name, true );
+				}
+			}
+
+			//Lets process the request. Check username and password
+			$user = sanitize_user( $swpm_user_name );
+			$pass = trim( $swpm_password );
+			SwpmLog::log_auth_debug( 'Authentication request - Username: ' . $swpm_user_name, true );
+
+			$query          = 'SELECT * FROM ' . $wpdb->prefix . 'swpm_members_tbl WHERE user_name = %s';
+			$userData       = $wpdb->get_row( $wpdb->prepare( $query, $user ) );
+			$this->userData = $userData;
+			if ( ! $userData ) {
+				$this->isLoggedIn    = false;
+				$this->userData      = null;
+				$this->lastStatusMsg = SwpmUtils::_( 'User Not Found.' );
+				return false;
+			}
+			$check = $this->check_password( $pass, $userData->password );
+			if ( ! $check ) {
+				$this->isLoggedIn    = false;
+				$this->userData      = null;
+				$this->lastStatusMsg = SwpmUtils::_( 'Password Empty or Invalid.' );
+				return false;
+			}
+			if ( $this->check_constraints() ) {
+				$rememberme = filter_input( INPUT_POST, 'rememberme' );
+				$remember   = empty( $rememberme ) ? false : true;
+				$this->set_cookie( $remember );
+				$this->isLoggedIn    = true;
+				$this->lastStatusMsg = 'Logged In.';
+				SwpmLog::log_auth_debug( 'Authentication successful for username: ' . $user . '. Executing swpm_login action hook.', true );
+				do_action( 'swpm_login', $user, $pass, $remember );
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private function check_constraints() {
+		if ( empty( $this->userData ) ) {
+			return false;
+		}
+		global $wpdb;
+		$enable_expired_login = SwpmSettings::get_instance()->get_value( 'enable-expired-account-login', '' );
+
+		//Update the last accessed date and IP address for this login attempt. $wpdb->update(table, data, where, format, where format)
+		$last_accessed_date = current_time( 'mysql' );
+		$last_accessed_ip   = SwpmUtils::get_user_ip_address();
+		$wpdb->update(
+			$wpdb->prefix . 'swpm_members_tbl',
+			array(
+				'last_accessed'         => $last_accessed_date,
+				'last_accessed_from_ip' => $last_accessed_ip,
+			),
+			array( 'member_id' => $this->userData->member_id ),
+			array( '%s', '%s' ),
+			array( '%d' )
+		);
+
+		//Check the member's account status.
+		$can_login = true;
+		if ( $this->userData->account_state == 'inactive' && empty( $enable_expired_login ) ) {
+			$this->lastStatusMsg = SwpmUtils::_( 'Account is inactive.' );
+			$can_login           = false;
+		} elseif ( ( $this->userData->account_state == 'expired' ) && empty( $enable_expired_login ) ) {
+			$this->lastStatusMsg = SwpmUtils::_( 'Account has expired.' );
+			$can_login           = false;
+		} elseif ( $this->userData->account_state == 'pending' ) {
+			$this->lastStatusMsg = SwpmUtils::_( 'Account is pending.' );
+			$can_login           = false;
+		} elseif ( $this->userData->account_state == 'activation_required' ) {
+			$resend_email_url    = add_query_arg(
+				array(
+					'swpm_resend_activation_email' => '1',
+					'swpm_member_id'               => $this->userData->member_id,
+				),
+				get_home_url()
+			);
+			$msg                 = sprintf( SwpmUtils::_( 'You need to activate your account. If you didn\'t receive an email then %s to resend the activation email.' ), '<a href="' . $resend_email_url . '">' . SwpmUtils::_( 'click here' ) . '</a>' );
+			$this->lastStatusMsg = $msg;
+			$can_login           = false;
+		}
+
+		if ( ! $can_login ) {
+			$this->isLoggedIn = false;
+			$this->userData   = null;
+			return false;
+		}
+
+		if ( SwpmUtils::is_subscription_expired( $this->userData ) ) {
+			if ( $this->userData->account_state == 'active' ) {
+				$wpdb->update( $wpdb->prefix . 'swpm_members_tbl', array( 'account_state' => 'expired' ), array( 'member_id' => $this->userData->member_id ), array( '%s' ), array( '%d' ) );
+			}
+			if ( empty( $enable_expired_login ) ) {
+				$this->lastStatusMsg = SwpmUtils::_( 'Account has expired.' );
+				$this->isLoggedIn    = false;
+				$this->userData      = null;
+				return false;
+			}
+		}
+
+		$this->permitted     = SwpmPermission::get_instance( $this->userData->membership_level );
+		$this->lastStatusMsg = SwpmUtils::_( 'You are logged in as:' ) . $this->userData->user_name;
+		$this->isLoggedIn    = true;
+		return true;
+	}
+
+	private function check_password( $plain_password, $hashed_pw ) {
+		global $wp_hasher;
+		if ( empty( $plain_password ) ) {
+			return false;
+		}
+		if ( empty( $wp_hasher ) ) {
+			require_once ABSPATH . 'wp-includes/class-phpass.php';
+			$wp_hasher = new PasswordHash( 8, true );
+		}
+		return $wp_hasher->CheckPassword( $plain_password, $hashed_pw );
+	}
+
+	public function match_password( $password ) {
+		if ( ! $this->is_logged_in() ) {
+			return false;
+		}
+		return $this->check_password( $password, $this->get( 'password' ) );
+	}
+
+	public function login_to_swpm_using_wp_user( $user ) {
+		if ( $this->isLoggedIn ) {
+			return false;
+		}
+		$email  = $user->user_email;
+		$member = SwpmMemberUtils::get_user_by_email( $email );
+		if ( empty( $member ) ) {
+			//There is no swpm profile with this email.
+			return false;
+		}
+		$this->userData   = $member;
+		$this->isLoggedIn = true;
+		$this->set_cookie();
+		SwpmLog::log_auth_debug( 'Member has been logged in using WP User object.', true );
+		$this->check_constraints();
+		return true;
+	}
+
+	public function login( $user, $pass, $remember = '', $secure = '' ) {
+		SwpmLog::log_auth_debug( 'SwpmAuth::login()', true );
+		if ( $this->isLoggedIn ) {
+			return;
+		}
+		if ( $this->authenticate( $user, $pass ) && $this->validate() ) {
+			$this->set_cookie( $remember, $secure );
+		} else {
+			$this->isLoggedIn = false;
+			$this->userData   = null;
+		}
+		return $this->lastStatusMsg;
+	}
+
+	public function logout() {
+		if ( ! $this->isLoggedIn ) {
+			return;
+		}
+		setcookie( SIMPLE_WP_MEMBERSHIP_AUTH, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
+		setcookie( SIMPLE_WP_MEMBERSHIP_SEC_AUTH, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
+		$this->userData      = null;
+		$this->isLoggedIn    = false;
+		$this->lastStatusMsg = SwpmUtils::_( 'Logged Out Successfully.' );
+		do_action( 'swpm_logout' );
+	}
+
+	private function set_cookie( $remember = '', $secure = '' ) {
+		if ( $remember ) {
+			$expiration = time() + 1209600; //14 days
+			$expire     = $expiration + 43200; //12 hours grace period
+		} else {
+			$expiration = time() + 259200; //3 days.
+			$expire     = $expiration; //The minimum cookie expiration should be at least a few days.
+		}
+
+		$expire = apply_filters( 'swpm_auth_cookie_expiry_value', $expire );
+
+		setcookie( 'swpm_in_use', 'swpm_in_use', $expire, COOKIEPATH, COOKIE_DOMAIN );
+
+		$expiration_timestamp = SwpmUtils::get_expiration_timestamp( $this->userData );
+		$enable_expired_login = SwpmSettings::get_instance()->get_value( 'enable-expired-account-login', '' );
+		// make sure cookie doesn't live beyond account expiration date.
+		// but if expired account login is enabled then ignore if account is expired
+		$expiration = empty( $enable_expired_login ) ? min( $expiration, $expiration_timestamp ) : $expiration;
+		$pass_frag  = substr( $this->userData->password, 8, 4 );
+		$scheme     = 'auth';
+		if ( ! $secure ) {
+			$secure = is_ssl();
+		}
+		$key              = self::b_hash( $this->userData->user_name . $pass_frag . '|' . $expiration, $scheme );
+		$hash             = hash_hmac( 'md5', $this->userData->user_name . '|' . $expiration, $key );
+		$auth_cookie      = $this->userData->user_name . '|' . $expiration . '|' . $hash;
+		$auth_cookie_name = $secure ? SIMPLE_WP_MEMBERSHIP_SEC_AUTH : SIMPLE_WP_MEMBERSHIP_AUTH;
+		setcookie( $auth_cookie_name, $auth_cookie, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure, true );
+	}
+
+	private function validate() {
+		$auth_cookie_name = is_ssl() ? SIMPLE_WP_MEMBERSHIP_SEC_AUTH : SIMPLE_WP_MEMBERSHIP_AUTH;
+		if ( ! isset( $_COOKIE[ $auth_cookie_name ] ) || empty( $_COOKIE[ $auth_cookie_name ] ) ) {
+			return false;
+		}
+		$cookie_elements = explode( '|', $_COOKIE[ $auth_cookie_name ] );
+		if ( count( $cookie_elements ) != 3 ) {
+			return false;
+		}
+
+		//SwpmLog::log_auth_debug("validate() - " . $_COOKIE[$auth_cookie_name], true);
+		list($username, $expiration, $hmac) = $cookie_elements;
+		$expired                            = $expiration;
+		// Allow a grace period for POST and AJAX requests
+		if ( defined( 'DOING_AJAX' ) || 'POST' == $_SERVER['REQUEST_METHOD'] ) {
+			$expired += HOUR_IN_SECONDS;
+		}
+		// Quick check to see if an honest cookie has expired
+		if ( $expired < time() ) {
+			$this->lastStatusMsg = SwpmUtils::_( 'Session Expired.' ); //do_action('auth_cookie_expired', $cookie_elements);
+			SwpmLog::log_auth_debug( 'validate() - Session Expired', true );
+			return false;
+		}
+
+		global $wpdb;
+		$query = ' SELECT * FROM ' . $wpdb->prefix . 'swpm_members_tbl WHERE user_name = %s';
+		$user  = $wpdb->get_row( $wpdb->prepare( $query, $username ) );
+		if ( empty( $user ) ) {
+			$this->lastStatusMsg = SwpmUtils::_( 'Invalid Username' );
+			return false;
+		}
+
+		$pass_frag = substr( $user->password, 8, 4 );
+		$key       = self::b_hash( $username . $pass_frag . '|' . $expiration );
+		$hash      = hash_hmac( 'md5', $username . '|' . $expiration, $key );
+		if ( $hmac != $hash ) {
+			$this->lastStatusMsg = SwpmUtils::_( 'Please login again.' );
+			SwpmLog::log_auth_debug( 'validate() - Bad Hash', true );
+                        do_action('swpm_validate_login_hash_mismatch');
+			wp_logout(); //Force logout of WP user session to clear the bad hash.
+			return false;
+		}
+
+		if ( $expiration < time() ) {
+			$GLOBALS['login_grace_period'] = 1;
+		}
+		$this->userData = $user;
+		return $this->check_constraints();
+	}
+
+	public static function b_hash( $data, $scheme = 'auth' ) {
+		$salt = wp_salt( $scheme ) . 'j4H!B3TA,J4nIn4.';
+		return hash_hmac( 'md5', $data, $salt );
+	}
+
+	public function is_logged_in() {
+		return $this->isLoggedIn;
+	}
+
+	public function get( $key, $default = '' ) {
+		if ( isset( $this->userData->$key ) ) {
+			return $this->userData->$key;
+		}
+		if ( isset( $this->permitted->$key ) ) {
+			return $this->permitted->$key;
+		}
+		if ( ! empty( $this->permitted ) ) {
+			return $this->permitted->get( $key, $default );
+		}
+		return $default;
+	}
+
+	public function get_message() {
+		return $this->lastStatusMsg;
+	}
+
+	public function get_expire_date() {
+		if ( $this->isLoggedIn ) {
+			return SwpmUtils::get_formatted_expiry_date( $this->get( 'subscription_starts' ), $this->get( 'subscription_period' ), $this->get( 'subscription_duration_type' ) );
+		}
+		return '';
+	}
+
+	public function delete() {
+		if ( ! $this->is_logged_in() ) {
+			return;
+		}
+		$user_name = $this->get( 'user_name' );
+		$user_id   = $this->get( 'member_id' );
+		$subscr_id = $this->get( 'subscr_id' );
+		$email     = $this->get( 'email' );
+
+		$this->logout();
+                wp_clear_auth_cookie();
+
+		SwpmMembers::delete_swpm_user_by_id( $user_id );
+		SwpmMembers::delete_wp_user( $user_name );
+	}
+
+	public function reload_user_data() {
+		if ( ! $this->is_logged_in() ) {
+			return;
+		}
+		global $wpdb;
+		$query          = 'SELECT * FROM ' . $wpdb->prefix . 'swpm_members_tbl WHERE member_id = %d';
+		$this->userData = $wpdb->get_row( $wpdb->prepare( $query, $this->userData->member_id ) );
+	}
+
+	public function is_expired_account() {
+		if ( ! $this->is_logged_in() ) {
+			return null;
+		}
+		$account_status = $this->get( 'account_state' );
+		if ( $account_status == 'expired' || $account_status == 'inactive' ) {
+			//Expired or Inactive accounts are both considered to be expired.
+			return true;
+		}
+		return false;
+	}
+
+}

+ 140 - 0
simple-membership/classes/class.swpm-category-list.php

@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * BCategoryList
+ *
+ * @author nur
+ */
+if (!class_exists('WP_List_Table')) {
+    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
+}
+
+class SwpmCategoryList extends WP_List_Table {
+
+    public $selected_level_id = 1;
+    public $category;
+
+    function __construct() {
+        parent::__construct(array(
+            'singular' => SwpmUtils::_('Membership Level'),
+            'plural' => SwpmUtils::_('Membership Levels'),
+            'ajax' => false
+        ));
+        $selected = filter_input(INPUT_POST, 'membership_level_id');
+        $this->selected_level_id = empty($selected) ? 1 : $selected;
+        $this->category = ($this->selected_level_id == 1) ?
+                SwpmProtection::get_instance() :
+                SwpmPermission::get_instance($this->selected_level_id);
+    }
+
+    function get_columns() {
+        return array(
+            'cb' => '<input type="checkbox" />'
+            , 'term_id' => SwpmUtils::_('Category ID')
+            , 'name' => SwpmUtils::_('Category Name')
+            , 'taxonomy' => SwpmUtils::_('Category Type (Taxonomy)')
+            , 'description' => SwpmUtils::_('Description')
+            , 'count' => SwpmUtils::_('Count')
+        );
+    }
+
+    function get_sortable_columns() {
+        return array();
+    }
+
+    function column_default($item, $column_name) {
+        return stripslashes($item->$column_name);
+    }
+
+    function column_term_id($item) {
+        return $item->term_id;
+    }
+
+    function column_taxonomy($item) {
+        $taxonomy = $item->taxonomy;
+        if ($taxonomy == 'category'){
+            $taxonomy = 'Post Category';
+        } else {
+            $taxonomy = 'Custom Post Type ('.$taxonomy.')';
+        }
+        return $taxonomy;
+    }
+    
+    function column_cb($item) {
+        return sprintf(
+                '<input type="hidden" name="ids_in_page[]" value="%s">
+            <input type="checkbox" %s name="ids[]" value="%s" />', $item->term_id, $this->category->in_categories($item->term_id) ? "checked" : "", $item->term_id
+        );
+    }
+
+    public static function update_category_list() {
+        //Check we are on the admin end and user has management permission 
+        SwpmMiscUtils::check_user_permission_and_is_admin('category protection update');
+        
+        //Check nonce
+        $swpm_category_prot_update_nonce = filter_input(INPUT_POST, 'swpm_category_prot_update_nonce');
+        if (!wp_verify_nonce($swpm_category_prot_update_nonce, 'swpm_category_prot_update_nonce_action')) {
+            //Nonce check failed.
+            wp_die(SwpmUtils::_("Error! Nonce security verification failed for Category Protection Update action. Clear cache and try again."));
+        }
+            
+        $selected = filter_input(INPUT_POST, 'membership_level_id');
+        $selected_level_id = empty($selected) ? 1 : $selected;
+        $category = ($selected_level_id == 1) ?
+                SwpmProtection::get_instance() :
+                SwpmPermission::get_instance($selected_level_id);
+        $args = array('ids' => array(
+                'filter' => FILTER_VALIDATE_INT,
+                'flags' => FILTER_REQUIRE_ARRAY,
+        ));
+        $filtered = filter_input_array(INPUT_POST, $args);
+        $ids = $filtered['ids'];
+        $args = array('ids_in_page' => array(
+                'filter' => FILTER_VALIDATE_INT,
+                'flags' => FILTER_REQUIRE_ARRAY,
+        ));
+        $filtered = filter_input_array(INPUT_POST, $args);
+        $ids_in_page = $filtered['ids_in_page'];
+        $category->remove($ids_in_page, 'category')->apply($ids, 'category')->save();
+        $message = array('succeeded' => true, 'message' => '<p class="swpm-green-box">' . SwpmUtils::_('Category protection updated!') . '</p>');
+        SwpmTransfer::get_instance()->set('status', $message);
+    }
+
+    function prepare_items() {
+        $all_categories = array();
+        $taxonomies = get_taxonomies($args = array('public' => true,'_builtin'=>false));
+        $taxonomies['category'] = 'category';
+        $all_terms = get_terms( $taxonomies, 'orderby=count&hide_empty=0&order=DESC');        
+        $totalitems = count($all_terms);
+        $perpage = 100;
+        $paged = !empty($_GET["paged"]) ? sanitize_text_field($_GET["paged"]) : '';
+        if (empty($paged) || !is_numeric($paged) || $paged <= 0) {
+            $paged = 1;
+        }
+        $totalpages = ceil($totalitems / $perpage);
+        $offset = 0;
+        if (!empty($paged) && !empty($perpage)) {
+            $offset = ($paged - 1) * $perpage;
+        }
+        for ($i = $offset; $i < ((int) $offset + (int) $perpage) && !empty($all_terms[$i]); $i++) {
+            $all_categories[] = $all_terms[$i];
+        }
+        $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 = $all_categories;
+    }
+
+    function no_items() {
+        SwpmUtils::e('No category found.');
+    }
+
+}

+ 82 - 0
simple-membership/classes/class.swpm-comment-form-related.php

@@ -0,0 +1,82 @@
+<?php
+
+class SwpmCommentFormRelated {
+
+    public static function customize_comment_form() {
+        $allow_comments = SwpmSettings::get_instance()->get_value('members-login-to-comment');
+        if (empty($allow_comments)){
+            return;
+        }        
+        if (SwpmAuth::get_instance()->is_logged_in()){
+            return;            
+        }
+        
+        //Apply a filter to the message so it can be customized using the custom message plugin
+        $comment_form_msg = apply_filters('swpm_login_to_comment_msg', SwpmUtils::_("Please login to comment."));
+        $comment_form_msg = '<div class="swpm-login-to-comment-msg">' . $comment_form_msg . '</div>';
+        ?>
+        <script type="text/javascript">
+            jQuery(document).ready(function($) {
+                $('#respond').html('<?php echo $comment_form_msg; ?>');
+            });
+        </script>
+        <?php        
+    }
+    
+    public static function customize_comment_fields($fields){
+        
+        //Check if login to comment feature is enabled.
+        $allow_comments = SwpmSettings::get_instance()->get_value('members-login-to-comment');
+        if (empty($allow_comments)){//Feature is disabled
+            return $fields;
+        }        
+        
+        if (SwpmAuth::get_instance()->is_logged_in()){//Member is logged-in.
+            return $fields;
+        }
+        
+        //Member is not logged-in so show the protection message.
+        $fields = array();
+        $login_link = SwpmUtils::_('Please Login to Comment.');
+        $fields['comment_field'] = $login_link;
+        $fields['title_reply'] = '';
+        $fields['cancel_reply_link'] = '';
+        $fields['comment_notes_before'] = '';
+        $fields['comment_notes_after'] = '';
+        $fields['fields'] = '';
+        $fields['label_submit'] = '';
+        $fields['title_reply_to'] = '';
+        $fields['id_submit'] = '';
+        $fields['id_form'] = '';
+        
+        return $fields;        
+    }
+    
+    /*
+     * This function checks and restricts comment posting (via HTTP POST) to members only (if the feature is enabled)
+     */
+    public static function check_and_restrict_comment_posting_to_members(){    
+        $allow_comments = SwpmSettings::get_instance()->get_value('members-login-to-comment');
+        if (empty($allow_comments)){
+            return;
+        }
+         
+        if (is_admin()) {
+            return;            
+        }          
+             
+        if (SwpmAuth::get_instance()->is_logged_in()){
+            return;            
+        }
+        
+        $comment_id = filter_input(INPUT_POST, 'comment_post_ID');
+        if (empty($comment_id)) {
+            return;            
+        }
+        
+        //Stop this request -> 1)we are on the front-side. 2) Comment posted by a not logged in member. 3) comment_post_ID missing. 
+        $_POST = array();        
+        wp_die(SwpmUtils::_('Comments not allowed by a non-member.'));
+    }
+    
+}

+ 89 - 0
simple-membership/classes/class.swpm-cronjob.php

@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * Description of BCronJob
+ *
+ * @author nur
+ */
+class SwpmCronJob {
+
+    public function __construct() {
+        add_action('swpm_account_status_event', array(&$this, 'update_account_status'));
+        add_action('swpm_delete_pending_account_event', array(&$this, 'delete_pending_account'));
+        add_action('swpm_delete_pending_account_event', array($this, 'delete_pending_email_activation_data'));
+    }
+
+    public function update_account_status() {
+        global $wpdb;
+        for ($counter = 0;; $counter += 100) {
+            $query = $wpdb->prepare("SELECT member_id, membership_level, subscription_starts, account_state
+                    FROM {$wpdb->prefix}swpm_members_tbl 
+                    WHERE membership_level NOT IN ( SELECT id FROM {$wpdb->prefix}swpm_membership_tbl 
+                                                WHERE subscription_period = '' OR subscription_period = '0' )
+                    LIMIT %d, 100", $counter);
+            $results = $wpdb->get_results($query);
+            if (empty($results)) {
+                break;
+            }
+            $expired = array();
+            foreach ($results as $result) {
+                $timestamp = SwpmUtils::get_expiration_timestamp($result);
+                if ($timestamp < time() && $result->account_state == 'active') {
+                    $expired[] = $result->member_id;
+                }
+            }
+            if (count($expired) > 0) {
+                $query = "UPDATE {$wpdb->prefix}swpm_members_tbl 
+                SET account_state='expired'  WHERE member_id IN (" . implode(',', $expired) . ")";
+                $wpdb->query($query);
+            }
+        }
+    }
+
+    public function delete_pending_account() {
+        global $wpdb;
+        $interval = SwpmSettings::get_instance()->get_value('delete-pending-account');
+        if (empty($interval)) {
+            return;
+        }
+        for ($counter = 0;; $counter += 100) {
+            $query = $wpdb->prepare("SELECT member_id
+                                     FROM 
+                                        {$wpdb->prefix}swpm_members_tbl 
+                                    WHERE account_state='pending' 
+                                         AND subscription_starts < DATE_SUB(NOW(), INTERVAL %d MONTH) LIMIT %d, 100", $interval, $counter);
+            $results = $wpdb->get_results($query);
+            if (empty($results)) {
+                break;
+            }
+            $to_delete = array();
+            foreach ($results as $result) {
+                $to_delete[] = $result->member_id;
+            }
+            if (count($to_delete) > 0) {
+                SwpmLog::log_simple_debug("Auto deleting pending account.", true);
+                $query = "DELETE FROM {$wpdb->prefix}swpm_members_tbl 
+                          WHERE member_id IN (" . implode(',', $to_delete) . ")";
+                $wpdb->query($query);
+            }
+        }
+    }
+
+    public function delete_pending_email_activation_data() {
+        global $wpdb;
+        $q = "SELECT * FROM {$wpdb->prefix}options WHERE option_name LIKE '%swpm_email_activation_data_usr_%'";
+        $res = $wpdb->get_results($q);
+        if (empty($res)) {
+            return;
+        }
+        foreach ($res as $data) {
+            $value = unserialize($data->option_value);
+            $timestamp = isset($value['timestamp']) ? $value['timestamp'] : 0;
+            $now = time();
+            if ($now > $timestamp * 60 * 60 * 24) {
+                delete_option($data->option_name);
+            }
+        }
+    }
+
+}

+ 305 - 0
simple-membership/classes/class.swpm-form.php

@@ -0,0 +1,305 @@
+<?php
+
+class SwpmForm {
+
+    protected $fields;
+    protected $op;
+    protected $errors;
+    protected $sanitized;
+
+    public function __construct($fields) {
+        $this->fields = $fields;
+        $this->sanitized = array();
+        $this->errors = array();
+        $this->validate_wp_user_email();
+        if ($this->is_valid()){
+            foreach ($fields as $key => $value){
+                $this->$key();
+            }
+        }
+    }
+    protected function validate_wp_user_email(){
+        $user_name = filter_input(INPUT_POST, 'user_name',FILTER_SANITIZE_STRING);
+        $email = filter_input(INPUT_POST, 'email', FILTER_UNSAFE_RAW);
+        if (empty($user_name)) {
+            return;
+        }
+
+        $user = get_user_by('login', $user_name);
+        if ($user && ($user->user_email != $email)){
+            $error_msg = SwpmUtils::_("Wordpress account exists with given username. But given email doesn't match.");
+            $error_msg .= SwpmUtils::_(" Use a different username to complete the registration. If you want to use that username then you must enter the correct email address associated with the existing WP user to connect with that account.");
+            $this->errors['wp_email'] = $error_msg;
+            return;
+        }
+        $user = get_user_by('email', $email);
+        if($user && ($user_name != $user->user_login)){
+            $error_msg = SwpmUtils::_("Wordpress account exists with given email. But given username doesn't match.");
+            $error_msg .= SwpmUtils::_(" Use a different email address to complete the registration. If you want to use that email then you must enter the correct username associated with the existing WP user to connect with that account.");
+            $this->errors['wp_user'] = $error_msg;
+        }
+    }
+
+    protected function user_name() {
+        global $wpdb;
+        if (!empty($this->fields['user_name'])){return;}
+        $user_name = filter_input(INPUT_POST, 'user_name',FILTER_SANITIZE_STRING);
+        if (empty($user_name)) {
+            $this->errors['user_name'] = SwpmUtils::_('Username is required');
+            return;
+        }
+        if (!SwpmMemberUtils::is_valid_user_name($user_name)) {
+            $this->errors['user_name'] = SwpmUtils::_('Username contains invalid character');
+            return;
+        }
+        $saned = sanitize_text_field($user_name);
+        $query = "SELECT count(member_id) FROM {$wpdb->prefix}swpm_members_tbl WHERE user_name= %s";
+        $result = $wpdb->get_var($wpdb->prepare($query, strip_tags($saned)));
+        if ($result > 0) {
+            if ($saned != $this->fields['user_name']) {
+                $this->errors['user_name'] = SwpmUtils::_('Username already exists.');
+                return;
+            }
+        }
+        $this->sanitized['user_name'] = $saned;
+    }
+
+    protected function first_name() {
+        $first_name = filter_input(INPUT_POST, 'first_name', FILTER_SANITIZE_STRING);
+        $this->sanitized['first_name'] = sanitize_text_field($first_name);
+    }
+
+    protected function last_name() {
+        $last_name = filter_input(INPUT_POST, 'last_name', FILTER_SANITIZE_STRING);
+        $this->sanitized['last_name'] = sanitize_text_field($last_name);
+    }
+
+    protected function password() {
+        $password = filter_input(INPUT_POST, 'password',FILTER_UNSAFE_RAW);
+        $password_re = filter_input(INPUT_POST, 'password_re',FILTER_UNSAFE_RAW);
+        if (empty($this->fields['password']) && empty($password)) {
+            $this->errors['password'] = SwpmUtils::_('Password is required');
+            return;
+        }
+        if (!empty($password)) {
+            $saned = sanitize_text_field($password);
+            $saned_re = sanitize_text_field($password_re);
+            if ($saned != $saned_re){
+                $this->errors['password'] = SwpmUtils::_('Password mismatch');
+            }
+            $this->sanitized['plain_password'] = $password;
+            $this->sanitized['password'] = SwpmUtils::encrypt_password(trim($password)); //should use $saned??;
+        }
+    }
+
+    protected function email() {
+        global $wpdb;
+        $email = filter_input(INPUT_POST, 'email', FILTER_UNSAFE_RAW);
+        if (empty($email)) {
+            $this->errors['email'] = SwpmUtils::_('Email is required');
+            return;
+        }
+        if (!is_email($email)) {
+            $this->errors['email'] = SwpmUtils::_('Email is invalid') . " (".$email.")";
+            return;
+        }
+        $saned = sanitize_email($email);
+        $query = "SELECT count(member_id) FROM {$wpdb->prefix}swpm_members_tbl WHERE email= %s";
+        $member_id = filter_input(INPUT_GET, 'member_id', FILTER_SANITIZE_NUMBER_INT);
+        if (!empty($member_id)) {
+            $query .= ' AND member_id !=%d';
+            $result = $wpdb->get_var($wpdb->prepare($query, strip_tags($saned), $member_id));
+        }
+        else{
+            $result = $wpdb->get_var($wpdb->prepare($query, strip_tags($saned)));
+        }
+
+        if ($result > 0) {
+            if ($saned != $this->fields['email']) {
+                $error_msg = SwpmUtils::_('Email is already used.') . " (" . $saned .")";
+                $this->errors['email'] = $error_msg;
+                return;
+            }
+        }
+        $this->sanitized['email'] = $saned;
+    }
+
+    protected function phone() {
+        $phone = filter_input(INPUT_POST, 'phone', FILTER_UNSAFE_RAW);
+        $saned = wp_kses($phone, array());
+        $this->sanitized['phone'] = $saned;
+        return;
+    }
+
+    protected function address_street() {
+        $address_street = filter_input(INPUT_POST, 'address_street', FILTER_SANITIZE_STRING);
+        $this->sanitized['address_street'] = wp_kses($address_street, array());
+    }
+
+    protected function address_city() {
+        $address_city = filter_input(INPUT_POST, 'address_city', FILTER_SANITIZE_STRING);
+        $this->sanitized['address_city'] = wp_kses($address_city, array());
+    }
+
+    protected function address_state() {
+        $address_state = filter_input(INPUT_POST, 'address_state', FILTER_SANITIZE_STRING);
+        $this->sanitized['address_state'] = wp_kses($address_state, array());
+    }
+
+    protected function address_zipcode() {
+        $address_zipcode = filter_input(INPUT_POST, 'address_zipcode', FILTER_UNSAFE_RAW);
+        $this->sanitized['address_zipcode'] = wp_kses($address_zipcode, array());
+    }
+
+    protected function country() {
+        $country = filter_input(INPUT_POST, 'country', FILTER_SANITIZE_STRING);
+        $this->sanitized['country'] = wp_kses($country, array());
+    }
+
+    protected function company_name() {
+        $company_name = filter_input(INPUT_POST, 'company_name', FILTER_SANITIZE_STRING);
+        $this->sanitized['company_name'] = $company_name;
+    }
+
+    protected function member_since() {
+        $member_since = filter_input(INPUT_POST, 'member_since', FILTER_UNSAFE_RAW);
+        if (empty($member_since)) {return;}
+        if (preg_match('/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/', $member_since)){
+            $this->sanitized['member_since'] =  sanitize_text_field($member_since);
+            return;
+        }
+        $this->errors['member_since'] = SwpmUtils::_('Member since field is invalid');
+
+    }
+
+    protected function subscription_starts() {
+        $subscription_starts = filter_input(INPUT_POST, 'subscription_starts', FILTER_SANITIZE_STRING);
+        if(empty($subscription_starts)) {return ;}
+        if (preg_match('/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/', $subscription_starts)){
+            $this->sanitized['subscription_starts'] =  sanitize_text_field($subscription_starts);
+            return;
+        }
+        $this->errors['subscription_starts'] = SwpmUtils::_('Access starts field is invalid');
+    }
+
+    protected function gender() {
+        $gender = filter_input(INPUT_POST, 'gender', FILTER_SANITIZE_STRING);
+        if(empty($gender)) {return;}
+        if (in_array($gender, array('male', 'female', 'not specified'))){
+            $this->sanitized['gender'] = $gender;
+        }
+        else{
+            $this->errors['gender'] = SwpmUtils::_('Gender field is invalid');
+        }
+    }
+
+    protected function account_state() {
+        $account_state = filter_input(INPUT_POST, 'account_state', FILTER_SANITIZE_STRING);
+        if(empty($account_state)) {return;}
+        if (in_array($account_state, array('active', 'pending', 'activation_required', 'inactive', 'expired'))){
+            $this->sanitized['account_state'] = $account_state;
+        }
+        else{
+            $this->errors['account_state'] = SwpmUtils::_('Account state field is invalid');
+        }
+    }
+
+    protected function membership_level() {
+        $membership_level = filter_input(INPUT_POST, 'membership_level', FILTER_SANITIZE_NUMBER_INT);
+        if ($membership_level == 1){
+            $this->errors['membership_level'] = SwpmUtils::_('Invalid membership level');
+            return;
+        }
+
+        if (empty($membership_level)) {return;}
+        $this->sanitized['membership_level'] = $membership_level;
+    }
+
+    protected function password_re() {
+
+    }
+
+    protected function last_accessed() {
+
+    }
+
+    protected function last_accessed_from_ip() {
+
+    }
+
+    protected function referrer() {
+
+    }
+
+    protected function extra_info() {
+
+    }
+
+    protected function reg_code() {
+
+    }
+
+    protected function txn_id() {
+
+    }
+
+    protected function subscr_id() {
+        $subscr_id = filter_input(INPUT_POST, 'subscr_id', FILTER_SANITIZE_STRING);
+        $this->sanitized['subscr_id'] = $subscr_id;
+    }
+
+    protected function flags() {
+
+    }
+
+    protected function more_membership_levels() {
+
+    }
+
+    protected function initial_membership_level() {
+
+    }
+
+    protected function home_page() {
+
+    }
+
+    protected function notes() {
+
+    }
+
+    protected function profile_image() {
+
+    }
+
+    protected function expiry_1st() {
+
+    }
+
+    protected function expiry_2nd() {
+
+    }
+
+    protected function member_id() {
+
+    }
+
+    public function is_valid() {
+
+        if (!isset($this->errors)){
+            //Errors are not set at all. Return true.
+            return true;
+        }
+
+        return count($this->errors) < 1;
+    }
+
+    public function get_sanitized_member_form_data() {
+        return $this->sanitized;
+    }
+
+    public function get_errors() {
+        return $this->errors;
+    }
+
+}

+ 7 - 0
simple-membership/classes/class.swpm-front-form.php

@@ -0,0 +1,7 @@
+<?php
+
+class SwpmFrontForm extends SwpmForm{
+	public function membership_level(){
+		
+	}
+}

+ 526 - 0
simple-membership/classes/class.swpm-front-registration.php

@@ -0,0 +1,526 @@
+<?php
+
+/**
+ * Description of BFrontRegistration
+ *
+ * @author nur
+ */
+class SwpmFrontRegistration extends SwpmRegistration {
+
+	public static function get_instance() {
+		self::$_intance = empty( self::$_intance ) ? new SwpmFrontRegistration() : self::$_intance;
+		return self::$_intance;
+	}
+
+	public function regigstration_ui( $level ) {
+
+		$settings_configs = SwpmSettings::get_instance();
+
+                //Check if the hide rego from logged-in users feature is enabled before rendering the registration form.
+                $hide_rego_to_logged_users = $settings_configs->get_value( 'hide-rego-form-to-logged-users' );
+                if ( ! empty( $hide_rego_to_logged_users ) ){
+                    //Hide registration form to logged-in users feature is enabled. Check if the form should be hidden.
+                    if ( SwpmMemberUtils::is_member_logged_in() ) {
+
+                        $rego_hidden_to_logged_users_msg = '<div class="registration_hidden_to_logged_users_msg">';
+                        $rego_hidden_to_logged_users_msg .= SwpmUtils::_( "You are already logged in. You don't need to create another account. So the registration form is hidden." );
+                        $rego_hidden_to_logged_users_msg .= '</div>';
+                        return $rego_hidden_to_logged_users_msg;
+                    }
+                }
+
+		//Trigger the filter to override the registration form (the form builder addon uses this filter)
+		$form = apply_filters( 'swpm_registration_form_override', '', $level ); //The $level value could be empty also so the code handling the filter need to check for it.
+		if ( ! empty( $form ) ) {
+			//An addon has overridden the registration form. So use that one.
+			return $form;
+		}
+
+		$joinuspage_url = $settings_configs->get_value( 'join-us-page-url' );
+		$membership_level = '';
+		global $wpdb;
+
+		if ( SwpmUtils::is_paid_registration() ) {
+			//Lets check if this is a registration for paid membership
+			$member = SwpmUtils::get_paid_member_info();
+			if ( empty( $member ) ) {
+				SwpmUtils::e( 'Error! Invalid Request. Could not find a match for the given security code and the user ID.' );
+			} else {
+				$membership_level = $member->membership_level;
+			}
+		} elseif ( ! empty( $level ) ) {
+			//Membership level is specified in the shortcode (level specific registration form).
+			$member           = SwpmTransfer::$default_fields;
+			$membership_level = absint( $level );
+		}
+
+		//Check if free membership registration is disalbed on the site
+		if ( empty( $membership_level ) ) {
+			$joinuspage_link         = '<a href="' . $joinuspage_url . '">' . SwpmUtils::_( 'Join Us' ) . '</a>';
+			$free_rego_disabled_msg  = '<p>';
+			$free_rego_disabled_msg .= SwpmUtils::_( 'Free membership is disabled on this site. Please make a payment from the ' );
+			$free_rego_disabled_msg .= SwpmUtils::_( $joinuspage_link );
+			$free_rego_disabled_msg .= SwpmUtils::_( ' page to pay for a premium membership.' );
+			$free_rego_disabled_msg .= '</p><p>';
+			$free_rego_disabled_msg .= SwpmUtils::_( 'You will receive a unique link via email after the payment. You will be able to use that link to complete the premium membership registration.' );
+			$free_rego_disabled_msg .= '</p>';
+			return $free_rego_disabled_msg;
+		}
+
+		//Handle the registration form in core plugin
+		$membership_info = SwpmPermission::get_instance( $membership_level );
+		$membership_level = $membership_info->get( 'id' );
+		if ( empty( $membership_level ) ) {
+			return 'Error! Failed to retrieve membership level ID from the membership info object.';
+		}
+		$level_identifier = md5( $membership_level );
+		$membership_level_alias = $membership_info->get( 'alias' );
+		$swpm_registration_submit = filter_input( INPUT_POST, 'swpm_registration_submit' );
+		if ( ! empty( $swpm_registration_submit ) ) {
+			$member = array_map( 'sanitize_text_field', $_POST );
+		}
+		ob_start();
+		extract( (array) $member, EXTR_SKIP );
+		include SIMPLE_WP_MEMBERSHIP_PATH . 'views/add.php';
+		return ob_get_clean();
+	}
+
+	public function register_front_end() {
+
+		//If captcha is present and validation failed, it returns an error string. If validation succeeds, it returns an empty string.
+		$captcha_validation_output = apply_filters( 'swpm_validate_registration_form_submission', '' );
+		if ( ! empty( $captcha_validation_output ) ) {
+			$message = array(
+				'succeeded' => false,
+				'message'   => SwpmUtils::_( 'Security check: captcha validation failed.' ),
+			);
+			SwpmTransfer::get_instance()->set( 'status', $message );
+			return;
+		}
+
+		//Check if Terms and Conditions enabled
+		$terms_enabled = SwpmSettings::get_instance()->get_value( 'enable-terms-and-conditions' );
+		if ( ! empty( $terms_enabled ) ) {
+			//check if user checked "I accept terms" checkbox
+			if ( empty( $_POST['accept_terms'] ) ) {
+				$message = array(
+					'succeeded' => false,
+					'message'   => SwpmUtils::_( 'You must accept the terms and conditions.' ),
+				);
+				SwpmTransfer::get_instance()->set( 'status', $message );
+				return;
+			}
+		}
+
+		//Check if Privacy Policy enabled
+		$pp_enabled = SwpmSettings::get_instance()->get_value( 'enable-privacy-policy' );
+		if ( ! empty( $pp_enabled ) ) {
+			//check if user checked "I agree with Privacy Policy" checkbox
+			if ( empty( $_POST['accept_pp'] ) ) {
+				$message = array(
+					'succeeded' => false,
+					'message'   => SwpmUtils::_( 'You must agree to the privacy policy.' ),
+				);
+				SwpmTransfer::get_instance()->set( 'status', $message );
+				return;
+			}
+		}
+
+		//Validate swpm level hash data.
+		$hash_val_posted = sanitize_text_field( $_POST['swpm_level_hash'] );
+		$level_value     = sanitize_text_field( $_POST['membership_level'] );
+		$swpm_p_key      = get_option( 'swpm_private_key_one' );
+		$hash_val        = md5( $swpm_p_key . '|' . $level_value );
+		if ( $hash_val != $hash_val_posted ) {//Level hash validation failed.
+			$msg  = '<p>Error! Security check failed for membership level validation.</p>';
+			$msg .= '<p>The submitted membership level data does not seem to be authentic.</p>';
+			$msg .= '<p>If you are using caching please empty the cache data and try again.</p>';
+			wp_die( $msg );
+		}
+
+		$this->email_activation = get_option( 'swpm_email_activation_lvl_' . $level_value );
+
+		//Crete the member profile and send notification
+		if ( $this->create_swpm_user() && $this->prepare_and_create_wp_user_front_end() && $this->send_reg_email() ) {
+			do_action( 'swpm_front_end_registration_complete' ); //Keep this action hook for people who are using it (so their implementation doesn't break).
+			do_action( 'swpm_front_end_registration_complete_user_data', $this->member_info );
+
+			//Check if there is after registration redirect
+			if ( ! $this->email_activation ) {
+				$after_rego_url = SwpmSettings::get_instance()->get_value( 'after-rego-redirect-page-url' );
+				$after_rego_url = apply_filters( 'swpm_after_registration_redirect_url', $after_rego_url );
+				if ( ! empty( $after_rego_url ) ) {
+					//Yes. Need to redirect to this after registration page
+					SwpmLog::log_simple_debug( 'After registration redirect is configured in settings. Redirecting user to: ' . $after_rego_url, true );
+					wp_redirect( $after_rego_url );
+					exit( 0 );
+				}
+			}
+
+			//Set the registration complete message
+			if ( $this->email_activation ) {
+				$email_act_msg  = '<div class="swpm-registration-success-msg">';
+				$email_act_msg .= SwpmUtils::_( 'You need to confirm your email address. Please check your email and follow instructions to complete your registration.' );
+				$email_act_msg .= '</div>';
+                                $email_act_msg = apply_filters( 'swpm_registration_email_activation_msg', $email_act_msg );//Can be added to the custom messages addon.
+				$message        = array(
+					'succeeded' => true,
+					'message'   => $email_act_msg,
+				);
+			} else {
+				$login_page_url = SwpmSettings::get_instance()->get_value( 'login-page-url' );
+
+				// Allow hooks to change the value of login_page_url
+				$login_page_url = apply_filters('swpm_register_front_end_login_page_url', $login_page_url);
+
+				$after_rego_msg = '<div class="swpm-registration-success-msg">' . SwpmUtils::_( 'Registration Successful. ' ) . SwpmUtils::_( 'Please' ) . ' <a href="' . $login_page_url . '">' . SwpmUtils::_( 'Login' ) . '</a></div>';
+				$after_rego_msg = apply_filters( 'swpm_registration_success_msg', $after_rego_msg );
+				$message        = array(
+					'succeeded' => true,
+					'message'   => $after_rego_msg,
+				);
+			}
+			SwpmTransfer::get_instance()->set( 'status', $message );
+			return;
+		}
+	}
+
+	private function create_swpm_user() {
+		global $wpdb;
+		$member = SwpmTransfer::$default_fields;
+		$form   = new SwpmFrontForm( $member );
+		if ( ! $form->is_valid() ) {
+			$message = array(
+				'succeeded' => false,
+				'message'   => SwpmUtils::_( 'Please correct the following' ),
+				'extra'     => $form->get_errors(),
+			);
+			SwpmTransfer::get_instance()->set( 'status', $message );
+			return false;
+		}
+
+		$member_info = $form->get_sanitized_member_form_data();
+
+		//Check if the email belongs to an existing wp user account with admin role.
+                SwpmMemberUtils::check_and_die_if_email_belongs_to_admin_user($member_info['email']);
+
+		//Go ahead and create the SWPM user record.
+		$free_level                           = SwpmUtils::get_free_level();
+		$account_status                       = SwpmSettings::get_instance()->get_value( 'default-account-status', 'active' );
+		$member_info['last_accessed_from_ip'] = SwpmUtils::get_user_ip_address();
+		$member_info['member_since']          = SwpmUtils::get_current_date_in_wp_zone(); //date( 'Y-m-d' );
+		$member_info['subscription_starts']   = SwpmUtils::get_current_date_in_wp_zone(); //date( 'Y-m-d' );
+		$member_info['account_state']         = $account_status;
+		if ( $this->email_activation ) {
+			$member_info['account_state'] = 'activation_required';
+		}
+		$plain_password = $member_info['plain_password'];
+		unset( $member_info['plain_password'] );
+
+		if ( SwpmUtils::is_paid_registration() ) {
+			$member_info['reg_code'] = '';
+			$member_id               = filter_input( INPUT_GET, 'member_id', FILTER_SANITIZE_NUMBER_INT );
+			$code                    = filter_input( INPUT_GET, 'code', FILTER_SANITIZE_STRING );
+			$wpdb->update(
+				$wpdb->prefix . 'swpm_members_tbl',
+				$member_info,
+				array(
+					'member_id' => $member_id,
+					'reg_code'  => $code,
+				)
+			);
+
+			$query                           = $wpdb->prepare( 'SELECT membership_level FROM ' . $wpdb->prefix . 'swpm_members_tbl WHERE member_id=%d', $member_id );
+			$member_info['membership_level'] = $wpdb->get_var( $query );
+			$last_insert_id                  = $member_id;
+		} elseif ( ! empty( $free_level ) ) {
+			$member_info['membership_level'] = $free_level;
+			$wpdb->insert( $wpdb->prefix . 'swpm_members_tbl', $member_info );
+			$last_insert_id = $wpdb->insert_id;
+		} else {
+			$message = array(
+				'succeeded' => false,
+				'message'   => SwpmUtils::_( 'Membership Level Couldn\'t be found.' ),
+			);
+			SwpmTransfer::get_instance()->set( 'status', $message );
+			return false;
+		}
+		$member_info['plain_password'] = $plain_password;
+		$this->member_info             = $member_info;
+		return true;
+	}
+
+	private function prepare_and_create_wp_user_front_end() {
+		global $wpdb;
+		$member_info = $this->member_info;
+
+		//Retrieve the user role assigned for this level
+		$query     = $wpdb->prepare( 'SELECT role FROM ' . $wpdb->prefix . 'swpm_membership_tbl WHERE id = %d', $member_info['membership_level'] );
+		$user_role = $wpdb->get_var( $query );
+		//Check to make sure that the user role of this level is not admin.
+		if ( $user_role == 'administrator' ) {
+			//For security reasons we don't allow users with administrator role to be creted from the front-end. That can only be done from the admin dashboard side.
+			$error_msg  = '<p>Error! The user role for this membership level (level ID: ' . $member_info['membership_level'] . ') is set to "Administrator".</p>';
+			$error_msg .= '<p>For security reasons, member registration to this level is not permitted from the front end.</p>';
+			$error_msg .= '<p>An administrator of the site can manually create a member record with this access level from the admin dashboard side.</p>';
+			wp_die( $error_msg );
+		}
+
+		$wp_user_info                    = array();
+		$wp_user_info['user_nicename']   = implode( '-', explode( ' ', $member_info['user_name'] ) );
+		$wp_user_info['display_name']    = $member_info['user_name'];
+		$wp_user_info['user_email']      = $member_info['email'];
+		$wp_user_info['nickname']        = $member_info['user_name'];
+		$wp_user_info['first_name']      = $member_info['first_name'];
+		$wp_user_info['last_name']       = $member_info['last_name'];
+		$wp_user_info['user_login']      = $member_info['user_name'];
+		$wp_user_info['password']        = $member_info['plain_password'];
+		$wp_user_info['role']            = $user_role;
+		$wp_user_info['user_registered'] = date( 'Y-m-d H:i:s' );
+		SwpmUtils::create_wp_user( $wp_user_info );
+		return true;
+	}
+
+	public function edit_profile_front_end() {
+		global $wpdb;
+		//Check that the member is logged in
+		$auth = SwpmAuth::get_instance();
+		if ( ! $auth->is_logged_in() ) {
+			return;
+		}
+
+		//Check nonce
+		if ( ! isset( $_POST['swpm_profile_edit_nonce_val'] ) || ! wp_verify_nonce( $_POST['swpm_profile_edit_nonce_val'], 'swpm_profile_edit_nonce_action' ) ) {
+			//Nonce check failed.
+			wp_die( SwpmUtils::_( 'Error! Nonce verification failed for front end profile edit.' ) );
+		}
+
+		$user_data = (array) $auth->userData;
+		unset( $user_data['permitted'] );
+		$form = new SwpmForm( $user_data );
+		if ( $form->is_valid() ) {
+			global $wpdb;
+			$msg_str = '<div class="swpm-profile-update-success">' . SwpmUtils::_( 'Profile updated successfully.' ) . '</div>';
+			$message = array(
+				'succeeded' => true,
+				'message'   => $msg_str,
+			);
+
+			$member_info = $form->get_sanitized_member_form_data();
+			SwpmUtils::update_wp_user( $auth->get( 'user_name' ), $member_info ); //Update corresponding wp user record.
+
+			//Lets check if password was also changed.
+			$password_also_changed = false;
+			if ( isset( $member_info['plain_password'] ) ) {
+				//Password was also changed.
+				$msg_str = '<div class="swpm-profile-update-success">' . SwpmUtils::_( 'Profile updated successfully. You will need to re-login since you changed your password.' ) . '</div>';
+				$message = array(
+					'succeeded' => true,
+					'message'   => $msg_str,
+				);
+				unset( $member_info['plain_password'] );
+				//Set the password chagned flag.
+				$password_also_changed = true;
+			}
+
+			//Update the data in the swpm database.
+			$swpm_id = $auth->get( 'member_id' );
+			//SwpmLog::log_simple_debug("Updating member profile data with SWPM ID: " . $swpm_id, true);
+			$member_info = array_filter( $member_info );//Remove any null values.
+			$wpdb->update( $wpdb->prefix . 'swpm_members_tbl', $member_info, array( 'member_id' => $swpm_id ) );
+			$auth->reload_user_data();//Reload user data after update so the profile page reflects the new data.
+
+			if ( $password_also_changed ) {
+				//Password was also changed. Logout the user's current session.
+				wp_logout(); //Log the user out from the WP user session also.
+				SwpmLog::log_simple_debug( 'Member has updated the password from profile edit page. Logging the user out so he can re-login using the new password.', true );
+			}
+
+			SwpmTransfer::get_instance()->set( 'status', $message );
+
+			do_action( 'swpm_front_end_profile_edited', $member_info );
+			return true; //Successful form submission.
+		} else {
+			$msg_str = '<div class="swpm-profile-update-error">' . SwpmUtils::_( 'Please correct the following.' ) . '</div>';
+			$message = array(
+				'succeeded' => false,
+				'message'   => $msg_str,
+				'extra'     => $form->get_errors(),
+			);
+			SwpmTransfer::get_instance()->set( 'status', $message );
+			return false; //Error in the form submission.
+		}
+	}
+
+	public function reset_password( $email ) {
+
+		//If captcha is present and validation failed, it returns an error string. If validation succeeds, it returns an empty string.
+		$captcha_validation_output = apply_filters( 'swpm_validate_pass_reset_form_submission', '' );
+		if ( ! empty( $captcha_validation_output ) ) {
+			$message = '<div class="swpm-reset-pw-error">' . SwpmUtils::_( 'Captcha validation failed.' ) . '</div>';
+			$message = array(
+				'succeeded' => false,
+				'message'   => $message,
+			);
+			SwpmTransfer::get_instance()->set( 'status', $message );
+			return;
+		}
+
+		$email = sanitize_email( $email );
+		if ( ! is_email( $email ) ) {
+			$message = '<div class="swpm-reset-pw-error">' . SwpmUtils::_( 'Email address not valid.' ) . '</div>';
+			$message = array(
+				'succeeded' => false,
+				'message'   => $message,
+			);
+			SwpmTransfer::get_instance()->set( 'status', $message );
+			return;
+		}
+		global $wpdb;
+		$query = 'SELECT member_id,user_name,first_name, last_name FROM ' .
+				$wpdb->prefix . 'swpm_members_tbl ' .
+				' WHERE email = %s';
+		$user  = $wpdb->get_row( $wpdb->prepare( $query, $email ) );
+		if ( empty( $user ) ) {
+			$message  = '<div class="swpm-reset-pw-error">' . SwpmUtils::_( 'No user found with that email address.' ) . '</div>';
+			$message .= '<div class="swpm-reset-pw-error-email">' . SwpmUtils::_( 'Email Address: ' ) . $email . '</div>';
+			$message  = array(
+				'succeeded' => false,
+				'message'   => $message,
+			);
+			SwpmTransfer::get_instance()->set( 'status', $message );
+			return;
+		}
+		$settings = SwpmSettings::get_instance();
+		$password = wp_generate_password();
+
+		$password_hash = SwpmUtils::encrypt_password( trim( $password ) ); //should use $saned??;
+		$wpdb->update( $wpdb->prefix . 'swpm_members_tbl', array( 'password' => $password_hash ), array( 'member_id' => $user->member_id ) );
+
+		//Update wp user password
+		add_filter( 'send_password_change_email', array( &$this, 'dont_send_password_change_email' ), 1, 3 ); //Stop WordPress from sending a reset password email to admin.
+		SwpmUtils::update_wp_user( $user->user_name, array( 'plain_password' => $password ) );
+
+		$body            = $settings->get_value( 'reset-mail-body' );
+		$subject         = $settings->get_value( 'reset-mail-subject' );
+		$body            = html_entity_decode( $body );
+		$additional_args = array( 'password' => $password );
+		$body            = SwpmMiscUtils::replace_dynamic_tags( $body, $user->member_id, $additional_args );
+		$from            = $settings->get_value( 'email-from' );
+		$headers         = 'From: ' . $from . "\r\n";
+		$subject         = apply_filters( 'swpm_email_password_reset_subject', $subject );
+		$body            = apply_filters( 'swpm_email_password_reset_body', $body );
+		SwpmMiscUtils::mail( $email, $subject, $body, $headers );
+		SwpmLog::log_simple_debug( 'Member password has been reset. Password reset email sent to: ' . $email, true );
+
+		$message  = '<div class="swpm-reset-pw-success-box">';
+		$message .= '<div class="swpm-reset-pw-success">' . SwpmUtils::_( 'New password has been sent to your email address.' ) . '</div>';
+		$message .= '<div class="swpm-reset-pw-success-email">' . SwpmUtils::_( 'Email Address: ' ) . $email . '</div>';
+		$message .= '</div>';
+
+		$message = array(
+			'succeeded'       => false,
+			'message'         => $message,
+			'pass_reset_sent' => true,
+		);
+		SwpmTransfer::get_instance()->set( 'status', $message );
+	}
+
+	function dont_send_password_change_email( $send = false, $user = '', $userdata = '' ) {
+		//Stop the WordPress's default password change email notification to site admin
+		//Only the simple membership plugin's password reset email will be sent.
+		return false;
+	}
+
+	public function email_activation() {
+		$login_page_url = SwpmSettings::get_instance()->get_value( 'login-page-url' );
+
+		// Allow hooks to change the value of login_page_url
+		$login_page_url = apply_filters('swpm_email_activation_login_page_url', $login_page_url);
+
+		$member_id = FILTER_INPUT( INPUT_GET, 'swpm_member_id', FILTER_SANITIZE_NUMBER_INT );
+
+		$member = SwpmMemberUtils::get_user_by_id( $member_id );
+		if ( empty( $member ) ) {
+			//can't find member
+			echo SwpmUtils::_( "Can't find member account." );
+			wp_die();
+		}
+		if ( $member->account_state !== 'activation_required' ) {
+			//account already active
+			echo SwpmUtils::_( 'Account already active. ' ) . '<a href="' . $login_page_url . '">' . SwpmUtils::_( 'click here' ) . '</a>' . SwpmUtils::_( ' to login.' );
+			wp_die();
+		}
+		$code     = FILTER_INPUT( INPUT_GET, 'swpm_token', FILTER_SANITIZE_STRING );
+		$act_data = get_option( 'swpm_email_activation_data_usr_' . $member_id );
+		if ( empty( $code ) || empty( $act_data ) || $act_data['act_code'] !== $code ) {
+			//code mismatch
+			wp_die( SwpmUtils::_( 'Activation code mismatch. Cannot activate this account. Please contact the site admin.' ) );
+		}
+		//activation code match
+		delete_option( 'swpm_email_activation_data_usr_' . $member_id );
+		//store rego form id in constant so FB addon could use it
+		if ( ! empty( $act_data['fb_form_id'] ) ) {
+			define( 'SWPM_EMAIL_ACTIVATION_FORM_ID', $act_data['fb_form_id'] );
+		}
+		$activation_account_status = apply_filters( 'swpm_activation_feature_override_account_status', 'active' );
+		SwpmMemberUtils::update_account_state( $member_id, $activation_account_status );
+		$this->member_info                   = (array) $member;
+		$this->member_info['plain_password'] = SwpmUtils::crypt( $act_data['plain_password'], 'd' );
+		$this->send_reg_email();
+
+		$msg = '<div class="swpm_temporary_msg" style="font-weight: bold;">' . SwpmUtils::_( 'Success! Your account has been activated successfully.' ) . '</div>';
+
+		$after_rego_url = SwpmSettings::get_instance()->get_value( 'after-rego-redirect-page-url' );
+		$after_rego_url = apply_filters( 'swpm_after_registration_redirect_url', $after_rego_url );
+		if ( ! empty( $after_rego_url ) ) {
+			//Yes. Need to redirect to this after registration page
+			SwpmLog::log_simple_debug( 'After registration redirect is configured in settings. Redirecting user to: ' . $after_rego_url, true );
+			SwpmMiscUtils::show_temporary_message_then_redirect( $msg, $after_rego_url );
+			exit( 0 );
+		}
+
+		//show success message and redirect to login page
+		SwpmMiscUtils::show_temporary_message_then_redirect( $msg, $login_page_url );
+		exit( 0 );
+	}
+
+	public function resend_activation_email() {
+		$login_page_url = SwpmSettings::get_instance()->get_value( 'login-page-url' );
+
+		// Allow hooks to change the value of login_page_url
+		$login_page_url = apply_filters('swpm_resend_activation_email_login_page_url', $login_page_url);
+
+		$member_id = FILTER_INPUT( INPUT_GET, 'swpm_member_id', FILTER_SANITIZE_NUMBER_INT );
+
+		$member = SwpmMemberUtils::get_user_by_id( $member_id );
+		if ( empty( $member ) ) {
+			//can't find member
+			echo SwpmUtils::_( 'Cannot find member account.' );
+			wp_die();
+		}
+		if ( $member->account_state !== 'activation_required' ) {
+			//account already active
+			$acc_active_msg = SwpmUtils::_( 'Account already active. ' ) . '<a href="' . $login_page_url . '">' . SwpmUtils::_( 'click here' ) . '</a>' . SwpmUtils::_( ' to login.' );
+			echo $acc_active_msg;
+			wp_die();
+		}
+		$act_data = get_option( 'swpm_email_activation_data_usr_' . $member_id );
+		if ( ! empty( $act_data ) ) {
+			//looks like activation data has been removed for some reason. We won't be able to have member's plain password in this case
+			$act_data['plain_password'] = '';
+		}
+
+		delete_option( 'swpm_email_activation_data_usr_' . $member_id );
+
+		$this->member_info                   = (array) $member;
+		$this->member_info['plain_password'] = SwpmUtils::crypt( $act_data['plain_password'], 'd' );
+		$this->email_activation              = true;
+		$this->send_reg_email();
+
+		$msg = '<div class="swpm_temporary_msg" style="font-weight: bold;">' . SwpmUtils::_( 'Activation email has been sent. Please check your email and activate your account.' ) . '</div>';
+		SwpmMiscUtils::show_temporary_message_then_redirect( $msg, $login_page_url );
+		wp_die();
+	}
+
+}

+ 214 - 0
simple-membership/classes/class.swpm-init-time-tasks.php

@@ -0,0 +1,214 @@
+<?php
+
+class SwpmInitTimeTasks {
+
+	public function __construct() {
+
+	}
+
+	public function do_init_tasks() {
+
+		//Set up localisation. First loaded ones will override strings present in later loaded file.
+		//Allows users to have a customized language in a different folder.
+		$locale = apply_filters( 'plugin_locale', get_locale(), 'simple-membership' );
+		load_textdomain( 'simple-membership', WP_LANG_DIR . "/simple-membership-$locale.mo" );
+		load_plugin_textdomain( 'simple-membership', false, SIMPLE_WP_MEMBERSHIP_DIRNAME . '/languages/' );
+
+		if ( ! isset( $_COOKIE['swpm_session'] ) ) { // give a unique ID to current session.
+			$uid                     = md5( microtime() );
+			$_COOKIE['swpm_session'] = $uid; // fake it for current session/
+			if ( ! headers_sent() ) {
+				setcookie( 'swpm_session', $uid, 0, '/' );
+			}
+		}
+
+		//Crete the custom post types
+		$this->create_post_type();
+
+		//Do frontend-only init time tasks
+		if ( ! is_admin() ) {
+			SwpmAuth::get_instance();
+
+			$this->check_and_handle_auto_login();
+			$this->verify_and_delete_account();
+
+			$swpm_logout = filter_input( INPUT_GET, 'swpm-logout' );
+			if ( ! empty( $swpm_logout ) ) {
+				SwpmAuth::get_instance()->logout();
+				$redirect_url = apply_filters( 'swpm_after_logout_redirect_url', SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL );
+				wp_redirect( trailingslashit( $redirect_url ) );
+				exit( 0 );
+			}
+			$this->process_password_reset();
+			$this->register_member();
+			$this->check_and_do_email_activation();
+			$this->edit_profile();
+			SwpmCommentFormRelated::check_and_restrict_comment_posting_to_members();
+		} else {
+			//Do admin side init time tasks
+			if ( current_user_can( SWPM_MANAGEMENT_PERMISSION ) ) {
+				//Admin dashboard side stuff
+				$this->admin_init();
+			}
+		}
+	}
+
+	public function admin_init() {
+		$createswpmuser = filter_input( INPUT_POST, 'createswpmuser' );
+		if ( ! empty( $createswpmuser ) ) {
+			SwpmAdminRegistration::get_instance()->register_admin_end();
+		}
+		$editswpmuser = filter_input( INPUT_POST, 'editswpmuser' );
+		if ( ! empty( $editswpmuser ) ) {
+			$id = filter_input( INPUT_GET, 'member_id', FILTER_VALIDATE_INT );
+			SwpmAdminRegistration::get_instance()->edit_admin_end( $id );
+		}
+		$createswpmlevel = filter_input( INPUT_POST, 'createswpmlevel' );
+		if ( ! empty( $createswpmlevel ) ) {
+			SwpmMembershipLevel::get_instance()->create_level();
+		}
+		$editswpmlevel = filter_input( INPUT_POST, 'editswpmlevel' );
+		if ( ! empty( $editswpmlevel ) ) {
+			$id = filter_input( INPUT_GET, 'id' );
+			SwpmMembershipLevel::get_instance()->edit_level( $id );
+		}
+		$update_category_list = filter_input( INPUT_POST, 'update_category_list' );
+		if ( ! empty( $update_category_list ) ) {
+			include_once 'class.swpm-category-list.php';
+			SwpmCategoryList::update_category_list();
+		}
+		$update_post_list = filter_input( INPUT_POST, 'update_post_list' );
+		if ( ! empty( $update_post_list ) ) {
+			include_once 'class.swpm-post-list.php';
+			SwpmPostList::update_post_list();
+		}
+	}
+
+	public function create_post_type() {
+		//The payment button data for membership levels will be stored using this CPT
+		register_post_type(
+			'swpm_payment_button',
+			array(
+				'public'             => false,
+				'publicly_queryable' => false,
+				'show_ui'            => false,
+				'query_var'          => false,
+				'rewrite'            => false,
+				'capability_type'    => 'page',
+				'has_archive'        => false,
+				'hierarchical'       => false,
+				'supports'           => array( 'title', 'editor' ),
+			)
+		);
+
+		//Transactions will be stored using this CPT in parallel with swpm_payments_tbl DB table
+		$args = array(
+			'supports'            => array( '' ),
+			'hierarchical'        => false,
+			'public'              => false,
+			'show_ui'             => false,
+			'can_export'          => false,
+			'has_archive'         => false,
+			'exclude_from_search' => true,
+			'publicly_queryable'  => false,
+			'capability_type'     => 'post',
+		);
+		register_post_type( 'swpm_transactions', $args );
+	}
+
+	private function verify_and_delete_account() {
+		include_once SIMPLE_WP_MEMBERSHIP_PATH . 'classes/class.swpm-members.php';
+		$delete_account = filter_input( INPUT_GET, 'swpm_delete_account' );
+		if ( empty( $delete_account ) ) {
+			return;
+		}
+		$password = filter_input( INPUT_POST, 'account_delete_confirm_pass', FILTER_UNSAFE_RAW );
+
+		$auth = SwpmAuth::get_instance();
+		if ( ! $auth->is_logged_in() ) {
+			return;
+		}
+		if ( empty( $password ) ) {
+			SwpmUtils::account_delete_confirmation_ui();
+		}
+
+		$nonce_field = filter_input( INPUT_POST, 'account_delete_confirm_nonce' );
+		if ( empty( $nonce_field ) || ! wp_verify_nonce( $nonce_field, 'swpm_account_delete_confirm' ) ) {
+			SwpmUtils::account_delete_confirmation_ui( SwpmUtils::_( 'Sorry, Nonce verification failed.' ) );
+		}
+		if ( $auth->match_password( $password ) ) {
+			$auth->delete();
+			wp_safe_redirect( get_home_url() );
+			exit( 0 );
+		} else {
+			SwpmUtils::account_delete_confirmation_ui( SwpmUtils::_( "Sorry, Password didn't match." ) );
+		}
+	}
+
+	public function process_password_reset() {
+		$message          = '';
+		$swpm_reset       = filter_input( INPUT_POST, 'swpm-reset' );
+		$swpm_reset_email = filter_input( INPUT_POST, 'swpm_reset_email', FILTER_UNSAFE_RAW );
+		if ( ! empty( $swpm_reset ) ) {
+			SwpmFrontRegistration::get_instance()->reset_password( $swpm_reset_email );
+		}
+	}
+
+	private function register_member() {
+		$registration = filter_input( INPUT_POST, 'swpm_registration_submit' );
+		if ( ! empty( $registration ) ) {
+			SwpmFrontRegistration::get_instance()->register_front_end();
+		}
+	}
+
+	private function check_and_do_email_activation() {
+		$email_activation = filter_input( INPUT_GET, 'swpm_email_activation', FILTER_SANITIZE_NUMBER_INT );
+		if ( ! empty( $email_activation ) ) {
+			SwpmFrontRegistration::get_instance()->email_activation();
+		}
+		//also check activation email resend request
+		$email_activation_resend = filter_input( INPUT_GET, 'swpm_resend_activation_email', FILTER_SANITIZE_NUMBER_INT );
+		if ( ! empty( $email_activation_resend ) ) {
+			SwpmFrontRegistration::get_instance()->resend_activation_email();
+		}
+	}
+
+	private function edit_profile() {
+		$swpm_editprofile_submit = filter_input( INPUT_POST, 'swpm_editprofile_submit' );
+		if ( ! empty( $swpm_editprofile_submit ) ) {
+			SwpmFrontRegistration::get_instance()->edit_profile_front_end();
+			//TODO - allow an option to do a redirect if successful edit profile form submission?
+		}
+	}
+
+	public function check_and_handle_auto_login() {
+
+		if ( isset( $_REQUEST['swpm_auto_login'] ) && $_REQUEST['swpm_auto_login'] == '1' ) {
+			//Handle the auto login
+			SwpmLog::log_simple_debug( 'Handling auto login request...', true );
+
+			$enable_auto_login = SwpmSettings::get_instance()->get_value( 'auto-login-after-rego' );
+			if ( empty( $enable_auto_login ) ) {
+				SwpmLog::log_simple_debug( 'Auto login after registration feature is disabled in settings.', true );
+				return;
+			}
+
+			//Check auto login nonce value
+			$auto_login_nonce = isset( $_REQUEST['swpm_auto_login_nonce'] ) ? $_REQUEST['swpm_auto_login_nonce'] : '';
+			if ( ! wp_verify_nonce( $auto_login_nonce, 'swpm-auto-login-nonce' ) ) {
+				SwpmLog::log_simple_debug( 'Error! Auto login nonce verification check failed!', false );
+				wp_die( 'Auto login nonce verification check failed!' );
+			}
+
+			//Perform the login
+			$auth         = SwpmAuth::get_instance();
+			$user         = apply_filters( 'swpm_user_name', filter_input( INPUT_GET, 'swpm_user_name' ) );
+			$user         = sanitize_user( $user );
+			$encoded_pass = filter_input( INPUT_GET, 'swpm_encoded_pw' );
+			$pass         = base64_decode( $encoded_pass );
+			$auth->login( $user, $pass );
+			SwpmLog::log_simple_debug( 'Auto login request completed for: ' . $user, true );
+		}
+	}
+
+}

+ 284 - 0
simple-membership/classes/class.swpm-installation.php

@@ -0,0 +1,284 @@
+<?php
+
+/**
+ * Description of BInstallation
+ *
+ * @author nur
+ */
+class SwpmInstallation {
+    /*
+     * This function is capable of handing both single site or multi-site install and upgrade all in one.
+     */
+
+    static function run_safe_installer() {
+        global $wpdb;
+
+        //Do this if multi-site setup
+        if (function_exists('is_multisite') && is_multisite()) {
+            // check if it is a network activation - if so, run the activation function for each blog id
+            if (isset($_GET['networkwide']) && ($_GET['networkwide'] == 1)) {
+                $old_blog = $wpdb->blogid;
+                // Get all blog ids
+                $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
+                foreach ($blogids as $blog_id) {
+                    switch_to_blog($blog_id);
+                    SwpmInstallation::installer();
+                    SwpmInstallation::initdb();
+                }
+                switch_to_blog($old_blog);
+                return;
+            }
+        }
+
+        //Do this if single site standard install
+        SwpmInstallation::installer();
+        SwpmInstallation::initdb();
+    }
+
+    public static function installer() {
+        global $wpdb;
+        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
+
+        $charset_collate = '';
+        if (!empty($wpdb->charset)) {
+            $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
+        } else {
+            $charset_collate = "DEFAULT CHARSET=utf8";
+        }
+        if (!empty($wpdb->collate)) {
+            $charset_collate .= " COLLATE $wpdb->collate";
+        }
+
+        $sql = "CREATE TABLE " . $wpdb->prefix . "swpm_members_tbl (
+			member_id int(12) NOT NULL PRIMARY KEY AUTO_INCREMENT,
+			user_name varchar(255) NOT NULL,
+			first_name varchar(64) DEFAULT '',
+			last_name varchar(64) DEFAULT '',
+			password varchar(255) NOT NULL,
+			member_since date NOT NULL DEFAULT '0000-00-00',
+			membership_level smallint(6) NOT NULL,
+			more_membership_levels VARCHAR(100) DEFAULT NULL,
+			account_state enum('active','inactive','activation_required','expired','pending','unsubscribed') DEFAULT 'pending',
+			last_accessed datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+			last_accessed_from_ip varchar(128) NOT NULL,
+			email varchar(255) DEFAULT NULL,
+			phone varchar(64) DEFAULT NULL,
+			address_street varchar(255) DEFAULT NULL,
+			address_city varchar(255) DEFAULT NULL,
+			address_state varchar(255) DEFAULT NULL,
+			address_zipcode varchar(255) DEFAULT NULL,
+			home_page varchar(255) DEFAULT NULL,
+			country varchar(255) DEFAULT NULL,
+			gender enum('male','female','not specified') DEFAULT 'not specified',
+			referrer varchar(255) DEFAULT NULL,
+			extra_info text,
+			reg_code varchar(255) DEFAULT NULL,
+			subscription_starts date DEFAULT NULL,
+			initial_membership_level smallint(6) DEFAULT NULL,
+			txn_id varchar(255) DEFAULT '',
+			subscr_id varchar(255) DEFAULT '',
+			company_name varchar(255) DEFAULT '',
+			notes text DEFAULT NULL,
+			flags int(11) DEFAULT '0',
+			profile_image varchar(255) DEFAULT ''
+          )" . $charset_collate . ";";
+        dbDelta($sql);
+
+        $sql = "CREATE TABLE " . $wpdb->prefix . "swpm_membership_tbl (
+			id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
+			alias varchar(127) NOT NULL,
+			role varchar(255) NOT NULL DEFAULT 'subscriber',
+			permissions tinyint(4) NOT NULL DEFAULT '0',
+			subscription_period varchar(11) NOT NULL DEFAULT '-1',
+                        subscription_duration_type tinyint NOT NULL default 0,
+			subscription_unit   VARCHAR(20)        NULL,
+			loginredirect_page  text NULL,
+			category_list longtext,
+			page_list longtext,
+			post_list longtext,
+			comment_list longtext,
+			attachment_list longtext,
+			custom_post_list longtext,
+			disable_bookmark_list longtext,
+			options longtext,
+                        protect_older_posts  tinyint(1) NOT NULL DEFAULT '0',
+			campaign_name varchar(255) NOT NULL DEFAULT ''
+          )" . $charset_collate . " AUTO_INCREMENT=1 ;";
+        dbDelta($sql);
+        $sql = "SELECT * FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id = 1";
+        $results = $wpdb->get_row($sql);
+        if (is_null($results)) {
+            $sql = "INSERT INTO  " . $wpdb->prefix . "swpm_membership_tbl  (
+			id ,
+			alias ,
+			role ,
+			permissions ,
+			subscription_period ,
+			subscription_unit,
+			loginredirect_page,
+			category_list ,
+			page_list ,
+			post_list ,
+			comment_list,
+			disable_bookmark_list,
+			options,
+			campaign_name
+			)VALUES (1 , 'Content Protection', 'administrator', '15', '0',NULL,NULL, NULL , NULL , NULL , NULL,NULL,NULL,'');";
+            $wpdb->query($sql);
+        }
+        $sql = "UPDATE  " . $wpdb->prefix . "swpm_membership_tbl SET subscription_duration_type = 1 WHERE subscription_unit='days' AND subscription_duration_type = 0";
+        $wpdb->query($sql);
+
+        $sql = "UPDATE  " . $wpdb->prefix . "swpm_membership_tbl SET subscription_duration_type = 2 WHERE subscription_unit='weeks' AND subscription_duration_type = 0";
+        $wpdb->query($sql);
+
+        $sql = "UPDATE  " . $wpdb->prefix . "swpm_membership_tbl SET subscription_duration_type = 3 WHERE subscription_unit='months' AND subscription_duration_type = 0";
+        $wpdb->query($sql);
+
+        $sql = "UPDATE  " . $wpdb->prefix . "swpm_membership_tbl SET subscription_duration_type = 4 WHERE subscription_unit='years' AND subscription_duration_type = 0";
+        $wpdb->query($sql);
+
+        $sql = "CREATE TABLE " . $wpdb->prefix . "swpm_membership_meta_tbl (
+                    id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+                    level_id int(11) NOT NULL,
+                    meta_key varchar(255) NOT NULL,
+                    meta_label varchar(255) NULL,
+                    meta_value text,
+                    meta_type varchar(255) NOT NULL DEFAULT 'text',
+                    meta_default text,
+                    meta_context varchar(255) NOT NULL DEFAULT 'default',
+                    KEY level_id (level_id)
+        )" . $charset_collate . " AUTO_INCREMENT=1;";
+        dbDelta($sql);
+
+        $sql = "CREATE TABLE " . $wpdb->prefix . "swpm_payments_tbl (
+                    id int(12) NOT NULL PRIMARY KEY AUTO_INCREMENT,
+                    email varchar(255) DEFAULT NULL,
+                    first_name varchar(64) DEFAULT '',
+                    last_name varchar(64) DEFAULT '',
+                    member_id varchar(16) DEFAULT '',
+                    membership_level varchar(64) DEFAULT '',
+                    txn_date date NOT NULL default '0000-00-00',
+                    txn_id varchar(255) NOT NULL default '',
+                    subscr_id varchar(255) NOT NULL default '',
+                    reference varchar(255) NOT NULL default '',
+                    payment_amount varchar(32) NOT NULL default '',
+                    gateway varchar(32) DEFAULT '',
+                    status varchar(255) DEFAULT '',
+                    ip_address varchar(128) default ''
+                    )" . $charset_collate . ";";
+        dbDelta($sql);
+
+        //Save the current DB version
+        update_option("swpm_db_version", SIMPLE_WP_MEMBERSHIP_DB_VER);
+    }
+
+    public static function initdb() {
+        $settings = SwpmSettings::get_instance();
+
+        $installed_version = $settings->get_value('swpm-active-version');
+
+        //Set other default settings values
+        $reg_prompt_email_subject = "Complete your registration";
+        $reg_prompt_email_body = "Dear {first_name} {last_name}" .
+                "\n\nThank you for joining us!" .
+                "\n\nPlease complete your registration by visiting the following link:" .
+                "\n\n{reg_link}" .
+                "\n\nThank You";
+        $reg_email_subject = "Your registration is complete";
+        $reg_email_body = "Dear {first_name} {last_name}\n\n" .
+                "Your registration is now complete!\n\n" .
+                "Registration details:\n" .
+                "Username: {user_name}\n" .
+                "Password: {password}\n\n" .
+                "Please login to the member area at the following URL:\n\n" .
+                "{login_link}\n\n" .
+                "Thank You";
+        $reg_email_subject_admin = "Notification of New Member Registration";
+        $reg_email_body_admin = "A new member has completed the registration.\n\n" .
+                "Username: {user_name}\n" .
+                "Email: {email}\n\n" .
+                "Please login to the admin dashboard to view details of this user.\n\n" .
+                "You can customize this email message from the Email Settings menu of the plugin.\n\n" .
+                "Thank You";
+
+        $upgrade_email_subject = "Subject for email sent after account upgrade";
+        $upgrade_email_body = "Dear {first_name} {last_name}" .
+                "\n\nYour Account Has Been Upgraded." .
+                "\n\nThank You";
+        $reset_email_subject = get_bloginfo('name') . ": New Password";
+        $reset_email_body = "Dear {first_name} {last_name}" .
+                "\n\nHere is your new password:" .
+                "\n\nUsername: {user_name}" .
+                "\nPassword: {password}" .
+                "\n\nYou can change the password from the edit profile section of the site (after you log into the site)" .
+                "\n\nThank You";
+
+        $status_change_email_subject = "Account Updated!";
+        $status_change_email_body = "Dear {first_name} {last_name}," .
+                "\n\nYour account status has been updated!" .
+                " Please login to the member area at the following URL:" .
+                "\n\n {login_link}" .
+                "\n\nThank You";
+
+        $bulk_activate_email_subject = "Account Activated!";
+        $bulk_activate_email_body = "Hi," .
+                "\n\nYour account has been activated!" .
+                "\n\nYou can now login to the member area." .
+                "\n\nThank You";
+
+        $email_activation_mail_subject = "Action Required to Activate Your Account";
+        $email_activation_mail_body = "Dear {first_name}" .
+                "\n\nThank you for registering. To activate your account, please click on the following link (this will confirm your email address):" .
+                "\n\n{activation_link}" .
+                "\n\nThank You";
+
+        $curr_email_act_mail_subj = $settings->get_value('email-activation-mail-subject', false);
+        if ($curr_email_act_mail_subj === false) {
+            $settings->set_value('email-activation-mail-subject', stripslashes($email_activation_mail_subject));
+        }
+
+        $curr_email_act_mail_body = $settings->get_value('email-activation-mail-body', false);
+        if ($curr_email_act_mail_body === false) {
+            $settings->set_value('email-activation-mail-body', stripslashes($email_activation_mail_body));
+        }
+
+        if (empty($installed_version)) {
+            //Do fresh install tasks
+            //Create the mandatory pages (if they are not there)
+            SwpmMiscUtils::create_mandatory_wp_pages();
+            //End of page creation
+
+            $example_from_address = 'hello@' . SwpmMiscUtils::get_home_url_without_http_and_www();
+            $senders_email_address = get_bloginfo('name') . " <" . $example_from_address . ">";
+
+            $settings->set_value('reg-complete-mail-subject', stripslashes($reg_email_subject))
+                    ->set_value('reg-complete-mail-body', stripslashes($reg_email_body))
+                    ->set_value('reg-prompt-complete-mail-subject', stripslashes($reg_prompt_email_subject))
+                    ->set_value('reg-prompt-complete-mail-body', stripslashes($reg_prompt_email_body))
+                    ->set_value('upgrade-complete-mail-subject', stripslashes($upgrade_email_subject))
+                    ->set_value('upgrade-complete-mail-body', stripslashes($upgrade_email_body))
+                    ->set_value('reset-mail-subject', stripslashes($reset_email_subject))
+                    ->set_value('reset-mail-body', stripslashes($reset_email_body))
+                    ->set_value('account-change-email-subject', stripslashes($status_change_email_subject))
+                    ->set_value('account-change-email-body', stripslashes($status_change_email_body))
+                    ->set_value('email-from', $senders_email_address);
+
+            $settings->set_value('reg-complete-mail-subject-admin', stripslashes($reg_email_subject_admin));
+            $settings->set_value('reg-complete-mail-body-admin', stripslashes($reg_email_body_admin));
+
+            $settings->set_value('bulk-activate-notify-mail-subject', stripslashes($bulk_activate_email_subject));
+            $settings->set_value('bulk-activate-notify-mail-body', stripslashes($bulk_activate_email_body));
+        }
+
+        if (version_compare($installed_version, SIMPLE_WP_MEMBERSHIP_VER) == -1) {
+            //Do upgrade tasks
+        }
+
+        $settings->set_value('swpm-active-version', SIMPLE_WP_MEMBERSHIP_VER)->save(); //save everything.
+        //Generate and save a swpm private key for this site
+        $unique_id = uniqid('', true);
+        add_option('swpm_private_key_one', $unique_id);
+    }
+
+}

+ 129 - 0
simple-membership/classes/class.swpm-level-form.php

@@ -0,0 +1,129 @@
+<?php
+
+class SwpmLevelForm {
+
+    protected $fields;
+    protected $op;
+    protected $errors;
+    protected $sanitized;
+
+    public function __construct($fields) {
+        $this->fields = $fields;
+        $this->sanitized = array();
+        $this->errors = array();
+        
+        foreach ($fields as $key => $value){
+            $this->$key();
+        }
+    }
+
+    protected function id() {
+        
+    }
+
+    protected function alias() {
+        $alias = filter_input(INPUT_POST, 'alias');
+        $this->sanitized['alias'] = sanitize_text_field($alias);
+    }
+
+    protected function role() {
+        $role = filter_input(INPUT_POST, 'role');
+        $this->sanitized['role'] = sanitize_text_field($role);
+    }
+
+    protected function permissions() {
+        $this->sanitized['permissions'] = 63;
+    }
+
+    protected function subscription_period() {
+        $subscript_duration_type = filter_input(INPUT_POST, 'subscription_duration_type');
+
+        if ($subscript_duration_type == SwpmMembershipLevel::NO_EXPIRY) {
+            $this->sanitized['subscription_period'] = "";
+            return;
+        }
+
+        $subscription_period = filter_input(INPUT_POST, 'subscription_period_'. $subscript_duration_type);
+        if (($subscript_duration_type == SwpmMembershipLevel::FIXED_DATE)){
+            $dateinfo = date_parse($subscription_period);
+            if ($dateinfo['warning_count']|| $dateinfo['error_count']){
+                $this->errors['subscription_period'] = SwpmUtils::_("Date format is not valid.");
+                return;
+            }
+            $this->sanitized['subscription_period'] = sanitize_text_field($subscription_period);
+            return;
+        }
+        
+        if (!is_numeric($subscription_period)) {
+            $this->errors['subscription_period'] = SwpmUtils::_("Access duration must be > 0.");
+            return;
+        }
+        $this->sanitized['subscription_period'] = sanitize_text_field($subscription_period);
+    }
+
+    protected function subscription_duration_type(){
+        $subscription_duration_type = filter_input(INPUT_POST, 'subscription_duration_type');
+        $this->sanitized['subscription_duration_type'] = $subscription_duration_type;
+        return;
+    }
+    protected function subscription_unit(){
+        
+    }
+    protected function loginredirect_page() {
+        
+    }
+
+    protected function category_list() {
+        
+    }
+
+    protected function page_list() {
+        
+    }
+
+    protected function post_list() {
+        
+    }
+
+    protected function comment_list() {
+        
+    }
+
+    protected function attachment_list() {
+        
+    }
+
+    protected function custom_post_list() {
+        
+    }
+
+    protected function disable_bookmark_list() {
+        
+    }
+
+    protected function options() {
+        
+    }
+
+    protected function campaign_name() {
+        
+    }
+
+    protected function protect_older_posts() {
+        $checked = filter_input(INPUT_POST, 'protect_older_posts');
+        $this->sanitized['protect_older_posts'] = empty($checked) ? 0 : 1;
+    }
+
+    public function is_valid() {
+        return count($this->errors) < 1;
+    }
+
+    public function get_sanitized() {
+        return $this->sanitized;
+    }
+
+    public function get_errors() {
+        return $this->errors;
+    }
+
+}

+ 154 - 0
simple-membership/classes/class.swpm-log.php

@@ -0,0 +1,154 @@
+<?php
+
+class SwpmLog {
+    private $error;
+    private $warn;
+    private $notice;
+    private static $intance;
+    private function __construct() {
+        $this->error  = array();
+        $this->warn   = array();
+        $this->notice = array();
+    }
+    public static function get_logger($context = ''){
+        $context = empty($context)? 'default': $context;
+        if (!isset(self::$intance[$context])){
+            self::$intance[$context] = new SwpmLog();
+        }
+        return self::$intance[$context];
+    }
+    public function error($msg){
+        $this->error[] = $msg;
+    }
+    public function warn($msg){
+        $this->warn[] = $msg;
+    }
+    public function debug($msg){
+        $this->notice[] = $msg;
+    }
+    public function get($to_screen = false){
+        $msg = '';
+        foreach ($this->error as $error ){
+            $msg .= 'ERROR: ' . $error . ($to_screen?"<br/>":"\n");
+        }
+        foreach($this->warn as $warn){
+            $msg .= 'WARN: ' . $warn . ($to_screen?"<br/>":"\n");
+        }
+        foreach ($this->notice as $notice){
+            $msg = 'NOTICE: ' . $notice . ($to_screen?"<br/>":"\n");
+        }
+        return $msg;
+    }
+    public static function writeall($path = ''){
+        if (empty($path)) {$path = SIMPLE_WP_MEMBERSHIP_PATH . 'log.txt';}
+        $fp = fopen($path, 'a');
+        $date = current_time('mysql');
+        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 = SwpmSettings::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
+        $log_timestamp = SwpmUtils::get_current_timestamp_for_debug_log();
+        $text = '[' . $log_timestamp . '] - ' . (($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
+    }
+
+    public static function log_array_data_to_debug($array_to_write, $success, $end = false) {
+        $settings = SwpmSettings::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
+        $log_timestamp = SwpmUtils::get_current_timestamp_for_debug_log();
+        $text = '[' . $log_timestamp . '] - ' . (($success) ? 'SUCCESS: ' : 'FAILURE: ') . "\n";
+        ob_start();
+        print_r($array_to_write);
+        $var = ob_get_contents();
+        ob_end_clean();
+        $text .= $var;
+
+        if ($end) {
+            $text .= "\n------------------------------------------------------------------\n\n";
+        }
+        // Write to log
+        $fp = fopen($debug_log_file_name, 'a');
+        fwrite($fp, $text);
+        fclose($fp);  // close file
+    }
+
+    public static function log_auth_debug($message, $success, $end = false) {
+        $settings = SwpmSettings::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-auth.txt';
+
+        // Timestamp
+        $log_timestamp = SwpmUtils::get_current_timestamp_for_debug_log();
+        $text = '[' . $log_timestamp . '] - ' . (($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
+    }
+
+    public static function reset_swmp_log_files() {
+        $log_reset = true;
+        $logfile_list = array(
+            SIMPLE_WP_MEMBERSHIP_PATH . '/log.txt',
+            SIMPLE_WP_MEMBERSHIP_PATH . '/log-auth.txt',
+        );
+
+        foreach ($logfile_list as $logfile) {
+            if (empty($logfile)) {
+                continue;
+            }
+
+            $log_timestamp = SwpmUtils::get_current_timestamp_for_debug_log();
+            $text = '[' . $log_timestamp . '] - 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;
+    }
+
+}

+ 780 - 0
simple-membership/classes/class.swpm-members.php

@@ -0,0 +1,780 @@
+<?php
+if ( ! class_exists( 'WP_List_Table' ) ) {
+	require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
+}
+
+class SwpmMembers extends WP_List_Table {
+
+	function __construct() {
+		parent::__construct(
+			array(
+				'singular' => SwpmUtils::_( 'Member' ),
+				'plural'   => SwpmUtils::_( 'Members' ),
+				'ajax'     => false,
+			)
+		);
+	}
+
+	function get_columns() {
+		return array(
+			'cb'                  => '<input type="checkbox" />',
+			'member_id'           => SwpmUtils::_( 'ID' ),
+			'user_name'           => SwpmUtils::_( 'Username' ),
+			'first_name'          => SwpmUtils::_( 'First Name' ),
+			'last_name'           => SwpmUtils::_( 'Last Name' ),
+			'email'               => SwpmUtils::_( 'Email' ),
+			'alias'               => SwpmUtils::_( 'Membership Level' ),
+			'subscription_starts' => SwpmUtils::_( 'Access Starts' ),
+			'account_state'       => SwpmUtils::_( 'Account State' ),
+			'last_accessed'       => SwpmUtils::_( 'Last Login Date' ),
+		);
+	}
+
+	function get_sortable_columns() {
+		return array(
+			'member_id'           => array( 'member_id', true ), //True means already sorted
+			'user_name'           => array( 'user_name', false ),
+			'first_name'          => array( 'first_name', false ),
+			'last_name'           => array( 'last_name', false ),
+			'email'               => array( 'email', false ),
+			'alias'               => array( 'alias', false ),
+			'subscription_starts' => array( 'subscription_starts', false ),
+			'account_state'       => array( 'account_state', false ),
+			'last_accessed'       => array( 'last_accessed', false ),
+		);
+	}
+
+	function get_bulk_actions() {
+		$actions = array(
+			'bulk_delete'        => SwpmUtils::_( 'Delete' ),
+			'bulk_active'        => SwpmUtils::_( 'Set Status to Active' ),
+			'bulk_active_notify' => SwpmUtils::_( 'Set Status to Active and Notify' ),
+			'bulk_inactive'      => SwpmUtils::_( 'Set Status to Inactive' ),
+			'bulk_pending'       => SwpmUtils::_( 'Set Status to Pending' ),
+			'bulk_expired'       => SwpmUtils::_( 'Set Status to Expired' ),
+		);
+		return $actions;
+	}
+
+	function column_default( $item, $column_name ) {
+		return $item[ $column_name ];
+	}
+
+	function column_account_state( $item ) {
+		$acc_state_str = ucfirst( $item['account_state'] );
+		return SwpmUtils::_( $acc_state_str );
+	}
+
+	function column_member_id( $item ) {
+		$delete_swpmuser_nonce = wp_create_nonce( 'delete_swpmuser_admin_end' );
+		$actions               = array(
+			'edit'   => sprintf( '<a href="admin.php?page=simple_wp_membership&member_action=edit&member_id=%s">Edit/View</a>', $item['member_id'] ),
+			'delete' => sprintf( '<a href="admin.php?page=simple_wp_membership&member_action=delete&member_id=%s&delete_swpmuser_nonce=%s" onclick="return confirm(\'Are you sure you want to delete this entry?\')">Delete</a>', $item['member_id'], $delete_swpmuser_nonce ),
+		);
+		return $item['member_id'] . $this->row_actions( $actions );
+	}
+
+	function column_user_name( $item ) {
+		$user_name = $item['user_name'];
+		if ( empty( $user_name ) ) {
+			$user_name = '[' . SwpmUtils::_( 'incomplete' ) . ']';
+		}
+		return $user_name;
+	}
+
+	function column_cb( $item ) {
+		return sprintf(
+			'<input type="checkbox" name="members[]" value="%s" />',
+			$item['member_id']
+		);
+	}
+
+	function prepare_items() {
+		global $wpdb;
+
+		$this->process_bulk_action();
+
+		$records_query_head = 'SELECT member_id,user_name,first_name,last_name,email,alias,subscription_starts,account_state,last_accessed';
+		$count_query_head   = 'SELECT COUNT(member_id)';
+
+		$query  = ' ';
+		$query .= ' FROM ' . $wpdb->prefix . 'swpm_members_tbl';
+		$query .= ' LEFT JOIN ' . $wpdb->prefix . 'swpm_membership_tbl';
+		$query .= ' ON ( membership_level = id ) ';
+
+		//Get the search string (if any)
+		$s = filter_input( INPUT_GET, 's' );
+		if ( empty( $s ) ) {
+			$s = filter_input( INPUT_POST, 's' );
+		}
+
+		$status = filter_input( INPUT_GET, 'status' );
+                $status = esc_attr( $status );//Escape value
+
+		$filters = array();
+
+		//Add the search parameter to the query
+		if ( ! empty( $s ) ) {
+			$s = sanitize_text_field( $s );
+			$s = trim( $s ); //Trim the input
+                        $s = esc_attr( $s );
+			$filters[] = "( user_name LIKE '%" . strip_tags( $s ) . "%' "
+					. " OR first_name LIKE '%" . strip_tags( $s ) . "%' "
+					. " OR last_name LIKE '%" . strip_tags( $s ) . "%' "
+					. " OR email LIKE '%" . strip_tags( $s ) . "%' "
+					. " OR address_city LIKE '%" . strip_tags( $s ) . "%' "
+					. " OR address_state LIKE '%" . strip_tags( $s ) . "%' "
+					. " OR country LIKE '%" . strip_tags( $s ) . "%' "
+					. " OR company_name LIKE '%" . strip_tags( $s ) . "%' )";
+		}
+
+		//Add account status filtering to the query
+		if ( ! empty( $status ) ) {
+			if ( $status == 'incomplete' ) {
+				$filters[] = "user_name = ''";
+			} else {
+				$filters[] = "account_state = '" . $status . "'";
+			}
+		}
+
+		//Add membership level filtering
+		$membership_level = filter_input( INPUT_GET, 'membership_level', FILTER_SANITIZE_NUMBER_INT );
+
+		if ( ! empty( $membership_level ) ) {
+			$filters[] = sprintf( "membership_level = '%d'", $membership_level );
+		}
+
+		//Build the WHERE clause of the query string
+		if ( ! empty( $filters ) ) {
+			$filter_str = '';
+			foreach ( $filters as $ind => $filter ) {
+				$filter_str .= $ind === 0 ? $filter : ' AND ' . $filter;
+			}
+			$query .= 'WHERE ' . $filter_str;
+		}
+
+		//Build the orderby and order query parameters
+		$orderby          = filter_input( INPUT_GET, 'orderby' );
+		$orderby          = empty( $orderby ) ? 'member_id' : $orderby;
+		$order            = filter_input( INPUT_GET, 'order' );
+		$order            = empty( $order ) ? 'DESC' : $order;
+		$sortable_columns = $this->get_sortable_columns();
+		$orderby          = SwpmUtils::sanitize_value_by_array( $orderby, $sortable_columns );
+		$order            = SwpmUtils::sanitize_value_by_array(
+			$order,
+			array(
+				'DESC' => '1',
+				'ASC'  => '1',
+			)
+		);
+		$query           .= ' ORDER BY ' . $orderby . ' ' . $order;
+
+		//Execute the query
+		$totalitems = $wpdb->get_var( $count_query_head . $query );
+		//Pagination setup
+		$perpage = apply_filters( 'swpm_members_menu_items_per_page', 50 );
+		$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( $records_query_head . $query, ARRAY_A );
+	}
+
+	function get_user_count_by_account_state() {
+		global $wpdb;
+		$query  = 'SELECT count(member_id) AS count, account_state FROM ' . $wpdb->prefix . 'swpm_members_tbl GROUP BY account_state';
+		$result = $wpdb->get_results( $query, ARRAY_A );
+		$count  = array();
+
+		$all = 0;
+		foreach ( $result as $row ) {
+			$count[ $row['account_state'] ] = $row['count'];
+			$all                           += intval( $row['count'] );
+		}
+		$count ['all'] = $all;
+
+		$count_incomplete_query = 'SELECT COUNT(*) FROM ' . $wpdb->prefix . "swpm_members_tbl WHERE user_name = ''";
+		$count['incomplete']    = $wpdb->get_var( $count_incomplete_query );
+
+		return $count;
+	}
+
+	function no_items() {
+		_e( 'No member found.', 'simple-membership' );
+	}
+
+	function process_form_request() {
+		if ( isset( $_REQUEST['member_id'] ) ) {
+			//This is a member profile edit action
+			$record_id = sanitize_text_field( $_REQUEST['member_id'] );
+			if ( ! is_numeric( $record_id ) ) {
+				wp_die( 'Error! ID must be numeric.' );
+			}
+			return $this->edit( absint( $record_id ) );
+		}
+
+		//This is an profile add action.
+		return $this->add();
+	}
+
+	function add() {
+		$form = apply_filters( 'swpm_admin_registration_form_override', '' );
+		if ( ! empty( $form ) ) {
+			echo $form;
+			return;
+		}
+		global $wpdb;
+		$member                        = SwpmTransfer::$default_fields;
+		$member['member_since']        = SwpmUtils::get_current_date_in_wp_zone();//date( 'Y-m-d' );
+		$member['subscription_starts'] = SwpmUtils::get_current_date_in_wp_zone();//date( 'Y-m-d' );
+		if ( isset( $_POST['createswpmuser'] ) ) {
+			$member = array_map( 'sanitize_text_field', $_POST );
+		}
+		extract( $member, EXTR_SKIP );
+		$query  = 'SELECT * FROM ' . $wpdb->prefix . 'swpm_membership_tbl WHERE  id !=1 ';
+		$levels = $wpdb->get_results( $query, ARRAY_A );
+
+                $add_user_template_path = apply_filters('swpm_admin_registration_add_user_template_path', SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_add.php');
+		include_once $add_user_template_path;
+
+		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'] = sanitize_text_field( $member['user_name'] );
+			$_POST['email']     = sanitize_email( $member['email'] );
+			foreach ( $_POST as $key => $value ) {
+				$key = sanitize_text_field( $key );
+				if ( $key == 'email' ) {
+					$member[ $key ] = sanitize_email( $value );
+				} else {
+					$member[ $key ] = sanitize_text_field( $value );
+				}
+			}
+		}
+		extract( $member, EXTR_SKIP );
+		$query  = 'SELECT * FROM ' . $wpdb->prefix . 'swpm_membership_tbl WHERE  id !=1 ';
+		$levels = $wpdb->get_results( $query, ARRAY_A );
+
+                $edit_user_template_path = apply_filters('swpm_admin_registration_edit_user_template_path', SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_edit.php');
+		include_once $edit_user_template_path;
+
+		return false;
+	}
+
+	function process_bulk_action() {
+		//Detect when a bulk action is being triggered... then perform the action.
+		$members = isset( $_REQUEST['members'] ) ? $_REQUEST['members'] : array();
+		$members = array_map( 'sanitize_text_field', $members );
+
+		$current_action = $this->current_action();
+		if ( ! empty( $current_action ) ) {
+			//Bulk operation action. Lets make sure multiple records were selected before going ahead.
+			if ( empty( $members ) ) {
+				echo '<div id="message" class="error"><p>Error! You need to select multiple records to perform a bulk action!</p></div>';
+				return;
+			}
+		} else {
+			//No bulk operation.
+			return;
+		}
+
+		//perform the bulk operation according to the selection
+		if ( 'bulk_delete' === $current_action ) {
+			foreach ( $members as $record_id ) {
+				if ( ! is_numeric( $record_id ) ) {
+					wp_die( 'Error! ID must be numeric.' );
+				}
+				self::delete_user_by_id( $record_id );
+			}
+			echo '<div id="message" class="updated fade"><p>Selected records deleted successfully!</p></div>';
+			return;
+		} elseif ( 'bulk_active' === $current_action ) {
+			$this->bulk_set_status( $members, 'active' );
+		} elseif ( 'bulk_active_notify' == $current_action ) {
+			$this->bulk_set_status( $members, 'active', true );
+		} elseif ( 'bulk_inactive' == $current_action ) {
+			$this->bulk_set_status( $members, 'inactive' );
+		} elseif ( 'bulk_pending' == $current_action ) {
+			$this->bulk_set_status( $members, 'pending' );
+		} elseif ( 'bulk_expired' == $current_action ) {
+			$this->bulk_set_status( $members, 'expired' );
+		}
+
+		echo '<div id="message" class="updated fade"><p>Bulk operation completed successfully!</p></div>';
+	}
+
+	function bulk_set_status( $members, $status, $notify = false ) {
+		$ids = implode( ',', array_map( 'absint', $members ) );
+		if ( empty( $ids ) ) {
+			return;
+		}
+		global $wpdb;
+		$query = 'UPDATE ' . $wpdb->prefix . 'swpm_members_tbl ' .
+				" SET account_state = '" . $status . "' WHERE member_id in (" . $ids . ')';
+		$wpdb->query( $query );
+
+		if ( $notify ) {
+			$settings = SwpmSettings::get_instance();
+
+			$emails = $wpdb->get_col( 'SELECT email FROM ' . $wpdb->prefix . 'swpm_members_tbl ' . " WHERE member_id IN ( $ids  ) " );
+
+			$subject = $settings->get_value( 'bulk-activate-notify-mail-subject' );
+			if ( empty( $subject ) ) {
+                            $subject = 'Account Activated!';
+			}
+			$body = $settings->get_value( 'bulk-activate-notify-mail-body' );
+			if ( empty( $body ) ) {
+                            $body = 'Hi, Your account has been activated successfully!';
+			}
+
+			$from_address = $settings->get_value( 'email-from' );
+			$headers = 'From: ' . $from_address . "\r\n";
+
+                        foreach ($emails as $to_email) {
+                            //Send the activation email one by one to all the selected members.
+                            $subject = apply_filters( 'swpm_email_bulk_set_status_subject', $subject );
+                            $body = apply_filters( 'swpm_email_bulk_set_status_body', $body );
+                            $to_email = trim($to_email);
+                            SwpmMiscUtils::mail( $to_email, $subject, $body, $headers );
+                            SwpmLog::log_simple_debug( 'Bulk activation email notification sent. Activation email sent to the following email: ' . $to_email, true );
+                        }
+		}
+	}
+
+	function delete() {
+		if ( isset( $_REQUEST['member_id'] ) ) {
+			//Check we are on the admin end and user has management permission
+			SwpmMiscUtils::check_user_permission_and_is_admin( 'member deletion by admin' );
+
+			//Check nonce
+			if ( ! isset( $_REQUEST['delete_swpmuser_nonce'] ) || ! wp_verify_nonce( $_REQUEST['delete_swpmuser_nonce'], 'delete_swpmuser_admin_end' ) ) {
+				//Nonce check failed.
+				wp_die( SwpmUtils::_( 'Error! Nonce verification failed for user delete from admin end.' ) );
+			}
+
+			$id = sanitize_text_field( $_REQUEST['member_id'] );
+			$id = absint( $id );
+			self::delete_user_by_id( $id );
+		}
+	}
+
+	public static function delete_user_by_id( $id ) {
+		if ( ! is_numeric( $id ) ) {
+			wp_die( 'Error! Member ID must be numeric.' );
+		}
+		$swpm_user = SwpmMemberUtils::get_user_by_id( $id );
+		$user_name = $swpm_user->user_name;
+		self::delete_wp_user( $user_name ); //Deletes the WP User record
+		self::delete_swpm_user_by_id( $id ); //Deletes the SWPM record
+	}
+
+	public static function delete_swpm_user_by_id( $id ) {
+		self::delete_user_subs( $id );
+		global $wpdb;
+		$query = 'DELETE FROM ' . $wpdb->prefix . "swpm_members_tbl WHERE member_id = $id";
+		$wpdb->query( $query );
+	}
+
+	public static function delete_wp_user( $user_name ) {
+		$wp_user_id = username_exists( $user_name );
+		if ( empty( $wp_user_id ) || ! is_numeric( $wp_user_id ) ) {
+			return;
+		}
+
+		if ( ! self::is_wp_super_user( $wp_user_id ) ) {
+			//Not an admin user so it is safe to delete this user.
+			include_once ABSPATH . 'wp-admin/includes/user.php';
+			wp_delete_user( $wp_user_id, 1 ); //assigns all related to this user to admin.
+		} else {
+			//This is an admin user. So not going to delete the WP User record.
+			SwpmTransfer::get_instance()->set( 'status', 'For safety, we do not allow deletion of any associated WordPress account with administrator role.' );
+			return;
+		}
+	}
+
+	private static function delete_user_subs( $id ) {
+		$member = SwpmMemberUtils::get_user_by_id( $id );
+		if ( ! $member ) {
+			return false;
+		}
+		// let's check if Stripe subscription needs to be cancelled
+		global $wpdb;
+		$q = $wpdb->prepare(
+			'SELECT *
+		FROM  `' . $wpdb->prefix . 'swpm_payments_tbl`
+		WHERE email =  %s
+		AND (gateway =  "stripe" OR gateway = "stripe-sca-subs")
+		AND subscr_id != ""',
+			array( $member->email )
+		);
+
+		$res = $wpdb->get_results( $q, ARRAY_A );
+
+		if ( ! $res ) {
+			return false;
+		}
+
+		foreach ( $res as $sub ) {
+
+			if ( substr( $sub['subscr_id'], 0, 4 ) !== 'sub_' ) {
+				//not Stripe subscription
+				continue;
+			}
+
+			//let's find the payment button
+			$q        = $wpdb->prepare( "SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key='subscr_id' AND meta_value=%s", $sub['subscr_id'] );
+			$res_post = $wpdb->get_row( $q );
+
+			if ( ! $res_post ) {
+				//no button found
+				continue;
+			}
+
+			$button_id = get_post_meta( $res_post->post_id, 'payment_button_id', true );
+
+			$button = get_post( $button_id );
+
+			if ( ! $button ) {
+				//no button found
+				continue;
+			}
+
+			SwpmLog::log_simple_debug( 'Attempting to cancel Stripe Subscription ' . $sub['subscr_id'], true );
+
+			$is_live = get_post_meta( $button_id, 'is_live', true );
+
+			//API Keys
+			$api_keys = SwpmMiscUtils::get_stripe_api_keys_from_payment_button( $button_id, $is_live );
+
+			//Include the Stripe library.
+			SwpmMiscUtils::load_stripe_lib();
+
+			\Stripe\Stripe::setApiKey( $api_keys['secret'] );
+
+			$error = null;
+			// Let's try to cancel subscription
+			try {
+				$sub = \Stripe\Subscription::retrieve( $sub['subscr_id'] );
+				$sub->cancel();
+			} catch ( Exception $e ) {
+				SwpmLog::log_simple_debug( 'Error occurred during Stripe Subscription cancellation. ' . $e->getMessage(), false );
+				$body         = $e->getJsonBody();
+				$error        = $body['error'];
+				$error_string = wp_json_encode( $error );
+				SwpmLog::log_simple_debug( 'Error details: ' . $error_string, false );
+			}
+			if ( ! isset( $error ) ) {
+				SwpmLog::log_simple_debug( 'Stripe Subscription has been cancelled.', true );
+			}
+		}
+	}
+
+	public static function is_wp_super_user( $wp_user_id ) {
+		$user_data = get_userdata( $wp_user_id );
+		if ( empty( $user_data ) ) {
+			//Not an admin user if we can't find his data for the given ID.
+			return false;
+		}
+		if ( isset( $user_data->wp_capabilities['administrator'] ) ) {//Check capability
+			//admin user
+			return true;
+		}
+		if ( $user_data->wp_user_level == 10 ) {//Check for old style wp user level
+			//admin user
+			return true;
+		}
+		//This is not an admin user
+		return false;
+	}
+
+	function bulk_operation_menu() {
+		echo '<div id="poststuff"><div id="post-body">';
+
+		if ( isset( $_REQUEST['swpm_bulk_change_level_process'] ) ) {
+			//Check nonce
+			$swpm_bulk_change_level_nonce = filter_input( INPUT_POST, 'swpm_bulk_change_level_nonce' );
+			if ( ! wp_verify_nonce( $swpm_bulk_change_level_nonce, 'swpm_bulk_change_level_nonce_action' ) ) {
+				//Nonce check failed.
+				wp_die( SwpmUtils::_( 'Error! Nonce security verification failed for Bulk Change Membership Level action. Clear cache and try again.' ) );
+			}
+
+			$errorMsg      = '';
+			$from_level_id = sanitize_text_field( $_REQUEST['swpm_bulk_change_level_from'] );
+			$to_level_id   = sanitize_text_field( $_REQUEST['swpm_bulk_change_level_to'] );
+
+			if ( $from_level_id == 'please_select' || $to_level_id == 'please_select' ) {
+				$errorMsg = SwpmUtils::_( 'Error! Please select a membership level first.' );
+			}
+
+			if ( empty( $errorMsg ) ) {//No validation errors so go ahead
+				$member_records = SwpmMemberUtils::get_all_members_of_a_level( $from_level_id );
+				if ( $member_records ) {
+					foreach ( $member_records as $row ) {
+						$member_id = $row->member_id;
+						SwpmMemberUtils::update_membership_level( $member_id, $to_level_id );
+					}
+				}
+			}
+
+			$message = '';
+			if ( ! empty( $errorMsg ) ) {
+				$message = $errorMsg;
+			} else {
+				$message = SwpmUtils::_( 'Membership level change operation completed successfully.' );
+			}
+			echo '<div id="message" class="updated fade"><p><strong>';
+			echo $message;
+			echo '</strong></p></div>';
+		}
+
+		if ( isset( $_REQUEST['swpm_bulk_user_start_date_change_process'] ) ) {
+			//Check nonce
+			$swpm_bulk_start_date_nonce = filter_input( INPUT_POST, 'swpm_bulk_start_date_nonce' );
+			if ( ! wp_verify_nonce( $swpm_bulk_start_date_nonce, 'swpm_bulk_start_date_nonce_action' ) ) {
+				//Nonce check failed.
+				wp_die( SwpmUtils::_( 'Error! Nonce security verification failed for Bulk Change Access Starts Date action. Clear cache and try again.' ) );
+			}
+
+			$errorMsg = '';
+			$level_id = sanitize_text_field( $_REQUEST['swpm_bulk_user_start_date_change_level'] );
+			$new_date = sanitize_text_field( $_REQUEST['swpm_bulk_user_start_date_change_date'] );
+
+			if ( $level_id == 'please_select' ) {
+				$errorMsg = SwpmUtils::_( 'Error! Please select a membership level first.' );
+			}
+
+			if ( empty( $errorMsg ) ) {//No validation errors so go ahead
+				$member_records = SwpmMemberUtils::get_all_members_of_a_level( $level_id );
+				if ( $member_records ) {
+					foreach ( $member_records as $row ) {
+						$member_id = $row->member_id;
+						SwpmMemberUtils::update_access_starts_date( $member_id, $new_date );
+					}
+				}
+			}
+
+			$message = '';
+			if ( ! empty( $errorMsg ) ) {
+				$message = $errorMsg;
+			} else {
+				$message = SwpmUtils::_( 'Access starts date change operation successfully completed.' );
+			}
+			echo '<div id="message" class="updated fade"><p><strong>';
+			echo $message;
+			echo '</strong></p></div>';
+		}
+		?>
+
+		<div class="postbox">
+			<h3 class="hndle"><label for="title"><?php SwpmUtils::e( 'Bulk Update Membership Level of Members' ); ?></label></h3>
+			<div class="inside">
+				<p>
+					<?php SwpmUtils::e( 'You can manually change the membership level of any member by editing the record from the members menu. ' ); ?>
+					<?php SwpmUtils::e( 'You can use the following option to bulk update the membership level of users who belong to the level you select below.' ); ?>
+				</p>
+				<form method="post" action="">
+					<input type="hidden" name="swpm_bulk_change_level_nonce" value="<?php echo wp_create_nonce( 'swpm_bulk_change_level_nonce_action' ); ?>" />
+
+					<table width="100%" border="0" cellspacing="0" cellpadding="6">
+						<tr valign="top">
+							<td width="25%" align="left">
+								<strong><?php SwpmUtils::e( 'Membership Level: ' ); ?></strong>
+							</td>
+							<td align="left">
+								<select name="swpm_bulk_change_level_from">
+									<option value="please_select"><?php SwpmUtils::e( 'Select Current Level' ); ?></option>
+									<?php echo SwpmUtils::membership_level_dropdown(); ?>
+								</select>
+								<p class="description"><?php SwpmUtils::e( 'Select the current membership level (the membership level of all members who are in this level will be updated).' ); ?></p>
+							</td>
+						</tr>
+
+						<tr valign="top">
+							<td width="25%" align="left">
+								<strong><?php SwpmUtils::e( 'Level to Change to: ' ); ?></strong>
+							</td>
+							<td align="left">
+								<select name="swpm_bulk_change_level_to">
+									<option value="please_select"><?php SwpmUtils::e( 'Select Target Level' ); ?></option>
+									<?php echo SwpmUtils::membership_level_dropdown(); ?>
+								</select>
+								<p class="description"><?php SwpmUtils::e( 'Select the new membership level.' ); ?></p>
+							</td>
+						</tr>
+
+						<tr valign="top">
+							<td width="25%" align="left">
+								<input type="submit" class="button" name="swpm_bulk_change_level_process" value="<?php SwpmUtils::e( 'Bulk Change Membership Level' ); ?>" />
+							</td>
+							<td align="left"></td>
+						</tr>
+
+					</table>
+				</form>
+			</div></div>
+
+		<div class="postbox">
+			<h3 class="hndle"><label for="title"><?php SwpmUtils::e( 'Bulk Update Access Starts Date of Members' ); ?></label></h3>
+			<div class="inside">
+
+				<p>
+					<?php SwpmUtils::e( 'The access starts date of a member is set to the day the user registers. This date value is used to calculate how long the member can access your content that are protected with a duration type protection in the membership level. ' ); ?>
+					<?php SwpmUtils::e( 'You can manually set a specific access starts date value of all members who belong to a particular level using the following option.' ); ?>
+				</p>
+				<form method="post" action="">
+					<input type="hidden" name="swpm_bulk_start_date_nonce" value="<?php echo wp_create_nonce( 'swpm_bulk_start_date_nonce_action' ); ?>" />
+
+					<table width="100%" border="0" cellspacing="0" cellpadding="6">
+						<tr valign="top">
+							<td width="25%" align="left">
+								<strong><?php SwpmUtils::e( 'Membership Level: ' ); ?></strong>
+							</td><td align="left">
+								<select name="swpm_bulk_user_start_date_change_level">
+									<option value="please_select"><?php SwpmUtils::e( 'Select Level' ); ?></option>
+									<?php echo SwpmUtils::membership_level_dropdown(); ?>
+								</select>
+								<p class="description"><?php SwpmUtils::e( 'Select the Membership level (the access start date of all members who are in this level will be updated).' ); ?></p>
+							</td>
+						</tr>
+
+						<tr valign="top">
+							<td width="25%" align="left">
+								<strong>Access Starts Date: </strong>
+							</td><td align="left">
+								<input name="swpm_bulk_user_start_date_change_date" id="swpm_bulk_user_start_date_change_date" class="swpm-select-date" type="text" size="20" value="<?php echo ( date( 'Y-m-d' ) ); ?>" />
+								<p class="description"><?php SwpmUtils::e( 'Specify the access starts date value.' ); ?></p>
+							</td>
+						</tr>
+
+						<tr valign="top">
+							<td width="25%" align="left">
+								<input type="submit" class="button" name="swpm_bulk_user_start_date_change_process" value="<?php SwpmUtils::e( 'Bulk Change Access Starts Date' ); ?>" />
+							</td>
+							<td align="left"></td>
+						</tr>
+
+					</table>
+				</form>
+			</div></div>
+
+		<script>
+			jQuery(document).ready(function ($) {
+				$('#swpm_bulk_user_start_date_change_date').datepicker({dateFormat: 'yy-mm-dd', changeMonth: true, changeYear: true, yearRange: "-100:+100"});
+			});
+		</script>
+		<?php
+		echo '</div></div>'; //<!-- end of #poststuff #post-body -->
+	}
+
+	function show_all_members() {
+		ob_start();
+		$status = filter_input( INPUT_GET, 'status' );
+		include_once SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_members_list.php';
+		$output = ob_get_clean();
+		return $output;
+	}
+
+	function handle_main_members_admin_menu() {
+		do_action( 'swpm_members_menu_start' );
+
+		//Check current_user_can() or die.
+		SwpmMiscUtils::check_user_permission_and_is_admin( 'Main Members Admin Menu' );
+
+		$action   = filter_input( INPUT_GET, 'member_action' );
+		$action   = empty( $action ) ? filter_input( INPUT_POST, 'action' ) : $action;
+		$selected = $action;
+		?>
+		<div class="wrap swpm-admin-menu-wrap"><!-- start wrap -->
+
+			<h1><?php echo SwpmUtils::_( 'Simple WP Membership::Members' ); ?><!-- page title -->
+				<a href="admin.php?page=simple_wp_membership&member_action=add" class="add-new-h2"><?php echo SwpmUtils::_( 'Add New' ); ?></a>
+			</h1>
+
+			<h2 class="nav-tab-wrapper swpm-members-nav-tab-wrapper"><!-- start nav menu tabs -->
+				<a class="nav-tab <?php echo ( $selected == '' ) ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership"><?php echo SwpmUtils::_( 'Members' ); ?></a>
+				<a class="nav-tab <?php echo ( $selected == 'add' ) ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership&member_action=add"><?php echo SwpmUtils::_( 'Add Member' ); ?></a>
+				<a class="nav-tab <?php echo ( $selected == 'bulk' ) ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership&member_action=bulk"><?php echo SwpmUtils::_( 'Bulk Operation' ); ?></a>
+				<?php
+				if ( $selected == 'edit' ) {//Only show the "edit member" tab when a member profile is being edited from the admin side.
+					echo '<a class="nav-tab nav-tab-active" href="#">Edit Member</a>';
+				}
+
+				//Trigger hooks that allows an extension to add extra nav tabs in the members menu.
+				do_action( 'swpm_members_menu_nav_tabs', $selected );
+
+				$menu_tabs = apply_filters( 'swpm_members_additional_menu_tabs_array', array() );
+				foreach ( $menu_tabs as $member_action => $title ) {
+					?>
+					<a class="nav-tab <?php echo ( $selected == $member_action ) ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership&member_action=<?php echo $member_action; ?>" ><?php SwpmUtils::e( $title ); ?></a>
+					<?php
+				}
+				?>
+			</h2><!-- end nav menu tabs -->
+			<?php
+			do_action( 'swpm_members_menu_after_nav_tabs' );
+
+			//Trigger hook so anyone listening for this particular action can handle the output.
+			do_action( 'swpm_members_menu_body_' . $action );
+
+			//Allows an addon to completely override the body section of the members admin menu for a given action.
+			$output = apply_filters( 'swpm_members_menu_body_override', '', $action );
+			if ( ! empty( $output ) ) {
+				//An addon has overriden the body of this page for the given action. So no need to do anything in core.
+				echo $output;
+				echo '</div>'; //<!-- end of wrap -->
+				return;
+			}
+
+			//Switch case for the various different actions handled by the core plugin.
+			switch ( $action ) {
+				case 'members_list':
+					//Show the members listing
+					echo $this->show_all_members();
+					break;
+				case 'add':
+					//Process member profile add
+					$this->process_form_request();
+					break;
+				case 'edit':
+					//Process member profile edit
+					$this->process_form_request();
+					break;
+				case 'bulk':
+					//Handle the bulk operation menu
+					$this->bulk_operation_menu();
+					break;
+				default:
+					//Show the members listing page by default.
+					echo $this->show_all_members();
+					break;
+			}
+
+			echo '</div>'; //<!-- end of wrap -->
+	}
+
+}
+

+ 93 - 0
simple-membership/classes/class.swpm-membership-level-custom.php

@@ -0,0 +1,93 @@
+<?php
+/**
+ * Description of BMembershipLevelCustom
+ *
+ * @author nur
+ */
+class SwpmMembershipLevelCustom {
+    private static $instances = array();
+    private $level_id;
+    private $fields;
+    private function __construct() {
+        $this->fields = array();
+    }
+    public static function get_instance_by_id($level_id){
+        if (!isset(self::$instances[$level_id])){
+            self::$instances[$level_id] = new SwpmMembershipLevelCustom();
+            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 SwpmMembershipLevelCustom::get_instance_by_id($level_id)->get($key, $default);
+    }
+    public static  function get_value_by_context($level_id, $context){
+        return SwpmMembershipLevelCustom::get_instance_by_id($level_id)->get_by_context($context);
+    }
+}

+ 116 - 0
simple-membership/classes/class.swpm-membership-level.php

@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * Description of SwpmMembershipLevel
+ */
+class SwpmMembershipLevel {
+
+    const NO_EXPIRY = 0;
+    const DAYS = 1;
+    const WEEKS = 2;
+    const MONTHS = 3;
+    const YEARS = 4;
+    const FIXED_DATE = 5;
+
+    private static $_instance = null;
+
+    private function __construct() {
+        //NOP
+    }
+
+    public static function get_instance() {
+        self::$_instance = empty(self::$_instance) ? new SwpmMembershipLevel() : self::$_instance;
+        return self::$_instance;
+    }
+
+    public static function get_level_duration_type_string($type){
+        $type_string = '';
+        switch($type){
+            case '0': $type_string = 'No Expiry or Until Cancelled';
+                break;
+            case '1': $type_string = 'Days';
+                break;
+            case '2': $type_string = 'Weeks';
+                break;
+            case '3': $type_string = 'Months';
+                break;
+            case '4': $type_string = 'Years';
+                break;
+            case '5': $type_string = 'Fixed Date';
+                break;
+        }
+        return $type_string;
+    }
+    
+    public function create_level() {
+        //Check we are on the admin end and user has management permission 
+        SwpmMiscUtils::check_user_permission_and_is_admin('membership level creation');
+        
+        //Check nonce
+        if ( !isset($_POST['_wpnonce_create_swpmlevel_admin_end']) || !wp_verify_nonce($_POST['_wpnonce_create_swpmlevel_admin_end'], 'create_swpmlevel_admin_end' )){
+            //Nonce check failed.
+            wp_die(SwpmUtils::_("Error! Nonce verification failed for membership level creation from admin end."));
+        }
+        
+        global $wpdb;
+        $level = SwpmTransfer::$default_level_fields;
+        $form = new SwpmLevelForm($level);
+        if ($form->is_valid()) {
+            $level_info = $form->get_sanitized();
+            $wpdb->insert($wpdb->prefix . "swpm_membership_tbl", $level_info);
+            $id = $wpdb->insert_id;
+            //save email_activation option
+            $email_activation=filter_input(INPUT_POST,'email_activation',FILTER_SANITIZE_NUMBER_INT);
+            update_option('swpm_email_activation_lvl_'.$id, $email_activation, false);
+
+            $custom = apply_filters('swpm_admin_add_membership_level', array());
+            $this->save_custom_fields($id, $custom);
+            $message = array('succeeded' => true, 'message' => '<p>' . SwpmUtils::_('Membership Level Creation Successful.') . '</p>');
+            SwpmTransfer::get_instance()->set('status', $message);
+            wp_redirect('admin.php?page=simple_wp_membership_levels');
+            exit(0);
+        }
+        $message = array('succeeded' => false, 'message' => SwpmUtils::_('Please correct the following:'), 'extra' => $form->get_errors());
+        SwpmTransfer::get_instance()->set('status', $message);
+    }
+
+    public function edit_level($id) {
+        //Check we are on the admin end and user has management permission 
+        SwpmMiscUtils::check_user_permission_and_is_admin('membership level edit');
+        
+        //Check nonce
+        if ( !isset($_POST['_wpnonce_edit_swpmlevel_admin_end']) || !wp_verify_nonce($_POST['_wpnonce_edit_swpmlevel_admin_end'], 'edit_swpmlevel_admin_end' )){
+            //Nonce check failed.
+            wp_die(SwpmUtils::_("Error! Nonce verification failed for membership level edit from admin end."));
+        }
+        
+        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 SwpmLevelForm($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
+            //save email_activation option
+            $email_activation=filter_input(INPUT_POST,'email_activation',FILTER_SANITIZE_NUMBER_INT);
+            update_option('swpm_email_activation_lvl_'.$id, $email_activation, false);
+
+            $custom = apply_filters('swpm_admin_edit_membership_level', array(), $id);
+            $this->save_custom_fields($id, $custom);
+            $message = array('succeeded' => true, 'message' => '<p>'. SwpmUtils::_('Membership Level Updated Successfully.') . '</p>');
+            SwpmTransfer::get_instance()->set('status', $message);
+            wp_redirect('admin.php?page=simple_wp_membership_levels');
+            exit(0);
+        }
+        $message = array('succeeded' => false, 'message' => SwpmUtils::_('Please correct the following:'), 'extra' => $form->get_errors());
+        SwpmTransfer::get_instance()->set('status', $message);
+    }
+
+    private function save_custom_fields($level_id, $data) {
+        $custom_obj = SwpmMembershipLevelCustom::get_instance_by_id($level_id);
+        foreach ($data as $item) {
+            $custom_obj->set($item);
+        }
+    }
+
+}

+ 331 - 0
simple-membership/classes/class.swpm-membership-levels.php

@@ -0,0 +1,331 @@
+<?php
+
+if (!class_exists('WP_List_Table')){
+    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
+}
+
+class SwpmMembershipLevels extends WP_List_Table {
+
+    function __construct() {
+        parent::__construct(array(
+            'singular' => SwpmUtils::_('Membership Level'),
+            'plural' => SwpmUtils::_('Membership Levels'),
+            'ajax' => false
+        ));
+    }
+
+    function get_columns() {
+        return array(
+            'cb' => '<input type="checkbox" />'
+            , 'id' => SwpmUtils::_('ID')
+            , 'alias' => SwpmUtils::_('Membership Level')
+            , 'role' => SwpmUtils::_('Role')
+            , 'valid_for' => SwpmUtils::_('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' => SwpmUtils::_('Delete')
+        );
+        return $actions;
+    }
+
+    function column_default($item, $column_name) {
+        if ($column_name == 'valid_for') {
+            if ($item['subscription_duration_type'] == SwpmMembershipLevel::NO_EXPIRY) {
+                return 'No Expiry';
+            }
+            if ($item['subscription_duration_type'] == SwpmMembershipLevel::FIXED_DATE) {
+                $formatted_date = SwpmUtils::get_formatted_date_according_to_wp_settings($item['subscription_period']);
+                return $formatted_date;
+            }
+            if ($item['subscription_duration_type'] == SwpmMembershipLevel::DAYS) {
+                return $item['subscription_period'] . " Day(s)";
+            }
+            if ($item['subscription_duration_type'] == SwpmMembershipLevel::WEEKS) {
+                return $item['subscription_period'] . " Week(s)";
+            }
+            if ($item['subscription_duration_type'] == SwpmMembershipLevel::MONTHS) {
+                return $item['subscription_period'] . " Month(s)";
+            }
+            if ($item['subscription_duration_type'] == SwpmMembershipLevel::YEARS) {
+                return $item['subscription_period'] . " Year(s)";
+            }
+        }
+        if ($column_name == 'role') {
+            return ucfirst($item['role']);
+        }
+        return stripslashes($item[$column_name]);
+    }
+
+    function column_id($item) {
+        $delete_swpmlevel_nonce = wp_create_nonce( 'nonce_delete_swpmlevel_admin_end' );
+
+        $actions = array(
+            'edit' => sprintf('<a href="admin.php?page=simple_wp_membership_levels&level_action=edit&id=%s">Edit</a>', $item['id']),
+            'delete' => sprintf('<a href="admin.php?page=simple_wp_membership_levels&level_action=delete&id=%s&delete_swpmlevel_nonce=%s" onclick="return confirm(\'Are you sure you want to delete this entry?\')">Delete</a>', $item['id'],$delete_swpmlevel_nonce),
+        );
+        return $item['id'] . $this->row_actions($actions);
+    }
+
+    function column_cb($item) {
+        return sprintf(
+                '<input type="checkbox" name="ids[]" value="%s" />', $item['id']
+        );
+    }
+
+    function prepare_items() {
+        global $wpdb;
+
+        $this->process_bulk_action();
+
+        $query = "SELECT * FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE  id !=1 ";
+        if (isset($_POST['s'])){
+            $search_keyword = sanitize_text_field($_POST['s']);
+            $search_keyword = esc_attr ($search_keyword);
+            $query .= " AND alias LIKE '%" . $search_keyword . "%' ";
+        }
+
+        //Read and sanitize the sort inputs.
+        $orderby = !empty($_GET["orderby"]) ? esc_sql($_GET["orderby"]) : 'id';
+        $order = !empty($_GET["order"]) ? esc_sql($_GET["order"]) : 'DESC';
+
+        $sortable_columns = $this->get_sortable_columns();
+        $orderby = SwpmUtils::sanitize_value_by_array($orderby, $sortable_columns);
+        $order = SwpmUtils::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 = 50;
+        $paged = !empty($_GET["paged"]) ? sanitize_text_field($_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() {
+        SwpmUtils::e('No membership levels found.');
+    }
+
+    function process_form_request() {
+        if (isset($_REQUEST['id'])) {
+            //This is a level edit action
+            $record_id = sanitize_text_field($_REQUEST['id']);
+            if(!is_numeric($record_id)){
+                wp_die('Error! ID must be numeric.');
+            }
+            return $this->edit($record_id);
+        }
+
+        //Level add action
+        return $this->add();
+    }
+
+    function add() {
+        //Level add interface
+        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);
+        $email_activation = get_option('swpm_email_activation_lvl_'.$id);
+        include_once(SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_edit_level.php');
+        return false;
+    }
+
+    function process_bulk_action() {
+        //Detect when a bulk action is being triggered...
+        global $wpdb;
+
+        if ('bulk_delete' === $this->current_action()) {
+            $records_to_delete = array_map( 'sanitize_text_field', $_REQUEST['ids'] );
+            if (empty($records_to_delete)) {
+                echo '<div id="message" class="updated fade"><p>Error! You need to select multiple records to perform a bulk action!</p></div>';
+                return;
+            }
+            foreach ($records_to_delete as $record_id) {
+                if( !is_numeric( $record_id )){
+                    wp_die('Error! ID must be numeric.');
+                }
+                $query = $wpdb->prepare("DELETE FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id = %d", $record_id);
+                $wpdb->query($query);
+            }
+            echo '<div id="message" class="updated fade"><p>Selected records deleted successfully!</p></div>';
+        }
+    }
+
+    function delete_level() {
+        global $wpdb;
+        if (isset($_REQUEST['id'])) {
+
+            //Check we are on the admin end and user has management permission
+            SwpmMiscUtils::check_user_permission_and_is_admin('membership level delete');
+
+            //Check nonce
+            if ( !isset($_REQUEST['delete_swpmlevel_nonce']) || !wp_verify_nonce($_REQUEST['delete_swpmlevel_nonce'], 'nonce_delete_swpmlevel_admin_end' )){
+                //Nonce check failed.
+                wp_die(SwpmUtils::_("Error! Nonce verification failed for membership level delete from admin end."));
+            }
+
+            $id = sanitize_text_field($_REQUEST['id']);
+            $id = absint($id);
+            $query = $wpdb->prepare("DELETE FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id = %d", $id);
+            $wpdb->query($query);
+            echo '<div id="message" class="updated fade"><p>Selected record deleted successfully!</p></div>';
+        }
+    }
+
+    function show_levels() {
+        ?>
+        <div class="swpm-margin-top-10"></div>
+        <form method="post">
+            <p class="search-box">
+                <label class="screen-reader-text" for="search_id-search-input">
+                    search:</label>
+                <input id="search_id-search-input" type="text" name="s" value="" />
+                <input id="search-submit" class="button" type="submit" name="" value="<?php echo  SwpmUtils::_('Search')?>" />
+            </p>
+        </form>
+
+        <?php $this->prepare_items(); ?>
+        <form method="post">
+            <?php $this->display(); ?>
+        </form>
+
+        <p>
+            <a href="admin.php?page=simple_wp_membership_levels&level_action=add" class="button-primary"><?php SwpmUtils::e('Add New') ?></a>
+        </p>
+        <?php
+    }
+
+    function manage() {
+        include_once(SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_membership_manage.php');
+    }
+
+    function manage_categroy() {
+        $selected = "category_list";
+        include_once('class.swpm-category-list.php');
+        $category_list = new SwpmCategoryList();
+        include_once(SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_category_list.php');
+    }
+
+    function manage_post() {
+        $selected = "post_list";
+        include_once('class.swpm-post-list.php');
+        $post_list = new SwpmPostList();
+        include_once(SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_post_list.php');
+    }
+
+    function handle_main_membership_level_admin_menu(){
+        do_action( 'swpm_membership_level_menu_start' );
+
+        //Check current_user_can() or die.
+        SwpmMiscUtils::check_user_permission_and_is_admin('Main Membership Level Admin Menu');
+
+        $level_action = filter_input(INPUT_GET, 'level_action');
+        $action = $level_action;
+        $selected= $action;
+
+        ?>
+        <div class="wrap swpm-admin-menu-wrap"><!-- start wrap -->
+
+        <!-- page title -->
+        <h1><?php echo  SwpmUtils::_('Simple WP Membership::Membership Levels') ?></h1>
+
+        <!-- start nav menu tabs -->
+        <h2 class="nav-tab-wrapper">
+            <a class="nav-tab <?php echo ($selected == "") ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership_levels"><?php echo SwpmUtils::_('Membership Levels') ?></a>
+            <a class="nav-tab <?php echo ($selected == "add") ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership_levels&level_action=add"><?php echo SwpmUtils::_('Add Level') ?></a>
+            <a class="nav-tab <?php echo ($selected == "manage") ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership_levels&level_action=manage"><?php echo SwpmUtils::_('Manage Content Protection') ?></a>
+            <a class="nav-tab <?php echo ($selected == "category_list") ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership_levels&level_action=category_list"><?php echo SwpmUtils::_('Category Protection') ?></a>
+            <a class="nav-tab <?php echo ($selected == "post_list") ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership_levels&level_action=post_list"><?php echo SwpmUtils::_('Post and Page Protection') ?></a>
+            <?php
+
+            //Trigger hooks that allows an extension to add extra nav tabs in the membership levels menu.
+            do_action ('swpm_membership_levels_menu_nav_tabs', $selected);
+
+            $menu_tabs = apply_filters('swpm_membership_levels_additional_menu_tabs_array', array());
+            foreach ($menu_tabs as $level_action => $title){
+                ?>
+                <a class="nav-tab <?php echo ($selected == $member_action) ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership_levels&level_action=<?php echo $level_action; ?>" ><?php SwpmUtils::e($title); ?></a>
+                <?php
+            }
+
+            ?>
+        </h2>
+        <!-- end nav menu tabs -->
+
+        <?php
+
+        do_action( 'swpm_membership_level_menu_after_nav_tabs' );
+
+        //Trigger hook so anyone listening for this particular action can handle the output.
+        do_action( 'swpm_membership_level_menu_body_' . $action );
+
+        //Allows an addon to completely override the body section of the membership level admin menu for a given action.
+        $output = apply_filters('swpm_membership_level_menu_body_override', '', $action);
+        if (!empty($output)) {
+            //An addon has overriden the body of this page for the given action. So no need to do anything in core.
+            echo $output;
+            echo '</div>';//<!-- end of wrap -->
+            return;
+        }
+
+        //Switch case for the various different actions handled by the core plugin.
+        switch ($action) {
+            case 'add':
+            case 'edit':
+                $this->process_form_request();
+                break;
+            case 'manage':
+                $this->manage();
+                break;
+            case 'category_list':
+                $this->manage_categroy();
+                break;
+            case 'post_list':
+                $this->manage_post();
+                break;
+            case 'delete':
+                $this->delete_level();
+            default:
+                $this->show_levels();
+                break;
+        }
+
+        echo '</div>';//<!-- end of wrap -->
+    }
+
+}

+ 35 - 0
simple-membership/classes/class.swpm-messages.php

@@ -0,0 +1,35 @@
+<?php
+
+/**
+ *
+ *
+ * @author nur
+ */
+class SwpmMessages {
+
+    private $messages;
+    private $session_key;
+
+    public function __construct() {
+        $this->messages = get_option('swpm-messages');
+        $this->sesion_key = $_COOKIE['swpm_session'];
+    }
+
+    public function get($key) {
+        $combined_key = $this->session_key . '_' . $key;
+        if (isset($this->messages[$combined_key])) {
+            $m = $this->messages[$combined_key];
+            unset($this->messages[$combined_key]);
+            update_option('swpm-messages', $this->messages);
+            return $m;
+        }
+        return '';
+    }
+
+    public function set($key, $value) {
+        $combined_key = $this->session_key . '_' . $key;
+        $this->messages[$combined_key] = $value;
+        update_option('swpm-messages', $this->messages);
+    }
+
+}

+ 10 - 0
simple-membership/classes/class.swpm-notification-bus.php

@@ -0,0 +1,10 @@
+<?php
+
+/**
+ * Description of BNotificationBus
+ *
+ * @author nur
+ */
+class SwpmNotificationBus {
+    
+}

+ 49 - 0
simple-membership/classes/class.swpm-permission-collection.php

@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of bPermissionCollection
+ *
+ * @author nur
+ */
+class SwpmPermissionCollection {
+    protected $permissions;
+    protected static $instance;
+    
+    protected function __construct() {
+        $this->permissions = array();
+    }
+    
+    public static function get_instance(){
+        self::$_this = empty(self::$_this)? new SwpmPermissionCollection():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[] = SwpmPermission::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;
+    }
+}

+ 65 - 0
simple-membership/classes/class.swpm-permission.php

@@ -0,0 +1,65 @@
+<?php
+
+include_once('class.swpm-protection-base.php');
+
+class SwpmPermission extends SwpmProtectionBase {
+
+    private static $_this = array();
+
+    private function __construct($level_id) {
+        $this->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 SwpmPermission($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);
+    }
+
+}

+ 240 - 0
simple-membership/classes/class.swpm-post-list.php

@@ -0,0 +1,240 @@
+<?php
+
+/**
+ * BCategoryList
+ *
+ * @author nur
+ */
+if (!class_exists('WP_List_Table')) {
+    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
+}
+
+class SwpmPostList extends WP_List_Table {
+
+    public $selected_level_id = 1;
+    public $post;
+    public $type;
+
+    function __construct() {
+        parent::__construct(array(
+            'singular' => SwpmUtils::_('Membership Level'),
+            'plural' => SwpmUtils::_('Membership Levels'),
+            'ajax' => false
+        ));
+        $selected = filter_input(INPUT_POST, 'membership_level_id');
+        $this->selected_level_id = empty($selected) ? 1 : $selected;
+        $this->post = ($this->selected_level_id == 1) ?
+                SwpmProtection::get_instance() :
+                SwpmPermission::get_instance($this->selected_level_id);
+        $this->type = filter_input(INPUT_GET, 'list_type');
+        if (is_null($this->type)) {
+            $this->type = filter_input(INPUT_POST, 'list_type');
+        }
+        if (is_null($this->type)) {
+            $this->type = 'post';
+        }
+    }
+
+    function get_columns() {
+        switch ($this->type) {
+            case 'page':
+                return array(
+                    'cb' => '<input type="checkbox" />'
+                    , 'date' => SwpmUtils::_('Date')
+                    , 'title' => SwpmUtils::_('Title')
+                    , 'author' => SwpmUtils::_('Author')
+                    , 'status' => SwpmUtils::_('Status')
+                );
+                break;
+            case 'post':
+                return array(
+                    'cb' => '<input type="checkbox" />'
+                    , 'date' => SwpmUtils::_('Date')
+                    , 'title' => SwpmUtils::_('Title')
+                    , 'author' => SwpmUtils::_('Author')
+                    , 'categories' => SwpmUtils::_('Categories')
+                    , 'status' => SwpmUtils::_('Status')
+                );
+                break;
+            case 'custom_post':
+                return array(
+                    'cb' => '<input type="checkbox" />'
+                    , 'date' => SwpmUtils::_('Date')
+                    , 'title' => SwpmUtils::_('Title')
+                    , 'author' => SwpmUtils::_('Author')
+                    , 'type' => SwpmUtils::_('Type')
+                    , 'status' => SwpmUtils::_('Status')
+                );
+                break;
+        }
+    }
+
+    function get_sortable_columns() {
+        return array();
+    }
+
+    function column_default($item, $column_name) {
+        return stripslashes($item[$column_name]);
+    }
+
+    function column_term_id($item) {
+        return $item->term_id;
+    }
+
+    function column_taxonomy($item) {
+        $taxonomy = $item->taxonomy;
+        if ($taxonomy == 'category') {
+            $taxonomy = 'Post Category';
+        } else {
+            $taxonomy = 'Custom Post Type (' . $taxonomy . ')';
+        }
+        return $taxonomy;
+    }
+
+    function column_cb($item) {
+        return sprintf(
+                '<input type="hidden" name="ids_in_page[]" value="%s">
+            <input type="checkbox" %s name="ids[]" value="%s" />', $item['ID'], $item['protected'], $item['ID']
+        );
+    }
+
+    public static function update_post_list() {
+        //Check we are on the admin end and user has management permission 
+        SwpmMiscUtils::check_user_permission_and_is_admin('post protection update');
+
+        //Check nonce
+        $swpm_post_prot_update_nonce = filter_input(INPUT_POST, 'swpm_post_prot_update_nonce');
+        if (!wp_verify_nonce($swpm_post_prot_update_nonce, 'swpm_post_prot_update_nonce_action')) {
+            //Nonce check failed.
+            wp_die(SwpmUtils::_("Error! Nonce security verification failed for Post Protection Update action. Clear cache and try again."));
+        }
+        
+        $type = filter_input(INPUT_POST, 'list_type');
+
+        $selected = filter_input(INPUT_POST, 'membership_level_id');
+        $selected_level_id = empty($selected) ? 1 : $selected;
+        $post = ($selected_level_id == 1) ?
+                SwpmProtection::get_instance() :
+                SwpmPermission::get_instance($selected_level_id);
+        $args = array('ids' => array(
+                'filter' => FILTER_VALIDATE_INT,
+                'flags' => FILTER_REQUIRE_ARRAY,
+        ));
+        $filtered = filter_input_array(INPUT_POST, $args);
+        $ids = $filtered['ids'];
+        $args = array('ids_in_page' => array(
+                'filter' => FILTER_VALIDATE_INT,
+                'flags' => FILTER_REQUIRE_ARRAY,
+        ));
+        $filtered = filter_input_array(INPUT_POST, $args);
+        $ids_in_page = $filtered['ids_in_page'];
+        $post->remove($ids_in_page, $type)->apply($ids, $type)->save();
+        $message = array('succeeded' => true, 'message' => '<p class="swpm-green-box">' . SwpmUtils::_('Protection settings updated!') . '</p>');
+        SwpmTransfer::get_instance()->set('status', $message);
+    }
+
+    function prepare_items() {
+        global $wpdb;
+        switch ($this->type) {
+            case 'page':
+                $args = array(
+                    'child_of' => 0,
+                    'sort_order' => 'ASC',
+                    'sort_column' => 'post_title',
+                    'hierarchical' => 0,
+                    'parent' => -1,
+                );
+                $all_pages = get_pages($args);
+                $filtered_items = array();
+                foreach ($all_pages as $page) {
+                    $page_summary = array();
+                    $user_info = get_userdata($page->post_author);
+                    $page_summary['protected'] = $this->post->in_pages($page->ID) ? " checked='checked'" : "";
+                    $page_summary['ID'] = $page->ID;
+                    $page_summary['date'] = $page->post_date;
+                    $page_summary['title'] = '<a href="' . get_permalink($page->ID) . '" target="_blank">' . $page->post_title . '</a>';
+                    $page_summary['author'] = $user_info->user_login;
+                    $page_summary['status'] = $page->post_status;
+                    $filtered_items[] = $page_summary;
+                }
+                break;
+            case 'post':
+                $sql = "SELECT ID,post_date,post_title,post_author, post_type, post_status FROM $wpdb->posts ";
+                $sql .= " WHERE post_type = 'post' AND post_status = 'publish'";
+                $all_posts = $wpdb->get_results($sql);
+                $filtered_items = array();
+                foreach ($all_posts as $post) {
+                    //if($post->post_type=='page')continue;
+                    $post_summary = array();
+                    $user_info = get_userdata($post->post_author);
+                    $categories = get_the_category($post->ID);
+                    $cat = array();
+                    foreach ($categories as $category)
+                        $cat[] = $category->category_nicename;
+                    $post_summary['protected'] = $this->post->in_posts($post->ID) ? " checked='checked'" : "";
+                    $post_summary['ID'] = $post->ID;
+                    $post_summary['date'] = $post->post_date;
+                    $post_summary['title'] = '<a href="' . get_permalink($post->ID) . '" target="_blank">' . $post->post_title . '</a>';
+                    $post_summary['author'] = $user_info->user_login;
+                    $post_summary['categories'] = rawurldecode(implode(' ', $cat));
+                    $post_summary['status'] = $post->post_status;
+                    $filtered_items[] = $post_summary;
+                }
+                break;
+            case 'custom_post':
+                $filtered_items = array();
+                $args = array('public' => true, '_builtin' => false);
+                $post_types = get_post_types($args);
+                $arg = "'" . implode('\',\'', $post_types) . "'";
+                if (!empty($arg)) {
+                    $sql = "SELECT ID,post_date,post_title,post_author, post_type, post_status FROM $wpdb->posts ";
+                    $sql .= " WHERE post_type IN (" . $arg . ") AND (post_status='inherit' OR post_status='publish')";
+                    $all_posts = $wpdb->get_results($sql);
+                    foreach ($all_posts as $post) {
+                        $post_summary = array();
+                        $user_info = get_userdata($post->post_author);
+                        $post_summary['protected'] = $this->post->in_custom_posts($post->ID) ? "checked='checked'" : "";
+                        $post_summary['ID'] = $post->ID;
+                        $post_summary['date'] = $post->post_date;
+                        $post_summary['title'] = '<a href="' . get_permalink($post->ID) . '" target="_blank">' . $post->post_title . '</a>';
+                        $post_summary['author'] = $user_info->user_login;
+                        $post_summary['type'] = $post->post_type;
+                        $post_summary['status'] = $post->post_status;
+                        $filtered_items[] = $post_summary;
+                    }
+                }
+                break;
+        }
+        $totalitems = count($filtered_items);
+        $perpage = 100;
+        $paged = !empty($_GET["paged"]) ? sanitize_text_field($_GET["paged"]) : '';
+        if (empty($paged) || !is_numeric($paged) || $paged <= 0) {
+            $paged = 1;
+        }
+        $totalpages = ceil($totalitems / $perpage);
+        $offset = 0;
+        if (!empty($paged) && !empty($perpage)) {
+            $offset = ($paged - 1) * $perpage;
+        }
+        for ($i = $offset; $i < ((int) $offset + (int) $perpage) && !empty($filtered_items[$i]); $i++) {
+            $all_items[] = $filtered_items[$i];
+        }
+        $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 = $all_items;
+    }
+
+    function no_items() {
+        SwpmUtils::e('No items found.');
+    }
+
+}

+ 326 - 0
simple-membership/classes/class.swpm-protection-base.php

@@ -0,0 +1,326 @@
+<?php
+
+abstract class SwpmProtectionBase {
+
+    protected $bitmap;
+    protected $posts;
+    protected $pages;
+    protected $comments;
+    protected $categories;
+    protected $attachments;
+    protected $custom_posts;
+    protected $details;
+    protected $options;
+
+    private function __construct() {
+
+    }
+
+    protected function init($level_id) {
+        global $wpdb;
+        $this->owning_level_id = $level_id;
+        $query = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}swpm_membership_tbl WHERE "
+                . (is_numeric($level_id) ? 'id = %d' : 'md5(id) = %s' ), $level_id);
+        $result = $wpdb->get_row($query);
+
+        $this->bitmap = isset($result->permissions) ? $result->permissions : 0;
+        $this->posts = isset($result->post_list) ? (array) unserialize($result->post_list) : array();
+        $this->pages = isset($result->page_list) ? (array) unserialize($result->page_list) : array();
+        $this->comments = isset($result->comment_list) ? (array) unserialize($result->comment_list) : array();
+        $this->categories = isset($result->category_list) ? (array) unserialize($result->category_list) : array();
+        $this->attachments = isset($result->attachment_list) ? (array) unserialize($result->attachment_list) : array();
+        $this->custom_posts = isset($result->custom_post_list) ? (array) unserialize($result->custom_post_list) : array();
+        $this->options = isset($result->options) ? (array) unserialize($result->options) : array();
+        $this->disable_bookmark = isset($result->disable_bookmark_list) ? (array) unserialize($result->disable_bookmark_list) : array();
+        $this->details = (array) $result;
+    }
+
+    public function apply($ids, $type) {
+        $post_types = get_post_types(array('public' => true, '_builtin' => false));
+        if (in_array($type, $post_types)) {
+            $type = 'custom_post';
+        }
+        return $this->update_perms($ids, true, $type);
+    }
+
+    public function remove($ids, $type) {
+        $post_types = get_post_types(array('public' => true, '_builtin' => false));
+        if (in_array($type, $post_types)) {
+            $type = 'custom_post';
+        }
+        return $this->update_perms($ids, false, $type);
+    }
+
+    public function get_options() {
+        return $this->options;
+    }
+
+    public function get_posts() {
+        return $this->posts;
+    }
+
+    public function get_pages() {
+        return $this->pages;
+    }
+
+    public function get_comments() {
+        return $this->comments;
+    }
+
+    public function get_categories() {
+        return $this->categories;
+    }
+
+    public function get_attachments() {
+        return $this->attachments;
+    }
+
+    public function get_custom_posts() {
+        return $this->custom_posts;
+    }
+
+    public function is_bookmark_disabled($id) {
+        $posts = isset($this->disable_bookmark['posts']) ?
+                (array) $this->disable_bookmark['posts'] : array();
+        $pages = isset($this->disable_bookmark['pages']) ?
+                (array) $this->disable_bookmark['pages'] : array();
+        return in_array($id, $pages) || in_array($id, $posts);
+    }
+
+    public function in_posts($id) {
+        return (/* ($this->bitmap&4)===4) && */in_array($id, (array) $this->posts));
+    }
+
+    public function in_pages($id) {
+        return (/* ($this->bitmap&8)===8) && */ in_array($id, (array) $this->pages));
+    }
+
+    public function in_attachments($id) {
+        return (/* ($this->bitmap&16)===16) && */in_array($id, (array) $this->attachments));
+    }
+
+    public function in_custom_posts($id) {
+        return (/* ($this->bitmap&32)===32) && */ in_array($id, (array) $this->custom_posts));
+    }
+
+    public function in_comments($id) {
+        return (/* ($this->bitmap&2)===2) && */ in_array($id, (array) $this->comments));
+    }
+
+    public function in_categories($id) {
+        if (empty($this->categories))
+            return false;
+        return (/* ($this->bitmap&1)===1) && */ in_array($id, (array) $this->categories));
+    }
+
+    public function post_in_categories($post_id) {
+        if (empty($this->categories)){
+            return false;
+        }
+        $taxonomies = get_taxonomies(array('public' => true,'_builtin'=>false));
+        if (!is_array($taxonomies) || empty($taxonomies)) {
+        	$taxonomies = 'category';
+        } else {
+        	$taxonomies['category'] = 'category';
+    	}
+        $terms = wp_get_post_terms( $post_id, $taxonomies, array('fields'=>'ids'));
+        if(!is_array($terms)){
+            return false;
+        }
+        
+        foreach ($terms as $key=>$value){
+            if (in_array($value, $this->categories)) {return true;}
+        }
+        return false;               
+    }
+
+    public function in_parent_categories($id) {
+    if (empty($this->categories)){
+            return false;
+        }
+        $taxonomies = get_taxonomies(array('public' => true,'_builtin'=>false));
+        if (!is_array($taxonomies) || empty($taxonomies)) {
+        	$taxonomies = 'category';
+        } else {
+        	$taxonomies['category'] = 'category';
+    	}
+        $terms = get_term($id, $taxonomies);
+        if(!is_array($terms)){
+            return false;
+        }
+        
+        foreach ($terms as $term){
+            if ($term->parent == 0) {continue;}
+            
+            if (in_array($term->parent, $this->categories)) {return true;}
+        }
+        return false;
+    }
+
+    public function post_in_parent_categories($post_id) {
+        if (empty($this->categories)){
+            return false;
+        }
+        $taxonomies = get_taxonomies(array('public' => true,'_builtin'=>false));
+        if (!is_array($taxonomies) || empty($taxonomies)) {
+        	$taxonomies = 'category';
+        } else {
+        	$taxonomies['category'] = 'category';
+    	}
+        $terms = wp_get_post_terms( $post_id, $taxonomies, array('fields'=>'all'));
+        if(!is_array($terms)){
+            return false;
+        }
+        
+        foreach ($terms as $term){
+            if ($term->parent != 0 &&in_array($term->parent, $this->categories)) {
+                return true;                
+            }            
+        }
+
+        return false;
+    }
+
+    public function add_posts($ids) {
+        return $this->update_perms($ids, true, 'post');
+    }
+
+    public function add_pages($ids) {
+        return $this->update_perms($ids, true, 'page');
+    }
+
+    public function add_attachments($ids) {
+        return $this->update_perms($ids, true, 'attachment');
+    }
+
+    public function add_comments($ids) {
+        return $this->update_perms($ids, true, 'comment');
+    }
+
+    public function add_categories($ids) {
+        return $this->update_perms($ids, true, 'category');
+    }
+
+    public function add_custom_posts($ids) {
+        return $this->update_perms($ids, true, 'custom_post');
+    }
+
+    public function remove_posts($ids) {
+        return $this->update_perms($ids, false, 'post');
+    }
+
+    public function remove_pages($ids) {
+        return $this->update_perms($ids, false, 'page');
+    }
+
+    public function remove_attachments($ids) {
+        return $this->update_perms($ids, false, 'attachment');
+    }
+
+    public function remove_comments($ids) {
+        return $this->update_perms($ids, false, 'comment');
+    }
+
+    public function remove_categories($ids) {
+        return $this->update_perms($ids, false, 'category');
+    }
+
+    public function remove_custom_posts($ids) {
+        return $this->update_perms($ids, false, 'custom_post');
+    }
+
+    private function update_perms($ids, $set, $type) {
+        $list = null;
+        $index = '';
+        if (empty($ids)) {
+            return $this;
+        }
+        $ids = (array) $ids;
+        switch ($type) {
+            case 'page':
+                $list = $this->pages;
+                $index = 'page_list';
+                break;
+            case 'post':
+                $list = $this->posts;
+                $index = 'post_list';
+                break;
+            case 'attachment':
+                $list = $this->attachments;
+                $index = 'attachment_list';
+                break;
+            case 'comment':
+                $list = $this->comments;
+                $index = 'comment_list';
+                break;
+            case 'category':
+                $list = $this->categories;
+                $index = 'category_list';
+                break;
+            case 'custom_post':
+                $list = $this->custom_posts;
+                $index = 'custom_post_list';
+                break;
+            default:
+                break;
+        }
+
+        if (!empty($index)) {
+            if ($set) {
+                $list = array_merge($list, $ids);
+                $list = array_unique($list);
+            } else {
+                $list = array_diff($list, $ids);
+            }
+            switch ($type) {
+                case 'page':
+                    $this->pages = $list;
+                    break;
+                case 'post':
+                    $this->posts = $list;
+                    break;
+                case 'attachment':
+                    $this->attachments = $list;
+                    break;
+                case 'comment':
+                    $this->comments = $list;
+                    break;
+                case 'category':
+                    $this->categories = $list;
+                    break;
+                case 'custom_post':
+                    $this->custom_posts = $list;
+                    break;
+                default:
+                    break;
+            }
+            $this->details[$index] = $list;
+        }
+        return $this;
+    }
+
+    public function save() {
+        global $wpdb;
+        $data = array();
+
+        $list_type = array('page_list', 'post_list', 'attachment_list',
+            'custom_post_list', 'comment_list', 'category_list');
+        foreach ($this->details as $key => $value) {
+            if ($key == 'id')
+                continue;
+            if (is_serialized($value) || !in_array($key, $list_type))
+                $data[$key] = $value;
+            else
+                $data[$key] = serialize($value);
+        }
+        $wpdb->update($wpdb->prefix . "swpm_membership_tbl", $data, array('id' => $this->owning_level_id));
+    }
+
+    public function get($key, $default = '') {
+        if (isset($this->details[$key])) {
+            return $this->details[$key];
+        }
+        return $default;
+    }
+
+}

+ 70 - 0
simple-membership/classes/class.swpm-protection.php

@@ -0,0 +1,70 @@
+<?php
+
+include_once('class.swpm-protection-base.php');
+
+class SwpmProtection extends SwpmProtectionBase {
+
+    private static $_this;
+
+    private function __construct() {
+        $this->msg = "";
+        $this->init(1);
+    }
+
+    public static function get_instance() {
+        self::$_this = empty(self::$_this) ? (new SwpmProtection()) : self::$_this;
+        return self::$_this;
+    }
+
+    public function is_protected($id) {
+        if ($this->post_in_parent_categories($id) || $this->post_in_categories($id)) {
+            $this->msg = '<p style="background: #FFF6D5; border: 1px solid #D1B655; color: #3F2502; margin: 10px 0px 10px 0px; padding: 5px 5px 5px 10px;">
+                    ' . SwpmUtils::_('The category or parent category of this post is protected. You can change the category protection settings from the ') . 
+                    '<a href="admin.php?page=simple_wp_membership_levels&level_action=category_list" target="_blank">' . SwpmUtils::_('category protection menu') . '</a>.
+                    </p>';
+            return true;
+        }
+        return $this->in_posts($id) || $this->in_pages($id) || $this->in_attachments($id) || $this->in_custom_posts($id);
+    }
+
+    public function get_last_message() {
+        return $this->msg;
+    }
+
+    public function is_protected_post($id) {
+        return /* (($this->bitmap&4) != 4) && */ $this->in_posts($id);
+    }
+
+    public function is_protected_page($id) {
+        return /* (($this->bitmap&4) != 4) && */ $this->in_pages($id);
+    }
+
+    public function is_protected_attachment($id) {
+        return /* (($this->bitmap&16)!=16) && */ $this->in_attachments($id);
+    }
+
+    public function is_protected_custom_post($id) {
+        return /* (($this->bitmap&32)!=32) && */ $this->in_custom_posts($id);
+    }
+
+    public function is_protected_comment($id) {
+        return /* (($this->bitmap&2)!=2) && */ $this->in_comments($id);
+    }
+
+    public function is_post_in_protected_category($post_id) {
+        return /* (($this->bitmap&1)!=1) && */ $this->post_in_categories($post_id);
+    }
+
+    public function is_post_in_protected_parent_category($post_id) {
+        return /* (($this->bitmap&1)!=1) && */ $this->post_in_parent_categories($post_id);
+    }
+
+    public function is_protected_category($id) {
+        return /* (($this->bitmap&1)!=1) && */ $this->in_categories($id);
+    }
+
+    public function is_protected_parent_category($id) {
+        return /* (($this->bitmap&1)!=1) && */ $this->in_parent_categories($id);
+    }
+
+}

+ 127 - 0
simple-membership/classes/class.swpm-registration.php

@@ -0,0 +1,127 @@
+<?php
+
+/**
+ * Description of BRegistration
+ *
+ * @author nur
+ */
+abstract class SwpmRegistration {
+
+	protected $member_info     = array();
+	var $email_activation      = false;
+	protected static $_intance = null;
+
+	//public abstract static function get_instance();
+	protected function send_reg_email() {
+		global $wpdb;
+		if ( empty( $this->member_info ) ) {
+			return false;
+		}
+
+		$member_info = $this->member_info;
+		$settings    = SwpmSettings::get_instance();
+		$subject     = $settings->get_value( 'reg-complete-mail-subject' );
+		$body        = $settings->get_value( 'reg-complete-mail-body' );
+
+		if ( $this->email_activation ) {
+			$swpm_user = SwpmMemberUtils::get_user_by_user_name( $member_info['user_name'] );
+			$member_id = $swpm_user->member_id;
+			$act_code  = md5( uniqid() . $member_id );
+			$enc_pass  = SwpmUtils::crypt( $member_info['plain_password'] );
+			$user_data = array(
+				'timestamp'      => time(),
+				'act_code'       => $act_code,
+				'plain_password' => $enc_pass,
+			);
+			$user_data = apply_filters( 'swpm_email_activation_data', $user_data );
+			update_option( 'swpm_email_activation_data_usr_' . $member_id, $user_data, false );
+			$body                           = $settings->get_value( 'email-activation-mail-body' );
+			$subject                        = $settings->get_value( 'email-activation-mail-subject' );
+			$activation_link                = add_query_arg(
+				array(
+					'swpm_email_activation' => '1',
+					'swpm_member_id'        => $member_id,
+					'swpm_token'            => $act_code,
+				),
+				get_home_url()
+			);
+
+			// Allow hooks to change the value of activation_link
+			$activation_link = apply_filters('swpm_send_reg_email_activation_link', $activation_link);
+
+			$member_info['activation_link'] = $activation_link;
+		}
+
+		$from_address                         = $settings->get_value( 'email-from' );
+		$login_link                           = $settings->get_value( 'login-page-url' );
+		$headers                              = 'From: ' . $from_address . "\r\n";
+		$member_info['membership_level_name'] = SwpmPermission::get_instance( $member_info['membership_level'] )->get( 'alias' );
+		$member_info['password']              = $member_info['plain_password'];
+		$member_info['login_link']            = $login_link;
+		$values                               = array_values( $member_info );
+		$keys                                 = array_map( 'swpm_enclose_var', array_keys( $member_info ) );
+		$body                                 = html_entity_decode( $body );
+		$body                                 = str_replace( $keys, $values, $body );
+
+		$swpm_user = SwpmMemberUtils::get_user_by_user_name( $member_info['user_name'] );
+		$member_id = $swpm_user->member_id;
+		$body      = SwpmMiscUtils::replace_dynamic_tags( $body, $member_id ); //Do the standard merge var replacement.
+
+		$email = sanitize_email( filter_input( INPUT_POST, 'email', FILTER_UNSAFE_RAW ) );
+
+		if ( empty( $email ) ) {
+			$email = $swpm_user->email;
+		}
+
+		$body = apply_filters( 'swpm_registration_complete_email_body', $body ); //This filter can be used to modify the registration complete email body dynamically.
+		//Send notification email to the member
+		$subject = apply_filters( 'swpm_email_registration_complete_subject', $subject );
+		$body    = apply_filters( 'swpm_email_registration_complete_body', $body ); //You can override the email to empty to disable this email.
+		if ( ! empty( $body ) ) {
+			SwpmMiscUtils::mail( trim( $email ), $subject, $body, $headers );
+			SwpmLog::log_simple_debug( 'Member registration complete email sent to: ' . $email . '. From email address value used: ' . $from_address, true );
+		} else {
+			SwpmLog::log_simple_debug( 'NOTICE: Registration complete email body value is empty. Member registration complete email will NOT be sent.', true );
+		}
+
+		if ( $settings->get_value( 'enable-admin-notification-after-reg' ) && ! $this->email_activation ) {
+			//Send notification email to the site admin
+			$admin_notification  = $settings->get_value( 'admin-notification-email' );
+			$admin_notification  = empty( $admin_notification ) ? $from_address : $admin_notification;
+			$notify_emails_array = explode( ',', $admin_notification );
+
+			$headers = 'From: ' . $from_address . "\r\n";
+
+			$admin_notify_subject = $settings->get_value( 'reg-complete-mail-subject-admin' );
+			if ( empty( $admin_notify_subject ) ) {
+				$admin_notify_subject = 'Notification of New Member Registration';
+			}
+
+			$admin_notify_body = $settings->get_value( 'reg-complete-mail-body-admin' );
+			if ( empty( $admin_notify_body ) ) {
+				$admin_notify_body = "A new member has completed the registration.\n\n" .
+						"Username: {user_name}\n" .
+						"Email: {email}\n\n" .
+						"Please login to the admin dashboard to view details of this user.\n\n" .
+						"You can customize this email message from the Email Settings menu of the plugin.\n\n" .
+						'Thank You';
+			}
+			$additional_args   = array( 'password' => $member_info['plain_password'] );
+			$admin_notify_body = SwpmMiscUtils::replace_dynamic_tags( $admin_notify_body, $member_id, $additional_args ); //Do the standard merge var replacement.
+
+			foreach ( $notify_emails_array as $to_email ) {
+				$to_email             = trim( $to_email );
+				$admin_notify_subject = apply_filters( 'swpm_email_admin_notify_subject', $admin_notify_subject );
+				$admin_notify_body    = apply_filters( 'swpm_email_admin_notify_body', $admin_notify_body );
+				SwpmMiscUtils::mail( $to_email, $admin_notify_subject, $admin_notify_body, $headers );
+				SwpmLog::log_simple_debug( 'Admin notification email sent to: ' . $to_email, true );
+			}
+		}
+		return true;
+	}
+
+}
+
+function swpm_enclose_var( $n ) {
+	return '{' . $n . '}';
+}

+ 110 - 0
simple-membership/classes/class.swpm-self-action-handler.php

@@ -0,0 +1,110 @@
+<?php
+
+class SwpmSelfActionHandler {
+
+    public function __construct() {
+        //Register all the self action hooks the plugin needs to handle
+        add_action('swpm_front_end_registration_complete_fb', array(&$this, 'after_registration_callback'));//For the form builder
+        add_action('swpm_front_end_registration_complete_user_data', array(&$this, 'after_registration_callback'));
+
+        add_action('swpm_membership_level_changed', array(&$this, 'handle_membership_level_changed_action'));
+
+        add_action('swpm_payment_ipn_processed', array(&$this, 'handle_swpm_payment_ipn_processed'));
+
+        add_filter('swpm_after_logout_redirect_url', array(&$this, 'handle_after_logout_redirection'));
+        add_filter('swpm_auth_cookie_expiry_value', array(&$this, 'handle_auth_cookie_expiry_value'));
+    }
+
+    public function handle_auth_cookie_expiry_value($expire){
+
+        $logout_member_on_browser_close = SwpmSettings::get_instance()->get_value('logout-member-on-browser-close');
+        if (!empty($logout_member_on_browser_close)) {
+            //This feature is enabled.
+            //Setting auth cookie expiry value to 0.
+            $expire = apply_filters( 'swpm_logout_on_close_auth_cookie_expiry_value', 0 );
+        }
+
+        return $expire;
+    }
+
+    public function handle_after_logout_redirection($redirect_url){
+        $after_logout_url = SwpmSettings::get_instance()->get_value('after-logout-redirection-url');
+        if(!empty($after_logout_url)){
+            //After logout URL is being used. Override re-direct URL.
+            $redirect_url = $after_logout_url;
+        }
+        return $redirect_url;
+    }
+
+    public function handle_swpm_payment_ipn_processed($ipn_data){
+        $ipn_forward_url = SwpmSettings::get_instance()->get_value('payment-notification-forward-url');
+        if(!empty($ipn_forward_url)){
+            SwpmLog::log_simple_debug("Payment Notification Forwarding is Enabled. Posting the payment data to URL: " . $ipn_forward_url, true);
+            $response = wp_remote_post($ipn_forward_url, $ipn_data);
+            if (is_wp_error($response)) {
+                $error_message = $response->get_error_message();
+                SwpmLog::log_simple_debug("There was an error posting the payment data. Error message: " . $error_message, true);
+            }
+        }
+    }
+
+    public function after_registration_callback($user_data){
+
+        //Handle auto login after registration if enabled
+        $enable_auto_login = SwpmSettings::get_instance()->get_value('auto-login-after-rego');
+        if (!empty($enable_auto_login)){
+            SwpmLog::log_simple_debug("Auto login after registration feature is enabled in settings. Performing auto login for user: " . $user_data['user_name'], true);
+            $login_page_url = SwpmSettings::get_instance()->get_value('login-page-url');
+
+            // Allow hooks to change the value of login_page_url
+            $login_page_url = apply_filters('swpm_after_reg_callback_login_page_url', $login_page_url);
+
+            $encoded_pass = base64_encode($user_data['plain_password']);
+            $swpm_auto_login_nonce = wp_create_nonce('swpm-auto-login-nonce');
+            $arr_params = array(
+                'swpm_auto_login' => '1',
+                'swpm_user_name' => urlencode($user_data['user_name']),
+                'swpm_encoded_pw' => $encoded_pass,
+                'swpm_auto_login_nonce' => $swpm_auto_login_nonce,
+            );
+            $redirect_page = add_query_arg($arr_params, $login_page_url);
+            wp_redirect($redirect_page);
+            exit(0);
+        }
+
+    }
+
+    public function handle_membership_level_changed_action($args){
+        $swpm_id = $args['member_id'];
+        $old_level = $args['from_level'];
+        $new_level = $args['to_level'];
+        SwpmLog::log_simple_debug('swpm_membership_level_changed action triggered. Member ID: '.$swpm_id.', Old Level: '.$old_level.', New Level: '.$new_level, true);
+
+        //Check to see if the old and the new levels are the same or not.
+        if(trim($old_level) == trim($new_level)){
+            SwpmLog::log_simple_debug('The to (Level ID: '.$new_level.') and from (Level ID: '.$old_level.') values are the same. Nothing to do here.', true);
+            return;
+        }
+
+        //Find record for this user
+        SwpmLog::log_simple_debug('Retrieving user record for member ID: '.$swpm_id, true);
+        $resultset = SwpmMemberUtils::get_user_by_id($swpm_id);
+        if($resultset){
+            //Found a record. Lets do some level update specific changes.
+            //$emailaddress  = $resultset->email;
+            //$account_status = $resultset->account_state;
+
+            //Retrieve the new memberhsip level's details
+            $level_row = SwpmUtils::get_membership_level_row_by_id($new_level);
+
+            //Update the WP user role according to the new level's configuration (if applicable).
+            $user_role = $level_row->role;
+            $user_info = get_user_by('login', $resultset->user_name);
+            $wp_user_id = $user_info->ID;
+            SwpmLog::log_simple_debug('Calling user role update function.', true);
+            SwpmMemberUtils::update_wp_user_role($wp_user_id, $user_role);
+        }
+
+    }
+
+}

+ 0 - 0
simple-membership/classes/class.swpm-session.php


+ 1245 - 0
simple-membership/classes/class.swpm-settings.php

@@ -0,0 +1,1245 @@
+<?php
+
+class SwpmSettings {
+
+	private static $_this;
+	private $settings;
+	public $current_tab;
+	private $tabs;
+
+	private function __construct() {
+		$this->settings = (array) get_option( 'swpm-settings' );
+	}
+
+	public function init_config_hooks() {
+		//This function is called from "admin_init"
+		//It sets up the various tabs and the fields for the settings admin page.
+
+		if ( is_admin() ) { // for frontend just load settings but dont try to render settings page.
+			//Read the value of tab query arg.
+			$tab               = isset( $_REQUEST['tab'] ) ? sanitize_text_field( $_REQUEST['tab'] ) : 1;
+			$this->current_tab = empty( $tab ) ? 1 : $tab;
+
+			//Setup the available settings tabs array.
+			$this->tabs = array(
+				1 => SwpmUtils::_( 'General Settings' ),
+				2 => SwpmUtils::_( 'Payment Settings' ),
+				3 => SwpmUtils::_( 'Email Settings' ),
+				4 => SwpmUtils::_( 'Tools' ),
+				5 => SwpmUtils::_( 'Advanced Settings' ),
+				6 => SwpmUtils::_( 'Addons Settings' ),
+			);
+
+			//Register the draw tab action hook. It will be triggered using do_action("swpm-draw-settings-nav-tabs")
+			add_action( 'swpm-draw-settings-nav-tabs', array( &$this, 'draw_tabs' ) );
+
+			//Register the various settings fields for the current tab.
+			$method = 'tab_' . $this->current_tab;
+			if ( method_exists( $this, $method ) ) {
+				$this->$method();
+			}
+		}
+	}
+
+	private function tab_1() {
+		//Register settings sections and fileds for the general settings tab.
+
+		register_setting( 'swpm-settings-tab-1', 'swpm-settings', array( &$this, 'sanitize_tab_1' ) );
+
+		//This settings section has no heading
+		add_settings_section( 'swpm-general-post-submission-check', '', array( &$this, 'swpm_general_post_submit_check_callback' ), 'simple_wp_membership_settings' );
+
+		add_settings_section( 'swpm-documentation', SwpmUtils::_( 'Plugin Documentation' ), array( &$this, 'swpm_documentation_callback' ), 'simple_wp_membership_settings' );
+		add_settings_section( 'general-settings', SwpmUtils::_( 'General Settings' ), array( &$this, 'general_settings_callback' ), 'simple_wp_membership_settings' );
+		add_settings_field(
+			'enable-free-membership',
+			SwpmUtils::_( 'Enable Free Membership' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'general-settings',
+			array(
+				'item'    => 'enable-free-membership',
+				'message' => SwpmUtils::_( 'Enable/disable registration for free membership level. When you enable this option, make sure to specify a free membership level ID in the field below.' ),
+			)
+		);
+		add_settings_field(
+			'free-membership-id',
+			SwpmUtils::_( 'Free Membership Level ID' ),
+			array( &$this, 'textfield_small_callback' ),
+			'simple_wp_membership_settings',
+			'general-settings',
+			array(
+				'item'    => 'free-membership-id',
+				'message' => SwpmUtils::_( 'Assign free membership level ID' ),
+			)
+		);
+		add_settings_field(
+			'enable-moretag',
+			SwpmUtils::_( 'Enable More Tag Protection' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'general-settings',
+			array(
+				'item'    => 'enable-moretag',
+				'message' => SwpmUtils::_( 'Enables or disables "more" tag protection in the posts and pages. Anything after the More tag is protected. Anything before the more tag is teaser content.' ),
+			)
+		);
+		add_settings_field(
+			'hide-adminbar',
+			SwpmUtils::_( 'Hide Adminbar' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'general-settings',
+			array(
+				'item'    => 'hide-adminbar',
+				'message' => SwpmUtils::_( 'WordPress shows an admin toolbar to the logged in users of the site. Check this if you want to hide that admin toolbar in the frontend of your site.' ),
+			)
+		);
+		add_settings_field(
+			'show-adminbar-admin-only',
+			SwpmUtils::_( 'Show Adminbar to Admin' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'general-settings',
+			array(
+				'item'    => 'show-adminbar-admin-only',
+				'message' => SwpmUtils::_( 'Use this option if you want to show the admin toolbar to admin users only. The admin toolbar will be hidden for all other users.' ),
+			)
+		);
+		add_settings_field(
+			'disable-access-to-wp-dashboard',
+			SwpmUtils::_( 'Disable Access to WP Dashboard' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'general-settings',
+			array(
+				'item'    => 'disable-access-to-wp-dashboard',
+				'message' => SwpmUtils::_( 'WordPress allows a standard wp user to be able to go to the wp-admin URL and access his profile from the wp dashbaord. Using this option will prevent any non admin users from going to the wp dashboard.' ),
+			)
+		);
+
+		add_settings_field(
+			'default-account-status',
+			SwpmUtils::_( 'Default Account Status' ),
+			array( &$this, 'selectbox_callback' ),
+			'simple_wp_membership_settings',
+			'general-settings',
+			array(
+				'item'    => 'default-account-status',
+				'options' => SwpmUtils::get_account_state_options(),
+				'default' => 'active',
+				'message' => SwpmUtils::_( 'Select the default account status for newly registered users. If you want to manually approve the members then you can set the status to "Pending".' ),
+			)
+		);
+
+		add_settings_field(
+			'members-login-to-comment',
+			SwpmUtils::_( 'Members Must be Logged in to Comment' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'general-settings',
+			array(
+				'item'    => 'members-login-to-comment',
+				'message' => SwpmUtils::_( 'Enable this option if you only want the members of the site to be able to post a comment.' ),
+			)
+		);
+
+		/*
+		  add_settings_field('protect-everything',  SwpmUtils::_('Protect Everything'),
+		  array(&$this, 'checkbox_callback'), 'simple_wp_membership_settings', 'general-settings',
+		  array('item' => 'protect-everything',
+		  'message'=>SwpmUtils::_('Check this box if you want to protect all posts/pages by default.')));
+		 */
+
+		add_settings_section( 'pages-settings', SwpmUtils::_( 'Pages Settings' ), array( &$this, 'pages_settings_callback' ), 'simple_wp_membership_settings' );
+		add_settings_field(
+			'login-page-url',
+			SwpmUtils::_( 'Login Page URL' ),
+			array( &$this, 'textfield_long_callback' ),
+			'simple_wp_membership_settings',
+			'pages-settings',
+			array(
+				'item'    => 'login-page-url',
+				'message' => '',
+			)
+		);
+		add_settings_field(
+			'registration-page-url',
+			SwpmUtils::_( 'Registration Page URL' ),
+			array( &$this, 'textfield_long_callback' ),
+			'simple_wp_membership_settings',
+			'pages-settings',
+			array(
+				'item'    => 'registration-page-url',
+				'message' => '',
+			)
+		);
+		add_settings_field(
+			'join-us-page-url',
+			SwpmUtils::_( 'Join Us Page URL' ),
+			array( &$this, 'textfield_long_callback' ),
+			'simple_wp_membership_settings',
+			'pages-settings',
+			array(
+				'item'    => 'join-us-page-url',
+				'message' => '',
+			)
+		);
+		add_settings_field(
+			'profile-page-url',
+			SwpmUtils::_( 'Edit Profile Page URL' ),
+			array( &$this, 'textfield_long_callback' ),
+			'simple_wp_membership_settings',
+			'pages-settings',
+			array(
+				'item'    => 'profile-page-url',
+				'message' => '',
+			)
+		);
+		add_settings_field(
+			'reset-page-url',
+			SwpmUtils::_( 'Password Reset Page URL' ),
+			array( &$this, 'textfield_long_callback' ),
+			'simple_wp_membership_settings',
+			'pages-settings',
+			array(
+				'item'    => 'reset-page-url',
+				'message' => '',
+			)
+		);
+
+		add_settings_section( 'debug-settings', SwpmUtils::_( 'Test & Debug Settings' ), array( &$this, 'testndebug_settings_callback' ), 'simple_wp_membership_settings' );
+
+		$debug_field_help_text  = SwpmUtils::_( 'Check this option to enable debug logging.' );
+		$debug_field_help_text .= SwpmUtils::_( ' This can be useful when troubleshooting an issue. Turn it off and reset the log files after the troubleshooting is complete.' );
+		$debug_field_help_text .= '<br />';
+		$debug_field_help_text .= '<br />- ' . SwpmUtils::_( 'View general debug log file by clicking ' ) . '<a href="' . SIMPLE_WP_MEMBERSHIP_URL . '/log.txt" target="_blank">' . SwpmUtils::_( 'here' ) . '</a>.';
+		$debug_field_help_text .= '<br />- ' . SwpmUtils::_( 'View login related debug log file by clicking ' ) . '<a href="' . SIMPLE_WP_MEMBERSHIP_URL . '/log-auth.txt" target="_blank">' . SwpmUtils::_( 'here' ) . '</a>.';
+		$debug_field_help_text .= '<br />- ' . SwpmUtils::_( 'Reset debug log files by clicking ' ) . '<a href="admin.php?page=simple_wp_membership_settings&swmp_reset_log=1" target="_blank">' . SwpmUtils::_( 'here' ) . '</a>.';
+		add_settings_field(
+			'enable-debug',
+			SwpmUtils::_( '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',
+			SwpmUtils::_( 'Enable Sandbox Testing' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'debug-settings',
+			array(
+				'item'    => 'enable-sandbox-testing',
+				'message' => SwpmUtils::_( 'Enable this option if you want to do sandbox payment testing.' ),
+			)
+		);
+	}
+
+	private function tab_2() {
+		//Register settings sections and fileds for the payment settings tab.
+		register_setting( 'swpm-settings-tab-2', 'swpm-settings', array( $this, 'sanitize_tab_2' ) );
+		add_settings_section( 'stripe-global-settings', SwpmUtils::_( 'Stripe Global Settings' ), null, 'simple_wp_membership_settings' );
+		add_settings_field(
+			'stripe-prefill-member-email',
+			SwpmUtils::_( 'Pre-fill Member Email Address' ),
+			array( $this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'stripe-global-settings',
+			array(
+				'item'    => 'stripe-prefill-member-email',
+				'message' => SwpmUtils::_( 'Pre-fills the email address of the logged-in member on the Stripe checkout form when possible' ),
+			)
+		);
+		add_settings_field(
+			'stripe-test-public-key',
+			SwpmUtils::_( 'Test Publishable Key' ),
+			array( $this, 'textfield_callback' ),
+			'simple_wp_membership_settings',
+			'stripe-global-settings',
+			array(
+				'item'    => 'stripe-test-public-key',
+				'message' => SwpmUtils::_( 'Stripe API Test publishable key' ),
+			)
+		);
+		add_settings_field(
+			'stripe-test-secret-key',
+			SwpmUtils::_( 'Test Secret Key' ),
+			array( $this, 'textfield_callback' ),
+			'simple_wp_membership_settings',
+			'stripe-global-settings',
+			array(
+				'item'    => 'stripe-test-secret-key',
+				'message' => SwpmUtils::_( 'Stripe API Test secret key' ),
+			)
+		);
+		add_settings_field(
+			'stripe-live-public-key',
+			SwpmUtils::_( 'Live Publishable Key' ),
+			array( $this, 'textfield_callback' ),
+			'simple_wp_membership_settings',
+			'stripe-global-settings',
+			array(
+				'item'    => 'stripe-live-public-key',
+				'message' => SwpmUtils::_( 'Stripe API Live publishable key' ),
+			)
+		);
+		add_settings_field(
+			'stripe-live-secret-key',
+			SwpmUtils::_( 'Live Secret Key' ),
+			array( $this, 'textfield_callback' ),
+			'simple_wp_membership_settings',
+			'stripe-global-settings',
+			array(
+				'item'    => 'stripe-live-secret-key',
+				'message' => SwpmUtils::_( 'Stripe API Live secret key' ),
+			)
+		);
+	}
+
+	private function tab_3() {
+		//Register settings sections and fileds for the email settings tab.
+
+		register_setting( 'swpm-settings-tab-3', 'swpm-settings', array( &$this, 'sanitize_tab_3' ) );
+
+		add_settings_section( 'email-settings-overview', SwpmUtils::_( 'Email Settings Overview' ), array( &$this, 'email_settings_overview_callback' ), 'simple_wp_membership_settings' );
+		add_settings_section( 'email-misc-settings', SwpmUtils::_( 'Email Misc. Settings' ), array( &$this, 'email_misc_settings_callback' ), 'simple_wp_membership_settings' );
+
+		add_settings_field(
+			'email-misc-from',
+			SwpmUtils::_( 'From Email Address' ),
+			array( &$this, 'textfield_callback' ),
+			'simple_wp_membership_settings',
+			'email-misc-settings',
+			array(
+				'item'    => 'email-from',
+				'message' => 'This value will be used as the sender\'s address for the emails. Example value: Your Name &lt;sales@your-domain.com&gt;',
+			)
+		);
+
+		add_settings_field(
+			'email-enable-html',
+			SwpmUtils::_( 'Allow HTML in Emails' ),
+			array( $this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'email-misc-settings',
+			array(
+				'item'    => 'email-enable-html',
+				'message' => 'Enables HTML support in emails. We recommend using plain text (non HTML) email as it has better email delivery rate.',
+			)
+		);
+
+		//Prompt to complete registration email settings
+		add_settings_section( 'reg-prompt-email-settings', SwpmUtils::_( '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',
+			SwpmUtils::_( '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',
+			SwpmUtils::_( 'Email Body' ),
+			array( &$this, 'wp_editor_callback' ),
+			'simple_wp_membership_settings',
+			'reg-prompt-email-settings',
+			array(
+				'item'    => 'reg-prompt-complete-mail-body',
+				'message' => '',
+			)
+		);
+
+		//Registration complete email settings
+		$msg_for_admin_notify_email_field  = SwpmUtils::_( 'Enter the email address where you want the admin notification email to be sent to.' );
+		$msg_for_admin_notify_email_field .= SwpmUtils::_( ' You can put multiple email addresses separated by comma (,) in the above field to send the notification to multiple email addresses.' );
+
+		$msg_for_admin_notify_email_subj = SwpmUtils::_( 'Enter the subject for the admin notification email.' );
+		$admin_notify_email_body_msg     = SwpmUtils::_( 'This email will be sent to the admin when a new user completes the membership registration. Only works if you have enabled the "Send Notification to Admin" option above.' );
+
+		add_settings_section( 'reg-email-settings', SwpmUtils::_( 'Email Settings (Registration Complete)' ), array( &$this, 'reg_email_settings_callback' ), 'simple_wp_membership_settings' );
+		add_settings_field(
+			'reg-complete-mail-subject',
+			SwpmUtils::_( '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',
+			SwpmUtils::_( 'Email Body' ),
+			array( &$this, 'wp_editor_callback' ),
+			'simple_wp_membership_settings',
+			'reg-email-settings',
+			array(
+				'item'    => 'reg-complete-mail-body',
+				'message' => '',
+			)
+		);
+		add_settings_field(
+			'enable-admin-notification-after-reg',
+			SwpmUtils::_( 'Send Notification to Admin' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'reg-email-settings',
+			array(
+				'item'    => 'enable-admin-notification-after-reg',
+				'message' => SwpmUtils::_( 'Enable this option if you want the admin to receive a notification when a member registers.' ),
+			)
+		);
+		add_settings_field(
+			'admin-notification-email',
+			SwpmUtils::_( 'Admin Email Address' ),
+			array( &$this, 'textfield_callback' ),
+			'simple_wp_membership_settings',
+			'reg-email-settings',
+			array(
+				'item'    => 'admin-notification-email',
+				'message' => $msg_for_admin_notify_email_field,
+			)
+		);
+		add_settings_field(
+			'reg-complete-mail-subject-admin',
+			SwpmUtils::_( 'Admin Notification Email Subject' ),
+			array( &$this, 'textfield_callback' ),
+			'simple_wp_membership_settings',
+			'reg-email-settings',
+			array(
+				'item'    => 'reg-complete-mail-subject-admin',
+				'message' => $msg_for_admin_notify_email_subj,
+			)
+		);
+		add_settings_field(
+			'reg-complete-mail-body-admin',
+			SwpmUtils::_( 'Admin Notification Email Body' ),
+			array( &$this, 'wp_editor_callback' ),
+			'simple_wp_membership_settings',
+			'reg-email-settings',
+			array(
+				'item'    => 'reg-complete-mail-body-admin',
+				'message' => $admin_notify_email_body_msg,
+			)
+		);
+
+		add_settings_field(
+			'enable-notification-after-manual-user-add',
+			SwpmUtils::_( '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' => '',
+			)
+		);
+
+		//Password reset email settings
+		add_settings_section( 'reset-password-settings', SwpmUtils::_( 'Email Settings (Password Reset)' ), array( &$this, 'reset_password_settings_callback' ), 'simple_wp_membership_settings' );
+		add_settings_field(
+			'reset-mail-subject',
+			SwpmUtils::_( 'Email Subject' ),
+			array( &$this, 'textfield_callback' ),
+			'simple_wp_membership_settings',
+			'reset-password-settings',
+			array(
+				'item'    => 'reset-mail-subject',
+				'message' => '',
+			)
+		);
+		add_settings_field(
+			'reset-mail-body',
+			SwpmUtils::_( 'Email Body' ),
+			array( &$this, 'wp_editor_callback' ),
+			'simple_wp_membership_settings',
+			'reset-password-settings',
+			array(
+				'item'    => 'reset-mail-body',
+				'message' => '',
+			)
+		);
+
+		//Account upgrade email settings
+		add_settings_section( 'upgrade-email-settings', SwpmUtils::_( ' Email Settings (Account Upgrade Notification)' ), array( &$this, 'upgrade_email_settings_callback' ), 'simple_wp_membership_settings' );
+		add_settings_field(
+			'upgrade-complete-mail-subject',
+			SwpmUtils::_( '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',
+			SwpmUtils::_( 'Email Body' ),
+			array( &$this, 'wp_editor_callback' ),
+			'simple_wp_membership_settings',
+			'upgrade-email-settings',
+			array(
+				'item'    => 'upgrade-complete-mail-body',
+				'message' => '',
+			)
+		);
+		add_settings_field(
+			'disable-email-after-upgrade',
+			SwpmUtils::_( 'Disable Email Notification After Upgrade' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'upgrade-email-settings',
+			array(
+				'item'    => 'disable-email-after-upgrade',
+				'message' => SwpmUtils::_( 'You can use this option to disable the email notification that gets sent to the members when they make a payment for upgrade or renewal.' ),
+			)
+		);
+
+		//Bulk account activate and notify email settings.
+		add_settings_section( 'bulk-activate-email-settings', SwpmUtils::_( ' Email Settings (Bulk Account Activate Notification)' ), array( &$this, 'bulk_activate_email_settings_callback' ), 'simple_wp_membership_settings' );
+		add_settings_field(
+			'bulk-activate-notify-mail-subject',
+			SwpmUtils::_( 'Email Subject' ),
+			array( &$this, 'textfield_callback' ),
+			'simple_wp_membership_settings',
+			'bulk-activate-email-settings',
+			array(
+				'item'    => 'bulk-activate-notify-mail-subject',
+				'message' => '',
+			)
+		);
+		add_settings_field(
+			'bulk-activate-notify-mail-body',
+			SwpmUtils::_( 'Email Body' ),
+			array( &$this, 'wp_editor_callback' ),
+			'simple_wp_membership_settings',
+			'bulk-activate-email-settings',
+			array(
+				'item'    => 'bulk-activate-notify-mail-body',
+				'message' => '',
+			)
+		);
+
+		//Email activation email settings.
+		add_settings_section( 'email-activation-email-settings', SwpmUtils::_( ' Email Settings (Email Activation)' ), array( &$this, 'email_activation_email_settings_callback' ), 'simple_wp_membership_settings' );
+		add_settings_field(
+			'email-activation-mail-subject',
+			SwpmUtils::_( 'Email Subject' ),
+			array( &$this, 'textfield_callback' ),
+			'simple_wp_membership_settings',
+			'email-activation-email-settings',
+			array(
+				'item'    => 'email-activation-mail-subject',
+				'message' => '',
+			)
+		);
+		add_settings_field(
+			'email-activation-mail-body',
+			SwpmUtils::_( 'Email Body' ),
+			array( &$this, 'wp_editor_callback' ),
+			'simple_wp_membership_settings',
+			'email-activation-email-settings',
+			array(
+				'item'    => 'email-activation-mail-body',
+				'message' => '',
+			)
+		);
+	}
+
+	private function tab_4() {
+		//Register settings sections and fileds for the tools tab.
+	}
+
+	private function tab_5() {
+		//Register settings sections and fileds for the advanced settings tab.
+
+		register_setting( 'swpm-settings-tab-5', 'swpm-settings', array( &$this, 'sanitize_tab_5' ) );
+
+		add_settings_section( 'advanced-settings', SwpmUtils::_( 'Advanced Settings' ), array( &$this, 'advanced_settings_callback' ), 'simple_wp_membership_settings' );
+
+		add_settings_field(
+			'enable-expired-account-login',
+			SwpmUtils::_( 'Enable Expired Account Login' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'enable-expired-account-login',
+				'message' => SwpmUtils::_( "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." ),
+			)
+		);
+
+		add_settings_field(
+			'renewal-page-url',
+			SwpmUtils::_( 'Membership Renewal URL' ),
+			array( &$this, 'textfield_long_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'renewal-page-url',
+				'message' => SwpmUtils::_( 'You can create a renewal page for your site. Read <a href="https://simple-membership-plugin.com/creating-membership-renewal-button/" target="_blank">this documentation</a> to learn how to create a renewal page.' ),
+			)
+		);
+
+		add_settings_field(
+			'after-rego-redirect-page-url',
+			SwpmUtils::_( 'After Registration Redirect URL' ),
+			array( &$this, 'textfield_long_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'after-rego-redirect-page-url',
+				'message' => SwpmUtils::_( 'You can enter an URL here to redirect the members to this page after they submit the registration form. Read <a href="https://simple-membership-plugin.com/configure-after-registration-redirect-for-members/" target="_blank">this documentation</a> to learn how to setup after registration redirect.' ),
+			)
+		);
+
+		add_settings_field(
+			'auto-login-after-rego',
+			SwpmUtils::_( 'Enable Auto Login After Registration' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'auto-login-after-rego',
+				'message' => SwpmUtils::_( 'Use this option if you want the members to be automatically logged into your site right after they complete the registration. This option will override any after registration redirection and instead it will trigger the after login redirection. Read <a href="https://simple-membership-plugin.com/configure-auto-login-after-registration-members/" target="_blank">this documentation</a> to learn more.' ),
+			)
+		);
+
+		add_settings_field(
+			'hide-rego-form-to-logged-users',
+			SwpmUtils::_( 'Hide Registration Form to Logged Users' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'hide-rego-form-to-logged-users',
+				'message' => SwpmUtils::_( 'Use this option if you want to hide the registration form to the logged-in members. If logged-in members visit the registration page, they will see a message instead of the registration form.' ),
+			)
+		);
+
+		add_settings_field(
+			'after-logout-redirection-url',
+			SwpmUtils::_( 'After Logout Redirect URL' ),
+			array( &$this, 'textfield_long_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'after-logout-redirection-url',
+				'message' => SwpmUtils::_( 'You can enter an URL here to redirect the members to this page after they click the logout link to logout from your site.' ),
+			)
+		);
+
+		add_settings_field(
+			'logout-member-on-browser-close',
+			SwpmUtils::_( 'Logout Member on Browser Close' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'logout-member-on-browser-close',
+				'message' => SwpmUtils::_( 'Enable this option if you want the member to be logged out of the account when he closes the browser.' ),
+			)
+		);
+
+		add_settings_field(
+			'allow-account-deletion',
+			SwpmUtils::_( 'Allow Account Deletion' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'allow-account-deletion',
+				'message' => SwpmUtils::_( 'Allow users to delete their accounts.' ),
+			)
+		);
+
+		add_settings_field(
+			'force-strong-passwords',
+			SwpmUtils::_( 'Force Strong Password for Members' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'force-strong-passwords',
+				'message' => SwpmUtils::_( 'Enable this if you want the users to be forced to use a strong password for their accounts.' ),
+			)
+		);
+
+		add_settings_field(
+			'delete-pending-account',
+			SwpmUtils::_( 'Auto Delete Pending Account' ),
+			array( &$this, 'selectbox_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'delete-pending-account',
+				'options' => array(
+					0 => 'Do not delete',
+					1 => 'Older than 1 month',
+					2 => 'Older than 2 months',
+				),
+				'default' => '0',
+				'message' => SwpmUtils::_( 'Select how long you want to keep "pending" account.' ),
+			)
+		);
+
+		add_settings_field(
+			'admin-dashboard-access-permission',
+			SwpmUtils::_( 'Admin Dashboard Access Permission' ),
+			array( &$this, 'selectbox_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'admin-dashboard-access-permission',
+				'options' => array(
+					'manage_options'       => translate_user_role( 'Administrator' ),
+					'edit_pages'           => translate_user_role( 'Editor' ),
+					'edit_published_posts' => translate_user_role( 'Author' ),
+					'edit_posts'           => translate_user_role( 'Contributor' ),
+				),
+				'default' => 'manage_options',
+				'message' => SwpmUtils::_( 'SWPM admin dashboard is accessible to admin users only (just like any other plugin). You can allow users with other WP user role to access the SWPM admin dashboard by selecting a value here. Note that this option cannot work if you enabled the "Disable Access to WP Dashboard" option in General Settings.' ),
+			)
+		);
+
+		add_settings_field(
+			'force-wp-user-sync',
+			SwpmUtils::_( 'Force WP User Synchronization' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'force-wp-user-sync',
+				'message' => SwpmUtils::_( 'Enable this option if you want to force the member login to be synchronized with WP user account. This can be useful if you are using another plugin that uses WP user records. For example: bbPress plugin.' ),
+			)
+		);
+
+		//Auto create SWPM user related settings section
+		add_settings_section( 'auto-create-swpm-user-settings', SwpmUtils::_( 'Create Member Accounts for New WP Users' ), array( &$this, 'advanced_settings_auto_create_swpm_uses_settings_callback' ), 'simple_wp_membership_settings' );
+
+		add_settings_field(
+			'enable-auto-create-swpm-members',
+			SwpmUtils::_( 'Enable Auto Create Member Accounts' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'auto-create-swpm-user-settings',
+			array(
+				'item'    => 'enable-auto-create-swpm-members',
+				'message' => SwpmUtils::_( 'Enable this option to automatically create member accounts for any new WP user that is created by another plugin.' ),
+			)
+		);
+
+		$levels_array = SwpmMembershipLevelUtils::get_all_membership_levels_in_array();
+		add_settings_field(
+			'auto-create-default-membership-level',
+			SwpmUtils::_( 'Default Membership Level' ),
+			array( &$this, 'selectbox_callback' ),
+			'simple_wp_membership_settings',
+			'auto-create-swpm-user-settings',
+			array(
+				'item'    => 'auto-create-default-membership-level',
+				'options' => $levels_array,
+				'default' => '',
+				'message' => SwpmUtils::_( 'When automatically creating a member account using this feature, the membership level of the user will be set to the one you specify here.' ),
+			)
+		);
+
+		$status_array = SwpmUtils::get_account_state_options();
+		add_settings_field(
+			'auto-create-default-account-status',
+			SwpmUtils::_( 'Default Account Status' ),
+			array( &$this, 'selectbox_callback' ),
+			'simple_wp_membership_settings',
+			'auto-create-swpm-user-settings',
+			array(
+				'item'    => 'auto-create-default-account-status',
+				'options' => $status_array,
+				'default' => '',
+				'message' => SwpmUtils::_( 'When automatically creating a member account using this feature, the membership account status of the user will be set to the one you specify here.' ),
+			)
+		);
+
+		add_settings_field(
+			'payment-notification-forward-url',
+			SwpmUtils::_( 'Payment Notification Forward URL' ),
+			array( &$this, 'textfield_long_callback' ),
+			'simple_wp_membership_settings',
+			'advanced-settings',
+			array(
+				'item'    => 'payment-notification-forward-url',
+				'message' => SwpmUtils::_( 'You can enter an URL here to forward the payment notification after the membership payment has been processed by this plugin. Useful if you want to forward the payment notification to an external script for further processing.' ),
+			)
+		);
+
+		//Terms and conditions section
+		add_settings_section( 'terms-and-conditions', SwpmUtils::_( 'Terms and Conditions' ), array( &$this, 'advanced_settings_terms_and_conditions_callback' ), 'simple_wp_membership_settings' );
+
+		add_settings_field(
+			'enable-terms-and-conditions',
+			SwpmUtils::_( 'Enable Terms and Conditions' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'terms-and-conditions',
+			array(
+				'item'    => 'enable-terms-and-conditions',
+				'message' => SwpmUtils::_( 'Users must accept the terms before they can complete the registration.' ),
+			)
+		);
+		add_settings_field(
+			'terms-and-conditions-page-url',
+			SwpmUtils::_( 'Terms and Conditions Page URL' ),
+			array( &$this, 'textfield_long_callback' ),
+			'simple_wp_membership_settings',
+			'terms-and-conditions',
+			array(
+				'item'    => 'terms-and-conditions-page-url',
+				'message' => SwpmUtils::_( 'Enter the URL of your terms and conditions page. You can create a WordPress page and specify your terms in there then specify the URL of that page in the above field.' ),
+			)
+		);
+		add_settings_field(
+			'enable-privacy-policy',
+			SwpmUtils::_( 'Enable Privacy Policy' ),
+			array( &$this, 'checkbox_callback' ),
+			'simple_wp_membership_settings',
+			'terms-and-conditions',
+			array(
+				'item'    => 'enable-privacy-policy',
+				'message' => SwpmUtils::_( 'Users must accept it before they can complete the registration.' ),
+			)
+		);
+		add_settings_field(
+			'privacy-policy-page-url',
+			SwpmUtils::_( 'Privacy Policy Page URL' ),
+			array( &$this, 'textfield_long_callback' ),
+			'simple_wp_membership_settings',
+			'terms-and-conditions',
+			array(
+				'item'    => 'privacy-policy-page-url',
+				'message' => SwpmUtils::_( 'Enter the URL of your privacy policy page.' ),
+			)
+		);
+	}
+
+	private function tab_6() {
+		//Register settings sections and fileds for the addon settings tab.
+	}
+
+	public static function get_instance() {
+		self::$_this = empty( self::$_this ) ? new SwpmSettings() : 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 "<select name='swpm-settings[" . $item . "]' >";
+		foreach ( $options as $key => $value ) {
+			$is_selected = ( $key == $selected ) ? 'selected="selected"' : '';
+			echo '<option ' . $is_selected . ' value="' . esc_attr( $key ) . '">' . esc_attr( $value ) . '</option>';
+		}
+		echo '</select>';
+		echo '<br/><i>' . $msg . '</i>';
+	}
+
+	public function checkbox_callback( $args ) {
+		$item = $args['item'];
+		$msg  = isset( $args['message'] ) ? $args['message'] : '';
+		$is   = esc_attr( $this->get_value( $item ) );
+		echo "<input type='checkbox' $is name='swpm-settings[" . $item . "]' value=\"checked='checked'\" />";
+		echo '<br/><i>' . $msg . '</i>';
+	}
+
+	public function textarea_callback( $args ) {
+		$item = $args['item'];
+		$msg  = isset( $args['message'] ) ? $args['message'] : '';
+		$text = esc_attr( $this->get_value( $item ) );
+		echo "<textarea name='swpm-settings[" . $item . "]'  rows='6' cols='60' >" . $text . '</textarea>';
+		echo '<br/><i>' . $msg . '</i>';
+	}
+
+	public function textfield_small_callback( $args ) {
+		$item = $args['item'];
+		$msg  = isset( $args['message'] ) ? $args['message'] : '';
+		$text = esc_attr( $this->get_value( $item ) );
+		echo "<input type='text' name='swpm-settings[" . $item . "]'  size='5' value='" . $text . "' />";
+		echo '<br/><i>' . $msg . '</i>';
+	}
+
+	public function textfield_callback( $args ) {
+		$item = $args['item'];
+		$msg  = isset( $args['message'] ) ? $args['message'] : '';
+		$text = $this->get_value( $item );
+		echo sprintf( '<input type="text" name="swpm-settings[%s]" size="50" value="%s" />', esc_attr( $item ), esc_attr( $text ) );
+		echo sprintf( '<p class="description">%s</p>', $msg );
+	}
+
+	public function textfield_long_callback( $args ) {
+		$item = $args['item'];
+		$msg  = isset( $args['message'] ) ? $args['message'] : '';
+		$text = esc_attr( $this->get_value( $item ) );
+		echo "<input type='text' name='swpm-settings[" . $item . "]'  size='100' value='" . $text . "' />";
+		echo '<br/><i>' . $msg . '</i>';
+	}
+
+	public function set_default_editor( $r ) {
+		$r = 'html';
+		return $r;
+	}
+
+	public function wp_editor_callback( $args ) {
+		$item         = $args['item'];
+		$msg          = isset( $args['message'] ) ? $args['message'] : '';
+		$text         = $this->get_value( $item );
+		$html_enabled = $this->get_value( 'email-enable-html' );
+		add_filter( 'wp_default_editor', array( $this, 'set_default_editor' ) );
+		echo '<style>#wp-' . esc_attr( sprintf( '%s', $item ) ) . '-wrap{max-width:40em;}</style>';
+		wp_editor(
+			html_entity_decode( $text ),
+			$item,
+			array(
+				'textarea_name'  => 'swpm-settings[' . $item . ']',
+				'teeny'          => true,
+				'default_editor' => ! empty( $html_enabled ) ? 'QuickTags' : '',
+				'textarea_rows'  => 15,
+			)
+		);
+		remove_filter( 'wp_default_editor', array( $this, 'set_default_editor' ) );
+		echo "<p class=\"description\">{$msg}</p>";
+	}
+
+	public function swpm_documentation_callback() {
+		?>
+		<div class="swpm-orange-box">
+			<?php printf( SwpmUtils::_( 'Visit the %s to read setup and configuration documentation.' ), '<a target="_blank" href="https://simple-membership-plugin.com/">' . SwpmUtils::_( 'Simple Membership Plugin Site' ) . '</a>' ); ?>
+			<?php printf( SwpmUtils::_( 'Please %s if you like the plugin.' ), '<a href="https://wordpress.org/support/view/plugin-reviews/simple-membership?filter=5" target="_blank">' . SwpmUtils::_( 'give us a rating' ) . '</a>' ); ?>
+		</div>
+		<?php
+	}
+
+	public function swpm_general_post_submit_check_callback() {
+		//Log file reset handler
+		if ( isset( $_REQUEST['swmp_reset_log'] ) ) {
+			if ( SwpmLog::reset_swmp_log_files() ) {
+				echo '<div id="message" class="updated fade"><p>Debug log files have been reset!</p></div>';
+			} else {
+				echo '<div id="message" class="updated fade"><p>Debug log files could not be reset!</p></div>';
+			}
+		}
+
+		//Show settings updated message
+		if ( isset( $_REQUEST['settings-updated'] ) ) {
+			echo '<div id="message" class="updated fade"><p>' . SwpmUtils::_( 'Settings updated!' ) . '</p></div>';
+		}
+	}
+
+	public function general_settings_callback() {
+		SwpmUtils::e( 'General Plugin Settings.' );
+	}
+
+	public function pages_settings_callback() {
+		SwpmUtils::e( 'Page Setup and URL Related settings.' );
+
+		echo '<p>';
+		SwpmUtils::e( 'The following pages are required for the plugin to function correctly. These pages were automatically created by the plugin at install time.' );
+		echo '</p>';
+	}
+
+	public function testndebug_settings_callback() {
+		SwpmUtils::e( 'Testing and Debug Related Settings.' );
+	}
+
+	public function reg_email_settings_callback() {
+		SwpmUtils::e( 'This email will be sent to your users when they complete the registration and become a member.' );
+	}
+
+	public function reset_password_settings_callback() {
+		SwpmUtils::e( 'This email will be sent to your users when they use the password reset functionality.' );
+	}
+
+	public function email_settings_overview_callback() {
+		echo '<div class="swpm-grey-box">';
+		echo '<p>';
+		SwpmUtils::e( 'This interface lets you custsomize the various emails that gets sent to your members for various actions. The default settings should be good to get your started.' );
+		echo '</p>';
+
+		echo '<p>';
+		echo '<a href="https://simple-membership-plugin.com/email-merge-tags-email-shortcodes-for-email-customization/" target="_blank">' . SwpmUtils::_( 'This documentation' ) . '</a>';
+		SwpmUtils::e( ' explains what email merge tags you can use in the email body field to customize it (if you want to).' );
+		echo '</p>';
+		echo '</div>';
+	}
+
+	public function email_misc_settings_callback() {
+
+		//Show settings updated message when it is updated
+		if ( isset( $_REQUEST['settings-updated'] ) ) {
+			//This status message need to be in the callback function to prevent header sent warning
+			echo '<div id="message" class="updated fade"><p>' . SwpmUtils::_( 'Settings updated!' ) . '</p></div>';
+		}
+
+		SwpmUtils::e( 'Settings in this section apply to all emails.' );
+	}
+
+	public function upgrade_email_settings_callback() {
+		SwpmUtils::e( 'This email will be sent to your users after account upgrade (when an existing member pays for a new membership level).' );
+	}
+
+	public function bulk_activate_email_settings_callback() {
+		SwpmUtils::e( 'This email will be sent to your members when you use the bulk account activate and notify action.' );
+		SwpmUtils::e( ' You cannot use email merge tags in this email. You can only use generic text.' );
+	}
+
+	public function email_activation_email_settings_callback() {
+		SwpmUtils::e( 'This email will be sent if Email Activation is enabled for a Membership Level.' );
+	}
+
+	public function reg_prompt_email_settings_callback() {
+		SwpmUtils::e( 'This email will be sent to prompt users to complete registration after the payment.' );
+	}
+
+	public function advanced_settings_callback() {
+
+		//Show settings updated message when it is updated
+		if ( isset( $_REQUEST['settings-updated'] ) ) {
+			//This status message need to be in the callback function to prevent header sent warning
+			echo '<div id="message" class="updated fade"><p>' . SwpmUtils::_( 'Settings updated!' ) . '</p></div>';
+
+                        /* Check if any conflicting setting options have been enabled together. */
+                        $disable_wp_dashboard_for_non_admins = $this->get_value('disable-access-to-wp-dashboard');
+                        if ($disable_wp_dashboard_for_non_admins) {
+                            //The disable wp dashboard option is enabled.
+                            //Check to make sure the "Admin Dashboard Access Permission" option is not being used for other roles.
+                            $admin_dashboard_permission = $this->get_value( 'admin-dashboard-access-permission' );
+                            if ( empty( $admin_dashboard_permission ) || $admin_dashboard_permission == 'manage_options' ) {
+                                //This is fine.
+                            } else {
+                                //Conflicting options enabled.
+                                //Show warning and reset the option value to default.
+                                $this->set_value('admin-dashboard-access-permission', 'manage_options');
+                                $this->save();
+                                echo '<div id="message" class="error"><p>' . SwpmUtils::_( 'Note: You cannot enable both the "Disable Access to WP Dashboard" and "Admin Dashboard Access Permission" options at the same time. Only use one of those options.' ) . '</p></div>';
+                            }
+                        }
+                        /* End of conflicting options check */
+
+		}
+
+		SwpmUtils::e( 'This page allows you to configure some advanced features of the plugin.' );
+	}
+
+	public function advanced_settings_auto_create_swpm_uses_settings_callback() {
+		SwpmUtils::e( 'This section allows you to configure automatic creation of member accounts when new WP User records are created by another plugin. It can be useful if you are using another plugin that creates WP user records and you want them to be recognized in the membership plugin.' );
+	}
+
+	public function advanced_settings_terms_and_conditions_callback() {
+		SwpmUtils::e( 'This section allows you to configure terms and conditions and privacy policy that users must accept at registration time.' );
+	}
+
+	public function sanitize_tab_1( $input ) {
+		if ( empty( $this->settings ) ) {
+			$this->settings = (array) get_option( 'swpm-settings' );
+		}
+		$output = $this->settings;
+		//general settings block
+
+		$output['hide-adminbar']                  = isset( $input['hide-adminbar'] ) ? esc_attr( $input['hide-adminbar'] ) : '';
+		$output['show-adminbar-admin-only']       = isset( $input['show-adminbar-admin-only'] ) ? esc_attr( $input['show-adminbar-admin-only'] ) : '';
+		$output['disable-access-to-wp-dashboard'] = isset( $input['disable-access-to-wp-dashboard'] ) ? esc_attr( $input['disable-access-to-wp-dashboard'] ) : '';
+
+		$output['protect-everything']     = isset( $input['protect-everything'] ) ? esc_attr( $input['protect-everything'] ) : '';
+		$output['enable-free-membership'] = isset( $input['enable-free-membership'] ) ? esc_attr( $input['enable-free-membership'] ) : '';
+		$output['enable-moretag']         = isset( $input['enable-moretag'] ) ? esc_attr( $input['enable-moretag'] ) : '';
+		$output['enable-debug']           = isset( $input['enable-debug'] ) ? esc_attr( $input['enable-debug'] ) : '';
+		$output['enable-sandbox-testing'] = isset( $input['enable-sandbox-testing'] ) ? esc_attr( $input['enable-sandbox-testing'] ) : '';
+
+		$output['free-membership-id']       = ( $input['free-membership-id'] != 1 ) ? absint( $input['free-membership-id'] ) : '';
+		$output['login-page-url']           = esc_url( $input['login-page-url'] );
+		$output['registration-page-url']    = esc_url( $input['registration-page-url'] );
+		$output['profile-page-url']         = esc_url( $input['profile-page-url'] );
+		$output['reset-page-url']           = esc_url( $input['reset-page-url'] );
+		$output['join-us-page-url']         = esc_url( $input['join-us-page-url'] );
+		$output['default-account-status']   = esc_attr( $input['default-account-status'] );
+		$output['members-login-to-comment'] = isset( $input['members-login-to-comment'] ) ? esc_attr( $input['members-login-to-comment'] ) : '';
+
+		return $output;
+	}
+
+	public function sanitize_tab_2( $input ) {
+		if ( empty( $this->settings ) ) {
+			$this->settings = (array) get_option( 'swpm-settings' );
+		}
+		$output = $this->settings;
+
+		$output['stripe-test-public-key'] = sanitize_text_field( $input['stripe-test-public-key'] );
+		$output['stripe-test-secret-key'] = sanitize_text_field( $input['stripe-test-secret-key'] );
+		$output['stripe-live-public-key'] = sanitize_text_field( $input['stripe-live-public-key'] );
+		$output['stripe-live-secret-key'] = sanitize_text_field( $input['stripe-live-secret-key'] );
+
+		$output['stripe-prefill-member-email'] = isset( $input['stripe-prefill-member-email'] ) ? esc_attr( $input['stripe-prefill-member-email'] ) : 0;
+
+		return $output;
+	}
+
+	public function sanitize_tab_3( $input ) {
+		if ( empty( $this->settings ) ) {
+			$this->settings = (array) get_option( 'swpm-settings' );
+		}
+		$output                              = $this->settings;
+		$output['reg-complete-mail-subject'] = sanitize_text_field( $input['reg-complete-mail-subject'] );
+
+		$output['reg-complete-mail-body']          = $input['reg-complete-mail-body'];
+		$output['reg-complete-mail-subject-admin'] = sanitize_text_field( $input['reg-complete-mail-subject-admin'] );
+		$output['reg-complete-mail-body-admin']    = $input['reg-complete-mail-body-admin'];
+
+		$output['reset-mail-subject'] = sanitize_text_field( $input['reset-mail-subject'] );
+		$output['reset-mail-body']    = $input['reset-mail-body'];
+
+		$output['upgrade-complete-mail-subject'] = sanitize_text_field( $input['upgrade-complete-mail-subject'] );
+		$output['upgrade-complete-mail-body']    = $input['upgrade-complete-mail-body'];
+		$output['disable-email-after-upgrade']   = isset( $input['disable-email-after-upgrade'] ) ? esc_attr( $input['disable-email-after-upgrade'] ) : '';
+
+		$output['bulk-activate-notify-mail-subject'] = sanitize_text_field( $input['bulk-activate-notify-mail-subject'] );
+		$output['bulk-activate-notify-mail-body']    = $input['bulk-activate-notify-mail-body'];
+
+		$output['email-activation-mail-subject'] = sanitize_text_field( $input['email-activation-mail-subject'] );
+		$output['email-activation-mail-body']    = $input['email-activation-mail-body'];
+
+		$output['reg-prompt-complete-mail-subject'] = sanitize_text_field( $input['reg-prompt-complete-mail-subject'] );
+		$output['reg-prompt-complete-mail-body']    = $input['reg-prompt-complete-mail-body'];
+		$output['email-from']                       = trim( $input['email-from'] );
+		$output['email-enable-html']                = isset( $input['email-enable-html'] ) ? esc_attr( $input['email-enable-html'] ) : '';
+
+		$output['enable-admin-notification-after-reg']       = isset( $input['enable-admin-notification-after-reg'] ) ? esc_attr( $input['enable-admin-notification-after-reg'] ) : '';
+		$output['admin-notification-email']                  = sanitize_text_field( $input['admin-notification-email'] );
+		$output['enable-notification-after-manual-user-add'] = isset( $input['enable-notification-after-manual-user-add'] ) ? esc_attr( $input['enable-notification-after-manual-user-add'] ) : '';
+
+		return $output;
+	}
+
+	public function sanitize_tab_5( $input ) {
+		if ( empty( $this->settings ) ) {
+			$this->settings = (array) get_option( 'swpm-settings' );
+		}
+		$output                                      = $this->settings;
+		$output['enable-expired-account-login']      = isset( $input['enable-expired-account-login'] ) ? esc_attr( $input['enable-expired-account-login'] ) : '';
+		$output['logout-member-on-browser-close']    = isset( $input['logout-member-on-browser-close'] ) ? esc_attr( $input['logout-member-on-browser-close'] ) : '';
+		$output['allow-account-deletion']            = isset( $input['allow-account-deletion'] ) ? esc_attr( $input['allow-account-deletion'] ) : '';
+		$output['delete-pending-account']            = isset( $input['delete-pending-account'] ) ? esc_attr( $input['delete-pending-account'] ) : 0;
+		$output['admin-dashboard-access-permission'] = isset( $input['admin-dashboard-access-permission'] ) ? esc_attr( $input['admin-dashboard-access-permission'] ) : '';
+		$output['renewal-page-url']                  = esc_url( $input['renewal-page-url'] );
+		$output['after-rego-redirect-page-url']      = esc_url( $input['after-rego-redirect-page-url'] );
+		$output['after-logout-redirection-url']      = esc_url( $input['after-logout-redirection-url'] );
+		$output['force-strong-passwords']            = isset( $input['force-strong-passwords'] ) ? esc_attr( $input['force-strong-passwords'] ) : '';
+		$output['auto-login-after-rego']             = isset( $input['auto-login-after-rego'] ) ? esc_attr( $input['auto-login-after-rego'] ) : '';
+                $output['hide-rego-form-to-logged-users']    = isset( $input['hide-rego-form-to-logged-users'] ) ? esc_attr( $input['hide-rego-form-to-logged-users'] ) : '';
+		$output['force-wp-user-sync']                = isset( $input['force-wp-user-sync'] ) ? esc_attr( $input['force-wp-user-sync'] ) : '';
+		$output['payment-notification-forward-url']  = esc_url( $input['payment-notification-forward-url'] );
+
+		//Auto create swpm user related settings
+		$output['enable-auto-create-swpm-members']      = isset( $input['enable-auto-create-swpm-members'] ) ? esc_attr( $input['enable-auto-create-swpm-members'] ) : '';
+		$output['auto-create-default-membership-level'] = isset( $input['auto-create-default-membership-level'] ) ? esc_attr( $input['auto-create-default-membership-level'] ) : '';
+		$output['auto-create-default-account-status']   = isset( $input['auto-create-default-account-status'] ) ? esc_attr( $input['auto-create-default-account-status'] ) : '';
+		//Terms and conditions related settings
+		$output['enable-terms-and-conditions']   = isset( $input['enable-terms-and-conditions'] ) ? esc_attr( $input['enable-terms-and-conditions'] ) : '';
+		$output['terms-and-conditions-page-url'] = esc_url( $input['terms-and-conditions-page-url'] );
+		$output['enable-privacy-policy']         = isset( $input['enable-privacy-policy'] ) ? esc_attr( $input['enable-privacy-policy'] ) : '';
+		$output['privacy-policy-page-url']       = esc_url( $input['privacy-policy-page-url'] );
+		return $output;
+	}
+
+	public function get_value( $key, $default = '' ) {
+		if ( isset( $this->settings[ $key ] ) ) {
+			return $this->settings[ $key ];
+		}
+		return $default;
+	}
+
+	public function set_value( $key, $value ) {
+		$this->settings[ $key ] = $value;
+		return $this;
+	}
+
+	public function save() {
+		update_option( 'swpm-settings', $this->settings );
+	}
+
+	public function draw_tabs() {
+		$current = $this->current_tab;
+		?>
+		<h2 class="nav-tab-wrapper">
+			<?php foreach ( $this->tabs as $id => $label ) { ?>
+				<a class="nav-tab <?php echo ( $current == $id ) ? 'nav-tab-active' : ''; ?>" href="admin.php?page=simple_wp_membership_settings&tab=<?php echo $id; ?>"><?php echo $label; ?></a>
+			<?php } ?>
+		</h2>
+		<?php
+	}
+
+	public function handle_main_settings_admin_menu() {
+		do_action( 'swpm_settings_menu_start' );
+
+		//Check current_user_can() or die.
+		SwpmMiscUtils::check_user_permission_and_is_admin( 'Main Settings Menu' );
+
+		?>
+		<div class="wrap swpm-admin-menu-wrap"><!-- start wrap -->
+
+			<h1><?php echo SwpmUtils::_( 'Simple WP Membership::Settings' ); ?></h1><!-- page title -->
+
+			<!-- start nav menu tabs -->
+			<?php do_action( 'swpm-draw-settings-nav-tabs' ); ?>
+			<!-- end nav menu tabs -->
+			<?php
+			do_action( 'swpm_settings_menu_after_nav_tabs' );
+
+			//Switch to handle the body of each of the various settings pages based on the currently selected tab
+			$current_tab = $this->current_tab;
+			switch ( $current_tab ) {
+				case 1:
+					//General settings
+					include SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_settings.php';
+					break;
+				case 2:
+					//Payment settings
+					include SIMPLE_WP_MEMBERSHIP_PATH . 'views/payments/admin_payment_settings.php';
+					break;
+				case 3:
+					//Email settings
+					include SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_settings.php';
+					break;
+				case 4:
+					//Tools
+					include SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_tools_settings.php';
+					break;
+				case 5:
+					//Advanced settings
+					include SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_settings.php';
+					break;
+				case 6:
+					//Addon settings
+					include SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_addon_settings.php';
+					break;
+				default:
+					//The default fallback (general settings)
+					include SIMPLE_WP_MEMBERSHIP_PATH . 'views/admin_settings.php';
+					break;
+			}
+
+			echo '</div>'; //<!-- end of wrap -->
+	}
+
+}
+

+ 87 - 0
simple-membership/classes/class.swpm-transactions.php

@@ -0,0 +1,87 @@
+<?php
+
+/*
+ * Provides some helpful functions to deal with the transactions
+ */
+
+class SwpmTransactions {
+
+	static function save_txn_record( $ipn_data, $items = array() ) {
+		global $wpdb;
+
+		$current_date = SwpmUtils::get_current_date_in_wp_zone();//date( 'Y-m-d' );
+		$custom_var   = self::parse_custom_var( $ipn_data['custom'] );
+
+		$txn_data                     = array();
+		$txn_data['email']            = $ipn_data['payer_email'];
+		$txn_data['first_name']       = $ipn_data['first_name'];
+		$txn_data['last_name']        = $ipn_data['last_name'];
+		$txn_data['ip_address']       = $ipn_data['ip'];
+		$txn_data['member_id']        = isset ( $custom_var['swpm_id'] ) ? $custom_var['swpm_id'] : '';
+		$txn_data['membership_level'] = isset ( $custom_var['subsc_ref'] ) ? $custom_var['subsc_ref'] : '';
+
+		$txn_data['txn_date']       = $current_date;
+		$txn_data['txn_id']         = $ipn_data['txn_id'];
+		$txn_data['subscr_id']      = $ipn_data['subscr_id'];
+		$txn_data['reference']      = isset( $custom_var['reference'] ) ? $custom_var['reference'] : '';
+		$txn_data['payment_amount'] = $ipn_data['mc_gross'];
+		$txn_data['gateway']        = $ipn_data['gateway'];
+		$txn_data['status']         = $ipn_data['status'];
+
+		$txn_data = array_filter( $txn_data );//Remove any null values.
+		$wpdb->insert( $wpdb->prefix . 'swpm_payments_tbl', $txn_data );
+
+		$db_row_id = $wpdb->insert_id;
+
+		//let's also store transactions data in swpm_transactions CPT
+		$post                = array();
+		$post['post_title']  = '';
+		$post['post_status'] = 'publish';
+		$post['content']     = '';
+		$post['post_type']   = 'swpm_transactions';
+
+		$post_id = wp_insert_post( $post );
+
+		update_post_meta( $post_id, 'db_row_id', $db_row_id );
+
+		if ( isset( $ipn_data['payment_button_id'] ) ) {
+			$txn_data['payment_button_id'] = $ipn_data['payment_button_id'];
+		}
+
+		if ( isset( $ipn_data['is_live'] ) ) {
+			$txn_data['is_live'] = $ipn_data['is_live'];
+		}
+
+		foreach ( $txn_data as $key => $value ) {
+			update_post_meta( $post_id, $key, $value );
+		}
+
+		do_action( 'swpm_txn_record_saved', $txn_data, $db_row_id, $post_id );
+
+	}
+
+	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;
+	}
+
+        static function get_transaction_row_by_subscr_id ($subscr_id) {
+                global $wpdb;
+                $query_db = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}swpm_payments_tbl WHERE subscr_id = %s", $subscr_id ), OBJECT );
+                return $query_db;
+        }
+}

+ 47 - 0
simple-membership/classes/class.swpm-transfer.php

@@ -0,0 +1,47 @@
+<?php
+
+class SwpmTransfer {
+
+    public static $default_fields = array(
+        'first_name' => '', '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' => SwpmMembershipLevel::NO_EXPIRY);
+    
+    public static $admin_messages = array();
+    private static $_this;
+
+    private function __construct() {
+        $this->message = get_option('swpm-messages');
+    }
+
+    public static function get_instance() {
+        self::$_this = empty(self::$_this) ? new SwpmTransfer() : self::$_this;
+        return self::$_this;
+    }
+
+    public function get($key) {
+        $messages = new SwpmMessages();
+        return $messages->get($key);
+    }
+
+    public function set($key, $value) {
+        $messages = new SwpmMessages();
+        $messages->set($key, $value);
+    }
+    
+    /*** Deprecated function - exists only for backwards compatibility ***/
+    public static function get_real_ip_addr() {
+        return SwpmUtils::get_user_ip_address();
+    }
+}

+ 295 - 0
simple-membership/classes/class.swpm-utils-member.php

@@ -0,0 +1,295 @@
+<?php
+
+/**
+ * SwpmMemberUtils
+ * All the utility functions related to member records should be added to this class
+ */
+class SwpmMemberUtils {
+
+	public static function create_swpm_member_entry_from_array_data( $fields ) {
+		global $wpdb;
+		$res = $wpdb->insert( $wpdb->prefix . 'swpm_members_tbl', $fields );
+
+		if ( ! $res ) {
+			//DB error occurred
+			$error_msg = 'create_swpm_member_entry_from_array_data() - DB error occurred: ' . json_encode( $wpdb->last_result );
+			SwpmLog::log_simple_debug( $error_msg, false );
+		}
+
+		$member_id = $wpdb->insert_id;
+		SwpmLog::log_simple_debug( 'create_swpm_member_entry_from_array_data() - SWPM member entry created successfully. Member ID: ' . $member_id, true );
+		return $member_id;
+	}
+
+	public static function is_member_logged_in() {
+		$auth = SwpmAuth::get_instance();
+		if ( $auth->is_logged_in() ) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	public static function get_logged_in_members_id() {
+		$auth = SwpmAuth::get_instance();
+		if ( ! $auth->is_logged_in() ) {
+			return SwpmUtils::_( 'User is not logged in.' );
+		}
+		return $auth->get( 'member_id' );
+	}
+
+	public static function get_logged_in_members_username() {
+		$auth = SwpmAuth::get_instance();
+		if ( ! $auth->is_logged_in() ) {
+			return SwpmUtils::_( 'User is not logged in.' );
+		}
+		return $auth->get( 'user_name' );
+	}
+
+	public static function get_logged_in_members_level() {
+		$auth = SwpmAuth::get_instance();
+		if ( ! $auth->is_logged_in() ) {
+			return SwpmUtils::_( 'User is not logged in.' );
+		}
+		return $auth->get( 'membership_level' );
+	}
+
+	public static function get_logged_in_members_level_name() {
+		$auth = SwpmAuth::get_instance();
+		if ( $auth->is_logged_in() ) {
+			return $auth->get( 'alias' );
+		}
+		return SwpmUtils::_( 'User is not logged in.' );
+	}
+
+	public static function get_logged_in_members_email() {
+		$auth = SwpmAuth::get_instance();
+		if ( ! $auth->is_logged_in() ) {
+			return SwpmUtils::_( 'User is not logged in.' );
+		}
+		return $auth->get( 'email' );
+	}
+
+	public static function get_member_field_by_id( $id, $field, $default = '' ) {
+		global $wpdb;
+		$query    = 'SELECT * FROM ' . $wpdb->prefix . 'swpm_members_tbl WHERE member_id = %d';
+		$userData = $wpdb->get_row( $wpdb->prepare( $query, $id ) );
+		if ( isset( $userData->$field ) ) {
+			return $userData->$field;
+		}
+
+		return apply_filters( 'swpm_get_member_field_by_id', $default, $id, $field );
+	}
+
+	public static function get_formatted_expiry_date_by_user_id( $swpm_id ) {
+		$expiry_timestamp = self::get_expiry_date_timestamp_by_user_id( $swpm_id );
+		if ( $expiry_timestamp == PHP_INT_MAX ) {
+			//No Expiry Setting
+			$formatted_expiry_date = SwpmUtils::_( 'No Expiry' );
+		} else {
+			$expiry_date           = date( 'Y-m-d', $expiry_timestamp );
+			$formatted_expiry_date = SwpmUtils::get_formatted_date_according_to_wp_settings( $expiry_date );
+		}
+		return $formatted_expiry_date;
+	}
+
+	public static function get_expiry_date_timestamp_by_user_id( $swpm_id ) {
+		$swpm_user        = self::get_user_by_id( $swpm_id );
+		$expiry_timestamp = SwpmUtils::get_expiration_timestamp( $swpm_user );
+		return $expiry_timestamp;
+	}
+
+	public static function get_user_by_id( $swpm_id ) {
+		//Retrieves the SWPM user record for the given member ID
+		global $wpdb;
+		$query  = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}swpm_members_tbl WHERE member_id = %d", $swpm_id );
+		$result = $wpdb->get_row( $query );
+		return $result;
+	}
+
+	public static function get_user_by_user_name( $swpm_user_name ) {
+		//Retrieves the SWPM user record for the given member username
+		global $wpdb;
+		$query  = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}swpm_members_tbl WHERE user_name = %s", $swpm_user_name );
+		$result = $wpdb->get_row( $query );
+		return $result;
+	}
+
+	public static function get_user_by_email( $swpm_email ) {
+		//Retrieves the SWPM user record for the given member email address
+		global $wpdb;
+		$query  = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}swpm_members_tbl WHERE email = %s", $swpm_email );
+		$result = $wpdb->get_row( $query );
+		return $result;
+	}
+
+	public static function get_user_by_subsriber_id( $subsc_id ) {
+		//Retrieves the SWPM user record for the given member ID
+		global $wpdb;
+		$query  = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}swpm_members_tbl WHERE subscr_id = %s", $subsc_id );
+		$result = $wpdb->get_row( $query );
+		return $result;
+	}
+
+	public static function get_wp_user_from_swpm_user_id( $swpm_id ) {
+		//Retrieves the WP user record for the given SWPM member ID.
+		$swpm_user_row = self::get_user_by_id( $swpm_id );
+		$username      = $swpm_user_row->user_name;
+		$wp_user       = get_user_by( 'login', $username );
+		return $wp_user;
+	}
+
+	public static function get_all_members_of_a_level( $level_id ) {
+		//Retrieves all the SWPM user records for the given membership level
+		global $wpdb;
+		$query  = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}swpm_members_tbl WHERE membership_level = %s", $level_id );
+		$result = $wpdb->get_results( $query );
+		return $result;
+	}
+
+	/*
+	 * Use this function to update or set membership level of a member easily.
+	 */
+	public static function update_membership_level( $member_id, $target_membership_level ) {
+		global $wpdb;
+		$members_table_name = $wpdb->prefix . 'swpm_members_tbl';
+		$query              = $wpdb->prepare( "UPDATE $members_table_name SET membership_level=%s WHERE member_id=%s", $target_membership_level, $member_id );
+		$resultset          = $wpdb->query( $query );
+	}
+
+	/*
+	 * Use this function to update or set account status of a member easily.
+	 */
+	public static function update_account_state( $member_id, $new_status = 'active' ) {
+		global $wpdb;
+		$members_table_name = $wpdb->prefix . 'swpm_members_tbl';
+
+		SwpmLog::log_simple_debug( 'Updating the account status value of member (' . $member_id . ') to: ' . $new_status, true );
+		$query     = $wpdb->prepare( "UPDATE $members_table_name SET account_state=%s WHERE member_id=%s", $new_status, $member_id );
+		$resultset = $wpdb->query( $query );
+	}
+
+	/*
+	 * Use this function to update or set access starts date of a member easily.
+	 */
+	public static function update_access_starts_date( $member_id, $new_date ) {
+		global $wpdb;
+		$members_table_name = $wpdb->prefix . 'swpm_members_tbl';
+		$query              = $wpdb->prepare( "UPDATE $members_table_name SET subscription_starts=%s WHERE member_id=%s", $new_date, $member_id );
+		$resultset          = $wpdb->query( $query );
+	}
+
+	/*
+	 * Calculates the Access Starts date value considering the level and current expiry. Useful for after payment member profile update.
+	 */
+	public static function calculate_access_start_date_for_account_update( $args ) {
+		$swpm_id              = $args['swpm_id'];
+		$membership_level     = $args['membership_level'];
+		$old_membership_level = $args['old_membership_level'];
+
+		$subscription_starts = SwpmUtils::get_current_date_in_wp_zone();//( date( 'Y-m-d' ) );
+		if ( $membership_level == $old_membership_level ) {
+			//Payment for the same membership level (renewal).
+
+			//Algorithm - ONLY set the $subscription_starts date to current expiry date if the current expiry date is in the future.
+			//Otherwise set $subscription_starts to TODAY.
+			$expiry_timestamp = self::get_expiry_date_timestamp_by_user_id( $swpm_id );
+			if ( $expiry_timestamp > time() ) {
+				//Account is not expired. Expiry date is in the future.
+				$level_row          = SwpmUtils::get_membership_level_row_by_id( $membership_level );
+				$subs_duration_type = $level_row->subscription_duration_type;
+				if ( $subs_duration_type == SwpmMembershipLevel::NO_EXPIRY ) {
+					//No expiry type level.
+					//Use todays date for $subscription_starts date parameter.
+				} elseif ( $subs_duration_type == SwpmMembershipLevel::FIXED_DATE ) {
+					//Fixed date expiry level.
+					//Use todays date for $subscription_starts date parameter.
+				} else {
+					//Duration expiry level.
+					//Set the $subscription_starts date to the current expiry date so the renewal time starts from then.
+					$subscription_starts = date( 'Y-m-d', $expiry_timestamp );
+				}
+			} else {
+				//Account is already expired.
+				//Use todays date for $subscription_starts date parameter.
+			}
+		} else {
+			//Payment for a NEW membership level (upgrade).
+			//Use todays date for $subscription_starts date parameter.
+		}
+
+		return $subscription_starts;
+	}
+
+	public static function is_valid_user_name( $user_name ) {
+		return preg_match( '/^[a-zA-Z0-9.\-_*@]+$/', $user_name ) == 1;
+	}
+
+        public static function check_and_die_if_email_belongs_to_admin_user( $email_to_check ){
+		//Check if the email belongs to an existing wp user account.
+		$wp_user_id = email_exists( $email_to_check );
+		if ( $wp_user_id ) {
+                    //A wp user account exist with this email.
+                    //Check if the user has admin role.
+                    $admin_user = SwpmMemberUtils::wp_user_has_admin_role( $wp_user_id );
+                    if ( $admin_user ) {
+                        //This email belongs to an admin user. Cannot use/register using an admin user's email from front-end. Show error message then exit.
+                        $error_msg = '<p>This email address (' . $email_to_check . ') belongs to an admin user. This email cannot be used to register a new account on this site for security reasons. Contact site admin.</p>';
+                        $error_msg .= '<p>For testing purpose, you can create another user account that is completely separate from the admin user account of this site.</p>';
+                        wp_die( $error_msg );
+                    }
+		}
+        }
+
+        public static function check_and_die_if_username_belongs_to_admin_user( $username_to_check ){
+                //Check if the username belongs to an existing wp user account.
+                $wp_user_id = username_exists( $username_to_check );
+                if ( $wp_user_id ) {
+                    //A wp user account exists with this username.
+                    //Check if the user has admin role.
+                    $admin_user = SwpmMemberUtils::wp_user_has_admin_role( $wp_user_id );
+                    if ( $admin_user ) {
+                        //This Username belongs to an admin user. Cannot use/register using an existing admin user's username from front-end. Show error message then exit.
+                        $error_msg = '<p>This username (' . $username_to_check . ') belongs to an admin user. It cannot be used to register a new account on this site for security reasons. Contact site admin.</p>';
+                        $error_msg .= '<p>For testing purpose, you can create another user account that is completely separate from the admin user account of this site.</p>';
+                        wp_die( $error_msg );
+                    }
+                }
+        }
+
+	public static function wp_user_has_admin_role( $wp_user_id ) {
+		$caps = get_user_meta( $wp_user_id, 'wp_capabilities', true );
+		if ( is_array( $caps ) && in_array( 'administrator', array_keys( (array) $caps ) ) ) {
+			//This wp user has "administrator" role.
+			return true;
+		}
+		return false;
+	}
+
+	public static function update_wp_user_role_with_level_id( $wp_user_id, $level_id ) {
+		$level_row = SwpmUtils::get_membership_level_row_by_id( $level_id );
+		$user_role = $level_row->role;
+		self::update_wp_user_role( $wp_user_id, $user_role );
+	}
+
+	public static function update_wp_user_role( $wp_user_id, $role ) {
+		if ( SwpmUtils::is_multisite_install() ) {//MS install
+			return; //TODO - don't do this for MS install
+		}
+
+		$admin_user = self::wp_user_has_admin_role( $wp_user_id );
+		if ( $admin_user ) {
+			SwpmLog::log_simple_debug( 'This user has admin role. No role modification will be done.', true );
+			return;
+		}
+
+		//wp_update_user() function will trigger the 'set_user_role' hook.
+		wp_update_user(
+			array(
+				'ID'   => $wp_user_id,
+				'role' => $role,
+			)
+		);
+		SwpmLog::log_simple_debug( 'User role updated.', true );
+	}
+}

+ 39 - 0
simple-membership/classes/class.swpm-utils-membership-level.php

@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * This class will contain various utility functions for the membership access level.
+ */
+
+class SwpmMembershipLevelUtils {
+
+    public static function get_membership_level_name_of_a_member($member_id){
+        $user_row = SwpmMemberUtils::get_user_by_id($member_id);
+        $level_id = $user_row->membership_level;
+        
+        $level_row = SwpmUtils::get_membership_level_row_by_id($level_id);
+        $level_name = $level_row->alias;
+        return $level_name;
+    }
+    
+    public static function get_membership_level_name_by_level_id($level_id){        
+        $level_row = SwpmUtils::get_membership_level_row_by_id($level_id);
+        $level_name = $level_row->alias;
+        return $level_name;
+    }
+    
+    public static function get_all_membership_levels_in_array(){
+        //Creates an array like the following with all the available levels.
+        //Array ( [2] => Free Level, [3] => Silver Level, [4] => Gold Level )
+        
+        global $wpdb;
+        $levels_array = array();
+        $query = "SELECT alias, id FROM " . $wpdb->prefix . "swpm_membership_tbl WHERE id != 1";
+        $levels = $wpdb->get_results($query);
+        foreach ($levels as $level) {
+            if(isset($level->id)){
+                $levels_array[$level->id] = $level->alias;
+            }
+        }
+        return $levels_array; 
+    }
+}

+ 791 - 0
simple-membership/classes/class.swpm-utils-misc.php

@@ -0,0 +1,791 @@
+<?php
+
+class SwpmMiscUtils {
+
+	public static $stripe_sca_frontend_scripts_printed = false;
+
+	public static function create_mandatory_wp_pages() {
+		$settings = SwpmSettings::get_instance();
+
+		//Create join us page
+		$swpm_join_page_content  = '<p style="color:red;font-weight:bold;">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.</p>';
+		$swpm_join_page_content .= '<p style="font-weight:bold;">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.</p>';
+		$swpm_join_page_content .= '<p style="border-top:1px solid #ccc;padding-top:10px;margin-top:10px;"></p>
+			<strong>Free Membership</strong>
+			<br />
+			You get unlimited access to free membership content
+			<br />
+			<em><strong>Price: Free!</strong></em>
+			<br /><br />Link the following image to go to the Registration Page if you want your visitors to be able to create a free membership account<br /><br />
+			<img title="Join Now" src="' . SIMPLE_WP_MEMBERSHIP_URL . '/images/join-now-button-image.gif" alt="Join Now Button" width="277" height="82" />
+			<p style="border-bottom:1px solid #ccc;padding-bottom:10px;margin-bottom:10px;"></p>';
+		$swpm_join_page_content .= '<p><strong>You can register for a Free Membership or pay for one of the following membership options</strong></p>';
+		$swpm_join_page_content .= '<p style="border-top:1px solid #ccc;padding-top:10px;margin-top:10px;"></p>
+			[ ==> Insert Payment Button For Your Paid Membership Levels Here <== ]
+			<p style="border-bottom:1px solid #ccc;padding-bottom:10px;margin-bottom:10px;"></p>';
+
+		$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'     => SwpmUtils::_( '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'     => SwpmUtils::_( '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'     => SwpmUtils::_( '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'     => SwpmUtils::_( '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 redirect_to_url( $url ) {
+		if ( empty( $url ) ) {
+			return;
+		}
+		$url = apply_filters( 'swpm_redirect_to_url', $url );
+
+		if ( ! preg_match( '/http/', $url ) ) {//URL value is incorrect
+			echo '<p>Error! The URL value you entered in the plugin configuration is incorrect.</p>';
+			echo '<p>A URL must always have the "http" keyword in it.</p>';
+			echo '<p style="font-weight: bold;">The URL value you currently configured is: <br />' . $url . '</p>';
+			echo '<p>Here are some examples of correctly formatted URL values for your reference: <br />http://www.example.com<br/>http://example.com<br />https://www.example.com</p>';
+			echo '<p>Find the field where you entered this incorrect URL value and correct the mistake then try again.</p>';
+			exit;
+		}
+		if ( ! headers_sent() ) {
+			header( 'Location: ' . $url );
+		} else {
+			echo '<meta http-equiv="refresh" content="0;url=' . $url . '" />';
+		}
+		exit;
+	}
+
+	public static function show_temporary_message_then_redirect( $msg, $redirect_url, $timeout = 5 ) {
+		$timeout       = absint( $timeout );
+		$redirect_html = sprintf( '<meta http-equiv="refresh" content="%d; url=\'%s\'" />', $timeout, $redirect_url );
+		$redir_msg     = SwpmUtils::_( 'You will be automatically redirected in a few seconds. If not, please %s.' );
+		$redir_msg     = sprintf( $redir_msg, '<a href="' . $redirect_url . '">' . SwpmUtils::_( 'click here' ) . '</a>' );
+
+		$msg   = $msg . '<br/><br/>' . $redir_msg . $redirect_html;
+		$title = SwpmUtils::_( 'Action Status' );
+		wp_die( $msg, $title );
+	}
+
+	public static function get_current_page_url() {
+		$pageURL = 'http';
+
+                if ( isset( $_SERVER['SCRIPT_URI'] ) && ! empty( $_SERVER['SCRIPT_URI'] ) ) {
+			$pageURL = $_SERVER['SCRIPT_URI'];
+                        $pageURL = str_replace(':443', '', $pageURL);//remove any port number from the URL value (some hosts include the port number with this).
+			$pageURL = apply_filters( 'swpm_get_current_page_url_filter', $pageURL );
+			return $pageURL;
+		}
+
+		if ( isset( $_SERVER['HTTPS'] ) && ( $_SERVER['HTTPS'] == 'on' ) ) {
+			$pageURL .= 's';
+		}
+		$pageURL .= '://';
+		if ( isset( $_SERVER['SERVER_PORT'] ) && ( $_SERVER['SERVER_PORT'] != '80' ) && ( $_SERVER['SERVER_PORT'] != '443' ) ) {
+			$pageURL .= ltrim( $_SERVER['SERVER_NAME'], '.*' ) . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI'];
+		} else {
+			$pageURL .= ltrim( $_SERVER['SERVER_NAME'], '.*' ) . $_SERVER['REQUEST_URI'];
+		}
+
+		$pageURL = apply_filters( 'swpm_get_current_page_url_filter', $pageURL );
+
+		return $pageURL;
+	}
+
+	/*
+	 * This is an alternative to the get_current_page_url() function. It needs to be tested on many different server conditions before it can be utilized
+	 */
+	public static function get_current_page_url_alt() {
+		$url_parts          = array();
+		$url_parts['proto'] = 'http';
+
+		if ( isset( $_SERVER['SCRIPT_URI'] ) && ! empty( $_SERVER['SCRIPT_URI'] ) ) {
+			return $_SERVER['SCRIPT_URI'];
+		}
+
+		if ( isset( $_SERVER['HTTPS'] ) && ( $_SERVER['HTTPS'] == 'on' ) ) {
+			$url_parts['proto'] = 'https';
+		}
+
+		$url_parts['port'] = '';
+		if ( isset( $_SERVER['SERVER_PORT'] ) && ( $_SERVER['SERVER_PORT'] != '80' ) && ( $_SERVER['SERVER_PORT'] != '443' ) ) {
+			$url_parts['port'] = $_SERVER['SERVER_PORT'];
+		}
+
+		$url_parts['domain'] = ltrim( $_SERVER['SERVER_NAME'], '.*' );
+		$url_parts['uri']    = $_SERVER['REQUEST_URI'];
+
+		$url_parts = apply_filters( 'swpm_get_current_page_url_alt_filter', $url_parts );
+
+		$pageURL = sprintf( '%s://%s%s%s', $url_parts['proto'], $url_parts['domain'], ! empty( $url_parts['port'] ) ? ':' . $url_parts['port'] : '', $url_parts['uri'] );
+
+		return $pageURL;
+	}
+
+	/*
+	 * Returns just the domain name. Something like example.com
+	 */
+
+	public static function get_home_url_without_http_and_www() {
+		$site_url = get_site_url();
+		$parse    = parse_url( $site_url );
+		$site_url = $parse['host'];
+		$site_url = str_replace( 'https://', '', $site_url );
+		$site_url = str_replace( 'http://', '', $site_url );
+		if ( preg_match( '/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $site_url, $regs ) ) {
+			$site_url = $regs['domain'];
+		}
+		return $site_url;
+	}
+
+	public static function replace_dynamic_tags( $msg_body, $member_id, $additional_args = '' ) {
+		$settings    = SwpmSettings::get_instance();
+		$user_record = SwpmMemberUtils::get_user_by_id( $member_id );
+
+		$password = '';
+		$reg_link = '';
+		if ( ! empty( $additional_args ) ) {
+			$password = isset( $additional_args['password'] ) ? $additional_args['password'] : $password;
+			$reg_link = isset( $additional_args['reg_link'] ) ? $additional_args['reg_link'] : $reg_link;
+		}
+		$login_link = $settings->get_value( 'login-page-url' );
+
+		//Construct the primary address value
+		$primary_address = '';
+		if ( ! empty( $user_record->address_street ) && ! empty( $user_record->address_city ) ) {
+			//An address value is present.
+			$primary_address .= $user_record->address_street;
+			$primary_address .= "\n" . $user_record->address_city;
+			if ( ! empty( $user_record->address_state ) ) {
+				$primary_address .= ' ' . $user_record->address_state;
+			}
+			if ( ! empty( $user_record->address_zipcode ) ) {
+				$primary_address .= ' ' . $user_record->address_zipcode;
+			}
+			if ( ! empty( $user_record->country ) ) {
+				$primary_address .= "\n" . $user_record->country;
+			}
+		}
+
+		$membership_level_name = SwpmMembershipLevelUtils::get_membership_level_name_of_a_member( $member_id );
+		//Format some field values
+		$member_since_formatted = SwpmUtils::get_formatted_date_according_to_wp_settings( $user_record->member_since );
+		$subsc_starts_formatted = SwpmUtils::get_formatted_date_according_to_wp_settings( $user_record->subscription_starts );
+
+		//Define the replacable tags
+		$tags = array(
+			'{member_id}',
+			'{user_name}',
+			'{first_name}',
+			'{last_name}',
+			'{membership_level}',
+			'{membership_level_name}',
+			'{account_state}',
+			'{email}',
+			'{phone}',
+			'{member_since}',
+			'{subscription_starts}',
+			'{company_name}',
+			'{password}',
+			'{login_link}',
+			'{reg_link}',
+			'{primary_address}',
+		);
+
+		//Define the values
+		$vals = array(
+			$member_id,
+			$user_record->user_name,
+			$user_record->first_name,
+			$user_record->last_name,
+			$user_record->membership_level,
+			$membership_level_name,
+			$user_record->account_state,
+			$user_record->email,
+			$user_record->phone,
+			$member_since_formatted,
+			$subsc_starts_formatted,
+			$user_record->company_name,
+			$password,
+			$login_link,
+			$reg_link,
+			$primary_address,
+		);
+
+		$msg_body = str_replace( $tags, $vals, $msg_body );
+		return $msg_body;
+	}
+
+	public static function get_login_link() {
+		$login_url  = SwpmSettings::get_instance()->get_value( 'login-page-url' );
+		$joinus_url = SwpmSettings::get_instance()->get_value( 'join-us-page-url' );
+		if ( empty( $login_url ) || empty( $joinus_url ) ) {
+			return '<span style="color:red;">Simple Membership is not configured correctly. The login page or the join us page URL is missing in the settings configuration. '
+					. 'Please contact <a href="mailto:' . get_option( 'admin_email' ) . '">Admin</a>';
+		}
+
+		//Create the login/protection message
+		$filtered_login_url = apply_filters( 'swpm_get_login_link_url', $login_url ); //Addons can override the login URL value using this filter.
+		$login_msg          = '';
+		$login_msg         .= SwpmUtils::_( 'Please' ) . ' <a class="swpm-login-link" href="' . $filtered_login_url . '">' . SwpmUtils::_( 'Login' ) . '</a>. ';
+		$login_msg         .= SwpmUtils::_( 'Not a Member?' ) . ' <a href="' . $joinus_url . '">' . SwpmUtils::_( 'Join Us' ) . '</a>';
+
+		return $login_msg;
+	}
+
+	public static function get_renewal_link() {
+		$renewal = SwpmSettings::get_instance()->get_value( 'renewal-page-url' );
+		if ( empty( $renewal ) ) {
+			//No renewal page is configured so don't show any renewal page link. It is okay to have no renewal page configured.
+			return '';
+		}
+		return SwpmUtils::_( 'Please' ) . ' <a class="swpm-renewal-link" href="' . $renewal . '">' . SwpmUtils::_( 'renew' ) . '</a> ' . SwpmUtils::_( ' your account to gain access to this content.' );
+	}
+
+	public static function compare_url( $url1, $url2 ) {
+		$url1 = trailingslashit( strtolower( $url1 ) );
+		$url2 = trailingslashit( strtolower( $url2 ) );
+		if ( $url1 == $url2 ) {
+			return true;
+		}
+
+		$url1 = parse_url( $url1 );
+		$url2 = parse_url( $url2 );
+
+		$components = array( 'scheme', 'host', 'port', 'path' );
+
+		foreach ( $components as $key => $value ) {
+			if ( ! isset( $url1[ $value ] ) && ! isset( $url2[ $value ] ) ) {
+				continue;
+			}
+
+			if ( ! isset( $url2[ $value ] ) ) {
+				return false;
+			}
+			if ( ! isset( $url1[ $value ] ) ) {
+				return false;
+			}
+
+			if ( $url1[ $value ] != $url2[ $value ] ) {
+				return false;
+			}
+		}
+
+		if ( ! isset( $url1['query'] ) && ! isset( $url2['query'] ) ) {
+			return true;
+		}
+
+		if ( ! isset( $url2['query'] ) ) {
+			return false;
+		}
+		if ( ! isset( $url1['query'] ) ) {
+			return false;
+		}
+
+		return strpos( $url1['query'], $url2['query'] ) || strpos( $url2['query'], $url1['query'] );
+	}
+
+	public static function is_swpm_admin_page() {
+		if ( isset( $_GET['page'] ) && ( stripos( $_GET['page'], 'simple_wp_membership' ) !== false ) ) {
+			//This is an admin page of the SWPM plugin
+			return true;
+		}
+		return false;
+	}
+
+	public static function check_user_permission_and_is_admin( $action_name ) {
+		//Check we are on the admin end
+		if ( ! is_admin() ) {
+			//Error! This is not on the admin end. This can only be done from the admin side
+			wp_die( SwpmUtils::_( 'Error! This action (' . $action_name . ') can only be done from admin end.' ) );
+		}
+
+		//Check user has management permission
+		if ( ! current_user_can( SWPM_MANAGEMENT_PERMISSION ) ) {
+			//Error! Only management users can do this
+			wp_die( SwpmUtils::_( 'Error! This action (' . $action_name . ') can only be done by an user with management permission.' ) );
+		}
+	}
+
+	public static function format_raw_content_for_front_end_display( $raw_content ) {
+		$formatted_content = wptexturize( $raw_content );
+		$formatted_content = convert_smilies( $formatted_content );
+		$formatted_content = convert_chars( $formatted_content );
+		$formatted_content = wpautop( $formatted_content );
+		$formatted_content = shortcode_unautop( $formatted_content );
+		$formatted_content = prepend_attachment( $formatted_content );
+		$formatted_content = capital_P_dangit( $formatted_content );
+		$formatted_content = do_shortcode( $formatted_content );
+
+		return $formatted_content;
+	}
+
+	public static function get_countries_dropdown( $country = '' ) {
+		$countries = array(
+			'Afghanistan',
+			'Albania',
+			'Algeria',
+			'Andorra',
+			'Angola',
+			'Antigua and Barbuda',
+			'Argentina',
+			'Armenia',
+			'Aruba',
+			'Australia',
+			'Austria',
+			'Azerbaijan',
+			'Bahamas',
+			'Bahrain',
+			'Bangladesh',
+			'Barbados',
+			'Belarus',
+			'Belgium',
+			'Belize',
+			'Benin',
+			'Bhutan',
+			'Bolivia',
+			'Bonaire',
+			'Bosnia and Herzegovina',
+			'Botswana',
+			'Brazil',
+			'Brunei',
+			'Bulgaria',
+			'Burkina Faso',
+			'Burundi',
+			'Cambodia',
+			'Cameroon',
+			'Canada',
+			'Cape Verde',
+			'Central African Republic',
+			'Chad',
+			'Chile',
+			'China',
+			'Colombia',
+			'Comoros',
+			'Congo (Brazzaville)',
+			'Congo',
+			'Costa Rica',
+			"Cote d\'Ivoire",
+			'Croatia',
+			'Cuba',
+			'Curacao',
+			'Cyprus',
+			'Czech Republic',
+			'Denmark',
+			'Djibouti',
+			'Dominica',
+			'Dominican Republic',
+			'East Timor (Timor Timur)',
+			'Ecuador',
+			'Egypt',
+			'El Salvador',
+			'Equatorial Guinea',
+			'Eritrea',
+			'Estonia',
+                        'Eswatini',
+			'Ethiopia',
+			'Fiji',
+			'Finland',
+			'France',
+			'Gabon',
+			'Gambia, The',
+			'Georgia',
+			'Germany',
+			'Ghana',
+			'Greece',
+			'Grenada',
+			'Guatemala',
+			'Guinea',
+			'Guinea-Bissau',
+			'Guyana',
+			'Haiti',
+			'Honduras',
+			'Hong Kong',
+			'Hungary',
+			'Iceland',
+			'India',
+			'Indonesia',
+			'Iran',
+			'Iraq',
+			'Ireland',
+			'Israel',
+			'Italy',
+			'Jamaica',
+			'Japan',
+			'Jordan',
+			'Kazakhstan',
+			'Kenya',
+			'Kiribati',
+			'Korea, North',
+			'Korea, South',
+			'Kuwait',
+			'Kyrgyzstan',
+			'Laos',
+			'Latvia',
+			'Lebanon',
+			'Lesotho',
+			'Liberia',
+			'Libya',
+			'Liechtenstein',
+			'Lithuania',
+			'Luxembourg',
+			'Macedonia',
+			'Madagascar',
+			'Malawi',
+			'Malaysia',
+			'Maldives',
+			'Mali',
+			'Malta',
+			'Marshall Islands',
+			'Mauritania',
+			'Mauritius',
+			'Mexico',
+			'Micronesia',
+			'Moldova',
+			'Monaco',
+			'Mongolia',
+			'Montenegro',
+			'Morocco',
+			'Mozambique',
+			'Myanmar',
+			'Namibia',
+			'Nauru',
+			'Nepa',
+			'Netherlands',
+			'New Zealand',
+			'Nicaragua',
+			'Niger',
+			'Nigeria',
+			'Norway',
+			'Oman',
+			'Pakistan',
+			'Palau',
+                        'Palestine',
+			'Panama',
+			'Papua New Guinea',
+			'Paraguay',
+			'Peru',
+			'Philippines',
+			'Poland',
+			'Portugal',
+			'Qatar',
+			'Romania',
+			'Russia',
+			'Rwanda',
+			'Saint Kitts and Nevis',
+			'Saint Lucia',
+			'Saint Vincent',
+			'Samoa',
+			'San Marino',
+			'Sao Tome and Principe',
+			'Saudi Arabia',
+			'Senegal',
+			'Serbia',
+			'Seychelles',
+			'Sierra Leone',
+			'Singapore',
+			'Slovakia',
+			'Slovenia',
+			'Solomon Islands',
+			'Somalia',
+			'South Africa',
+			'Spain',
+			'Sri Lanka',
+			'Sudan',
+			'Suriname',
+			'Swaziland',
+			'Sweden',
+			'Switzerland',
+			'Syria',
+			'Taiwan',
+			'Tajikistan',
+			'Tanzania',
+			'Thailand',
+			'Togo',
+			'Tonga',
+			'Trinidad and Tobago',
+			'Tunisia',
+			'Turkey',
+			'Turkmenistan',
+			'Tuvalu',
+			'Uganda',
+			'Ukraine',
+			'United Arab Emirates',
+			'United Kingdom',
+			'United States of America',
+			'Uruguay',
+			'Uzbekistan',
+			'Vanuatu',
+			'Vatican City',
+			'Venezuela',
+			'Vietnam',
+			'Yemen',
+			'Zambia',
+			'Zimbabwe',
+		);
+		//let's try to "guess" country name
+		$curr_lev      = -1;
+		$guess_country = '';
+		foreach ( $countries as $country_name ) {
+			similar_text( strtolower( $country ), strtolower( $country_name ), $lev );
+			if ( $lev >= $curr_lev ) {
+				//this is closest match so far
+				$curr_lev      = $lev;
+				$guess_country = $country_name;
+			}
+			if ( $curr_lev == 100 ) {
+				//exact match
+				break;
+			}
+		}
+		if ( $curr_lev <= 80 ) {
+			// probably bad guess
+			$guess_country = '';
+		}
+		$countries_dropdown = '';
+		//let's add "(Please select)" option
+		$countries_dropdown .= "\r\n" . '<option value=""' . ( $country == '' ? ' selected' : '' ) . '>' . SwpmUtils::_( '(Please Select)' ) . '</option>';
+		if ( $guess_country == '' && $country != '' ) {
+			//since we haven't guessed the country name, let's add current value to the options
+			$countries_dropdown .= "\r\n" . '<option value="' . $country . '" selected>' . $country . '</option>';
+		}
+		if ( $guess_country != '' ) {
+			$country = $guess_country;
+		}
+		foreach ( $countries as $country_name ) {
+			$countries_dropdown .= "\r\n" . '<option value="' . $country_name . '"' . ( strtolower( $country_name ) == strtolower( $country ) ? ' selected' : '' ) . '>' . $country_name . '</option>';
+		}
+		return $countries_dropdown;
+	}
+
+	public static function get_button_type_name( $button_type ) {
+		$btnTypesNames = array(
+			'pp_buy_now'              => SwpmUtils::_( 'PayPal Buy Now' ),
+			'pp_subscription'         => SwpmUtils::_( 'PayPal Subscription' ),
+			'pp_smart_checkout'       => SwpmUtils::_( 'PayPal Smart Checkout' ),
+			'stripe_buy_now'          => SwpmUtils::_( 'Stripe Buy Now' ),
+			'stripe_subscription'     => SwpmUtils::_( 'Stripe Subscription' ),
+			'stripe_sca_buy_now'      => SwpmUtils::_( 'Stripe SCA Buy Now' ),
+			'stripe_sca_subscription' => SwpmUtils::_( 'Stripe SCA Subscription' ),
+			'braintree_buy_now'       => SwpmUtils::_( 'Braintree Buy Now' ),
+		);
+
+		$button_type_name = $button_type;
+
+		if ( array_key_exists( $button_type, $btnTypesNames ) ) {
+			$button_type_name = $btnTypesNames[ $button_type ];
+		}
+
+		return $button_type_name;
+	}
+
+	public static function format_money( $amount, $currency = false ) {
+		$formatted = number_format( $amount, 2 );
+		if ( $currency ) {
+			$formatted .= ' ' . $currency;
+		}
+		return $formatted;
+	}
+
+	public static function load_stripe_lib() {
+		//this function loads Stripe PHP SDK and ensures only once instance is loaded
+		if ( ! class_exists( '\Stripe\Stripe' ) ) {
+			require_once SIMPLE_WP_MEMBERSHIP_PATH . 'lib/stripe-gateway/init.php';
+			\Stripe\Stripe::setAppInfo( 'Simple Membership', SIMPLE_WP_MEMBERSHIP_VER, 'https://simple-membership-plugin.com/', 'pp_partner_Fvas9OJ0jQ2oNQ' );
+		}
+	}
+
+	public static function get_stripe_api_keys_from_payment_button( $button_id, $live = false ) {
+		$keys   = array(
+			'public' => '',
+			'secret' => '',
+		);
+		$button = get_post( $button_id );
+		if ( $button ) {
+			$opts            = get_option( 'swpm-settings' );
+			$use_global_keys = get_post_meta( $button_id, 'stripe_use_global_keys', true );
+
+			if ( $use_global_keys ) {
+				if ( $live ) {
+					$keys['public'] = isset( $opts['stripe-live-public-key'] ) ? $opts['stripe-live-public-key'] : '';
+					$keys['secret'] = isset( $opts['stripe-live-secret-key'] ) ? $opts['stripe-live-secret-key'] : '';
+				} else {
+					$keys['public'] = isset( $opts['stripe-test-public-key'] ) ? $opts['stripe-test-public-key'] : '';
+					$keys['secret'] = isset( $opts['stripe-test-secret-key'] ) ? $opts['stripe-test-secret-key'] : '';
+				}
+			} else {
+				if ( $live ) {
+					$stripe_live_secret_key      = get_post_meta( $button_id, 'stripe_live_secret_key', true );
+					$stripe_live_publishable_key = get_post_meta( $button_id, 'stripe_live_publishable_key', true );
+
+					$keys['public'] = $stripe_live_publishable_key;
+					$keys['secret'] = $stripe_live_secret_key;
+				} else {
+					$stripe_test_secret_key      = get_post_meta( $button_id, 'stripe_test_secret_key', true );
+					$stripe_test_publishable_key = get_post_meta( $button_id, 'stripe_test_publishable_key', true );
+
+					$keys['public'] = $stripe_test_publishable_key;
+					$keys['secret'] = $stripe_test_secret_key;
+				}
+			}
+		}
+		return $keys;
+	}
+
+	public static function mail( $email, $subject, $email_body, $headers ) {
+		$settings     = SwpmSettings::get_instance();
+		$html_enabled = $settings->get_value( 'email-enable-html' );
+		if ( ! empty( $html_enabled ) ) {
+			$headers   .= "Content-Type: text/html; charset=UTF-8\r\n";
+			$email_body = nl2br( $email_body );
+		}
+		wp_mail( $email, $subject, $email_body, $headers );
+	}
+
+	/**
+	* Outputs Stripe SCA frontend scripts and styles once
+	*/
+	public static function output_stripe_sca_frontend_scripts_once() {
+		$out = '';
+		if ( ! self::$stripe_sca_frontend_scripts_printed ) {
+			$out                                      .= '<script src="https://js.stripe.com/v3/"></script>';
+			$out                                      .= "<link rel='stylesheet' href='https://checkout.stripe.com/v3/checkout/button.css' type='text/css' media='all' />";
+			self::$stripe_sca_frontend_scripts_printed = true;
+		}
+		return $out;
+	}
+
+}

+ 42 - 0
simple-membership/classes/class.swpm-utils-template.php

@@ -0,0 +1,42 @@
+<?php
+
+class SwpmUtilsTemplate {
+
+    /*
+     * This function will load the template file in the following order
+     * wp-content/themes/your-child-theme/simple-membership/template-name.php
+     * wp-content/themes/your-main-theme/simple-membership/template-name.php
+     * The standard plugin's template file
+     */
+    public static function swpm_load_template($template_name, $require_once = true) {
+        
+        //List of file paths (in order of priority) where the plugin should check for the template.
+        $template_files = array(
+            get_stylesheet_directory() . '/' . SIMPLE_WP_MEMBERSHIP_TEMPLATE_PATH . '/' . $template_name, //First check inside child theme (if you are using a child theme)
+            get_template_directory() . '/' . SIMPLE_WP_MEMBERSHIP_TEMPLATE_PATH . '/' . $template_name, //Then check inside the main theme folder
+            SIMPLE_WP_MEMBERSHIP_PATH . 'views/' . $template_name //Otherwise load the standard template
+        );
+
+        //Filter hook to allow overriding of the template file path
+        $template_files = apply_filters( 'swpm_load_template_files', $template_files, $template_name);
+
+        foreach ($template_files as $file) {
+            if (file_exists($file)) {
+                $template_to_load = $file;
+                break;
+            }
+        }
+
+        //Lets load this template
+        if ($template_to_load) {
+            if ($require_once) {
+                require_once( $template_to_load );
+            } else {
+                require( $template_to_load );
+            }
+        } else {
+            wp_die(SwpmUtils::_('Error! Failed to find a template path for the specified template: ' . $template_name));
+        }
+    }
+
+}

+ 571 - 0
simple-membership/classes/class.swpm-utils.php

@@ -0,0 +1,571 @@
+<?php
+
+abstract class SwpmUtils {
+
+	public static function is_ajax() {
+		return defined( 'DOING_AJAX' ) && DOING_AJAX;
+	}
+
+	/*
+	 * This function handles various initial setup tasks that need to be executed very early on (before other functions of the plugin is called).
+	 */
+
+	public static function do_misc_initial_plugin_setup_tasks() {
+
+		//Management role/permission setup
+		$admin_dashboard_permission = SwpmSettings::get_instance()->get_value( 'admin-dashboard-access-permission' );
+		if ( empty( $admin_dashboard_permission ) ) {
+			//By default only admins can manage/see admin dashboard
+			define( 'SWPM_MANAGEMENT_PERMISSION', 'manage_options' );
+		} else {
+			define( 'SWPM_MANAGEMENT_PERMISSION', $admin_dashboard_permission );
+		}
+
+                //Override the settings menu (options.php) update capability according to the role set in "Admin Dashboard Access Permission" option.
+                add_filter( 'option_page_capability_swpm-settings-tab-1', 'SwpmUtils::swpm_settings_update_capability' );
+                add_filter( 'option_page_capability_swpm-settings-tab-2', 'SwpmUtils::swpm_settings_update_capability' );
+                add_filter( 'option_page_capability_swpm-settings-tab-3', 'SwpmUtils::swpm_settings_update_capability' );
+                add_filter( 'option_page_capability_swpm-settings-tab-4', 'SwpmUtils::swpm_settings_update_capability' );
+                add_filter( 'option_page_capability_swpm-settings-tab-5', 'SwpmUtils::swpm_settings_update_capability' );
+
+	}
+
+        public static function swpm_settings_update_capability($capability){
+            if ( defined('SWPM_MANAGEMENT_PERMISSION') ){
+                //Use SWPM defined one.
+                $capability = SWPM_MANAGEMENT_PERMISSION;
+            } else {
+                //Use default.
+                $capability = 'manage_options';
+            }
+            return $capability;
+        }
+
+	public static function subscription_type_dropdown( $selected ) {
+		return '<option ' . ( ( $selected == SwpmMembershipLevel::NO_EXPIRY ) ? 'selected="selected"' : '' ) . ' value="' . SwpmMembershipLevel::NO_EXPIRY . '">No Expiry</option>' .
+				'<option ' . ( ( $selected == SwpmMembershipLevel::DAYS ) ? 'selected="selected"' : '' ) . ' value="' . SwpmMembershipLevel::DAYS . '">Day(s)</option>' .
+				'<option ' . ( ( $selected == SwpmMembershipLevel::WEEKS ) ? 'selected="selected"' : '' ) . ' value="' . SwpmMembershipLevel::WEEKS . '">Week(s)</option>' .
+				'<option ' . ( ( $selected == SwpmMembershipLevel::MONTHS ) ? 'selected="selected"' : '' ) . ' value="' . SwpmMembershipLevel::MONTHS . '">Month(s)</option>' .
+				'<option ' . ( ( $selected == SwpmMembershipLevel::YEARS ) ? 'selected="selected"' : '' ) . ' value="' . SwpmMembershipLevel::YEARS . '">Year(s)</option>' .
+				'<option ' . ( ( $selected == SwpmMembershipLevel::FIXED_DATE ) ? 'selected="selected"' : '' ) . ' value="' . SwpmMembershipLevel::FIXED_DATE . '">Fixed Date</option>';
+	}
+
+	// $subscript_period must be integer.
+	public static function calculate_subscription_period_days( $subcript_period, $subscription_duration_type ) {
+		if ( $subscription_duration_type == SwpmMembershipLevel::NO_EXPIRY ) {
+			return 'noexpire';
+		}
+		if ( ! is_numeric( $subcript_period ) ) {
+			throw new Exception( ' subcript_period parameter must be integer in SwpmUtils::calculate_subscription_period_days method' );
+		}
+		switch ( strtolower( $subscription_duration_type ) ) {
+			case SwpmMembershipLevel::DAYS:
+				break;
+			case SwpmMembershipLevel::WEEKS:
+				$subcript_period = $subcript_period * 7;
+				break;
+			case SwpmMembershipLevel::MONTHS:
+				$subcript_period = $subcript_period * 30;
+				break;
+			case SwpmMembershipLevel::YEARS:
+				$subcript_period = $subcript_period * 365;
+				break;
+		}
+		return $subcript_period;
+	}
+
+	public static function get_expiration_timestamp( $user ) {
+		$permission = SwpmPermission::get_instance( $user->membership_level );
+		if ( SwpmMembershipLevel::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 = self::get_expiration_timestamp( $user );
+		if ( $expiration_timestamp < time() ) {
+			//Account expired.
+			return true;
+		}
+		return false;
+	}
+
+	/*
+	 * Returns a formatted expiry date string (of a member). This can be useful to echo the date value.
+	 */
+
+	public static function get_formatted_expiry_date( $start_date, $subscription_duration, $subscription_duration_type ) {
+		if ( $subscription_duration_type == SwpmMembershipLevel::FIXED_DATE ) {
+			//Membership will expire after a fixed date.
+			return self::get_formatted_and_translated_date_according_to_wp_settings( $subscription_duration );
+		}
+
+		$expires = self::calculate_subscription_period_days( $subscription_duration, $subscription_duration_type );
+		if ( $expires == 'noexpire' ) {
+			//Membership is set to no expiry or until cancelled.
+			return self::_( 'Never' );
+		}
+
+		//Membership is set to a duration expiry settings.
+		return date_i18n( get_option( 'date_format' ), strtotime( $start_date . ' ' . $expires . ' days' ) );
+	}
+
+	public static function gender_dropdown( $selected = 'not specified' ) {
+		return '<option ' . ( ( strtolower( $selected ) == 'male' ) ? 'selected="selected"' : '' ) . ' value="male">' . SwpmUtils::_('Male') . '</option>' .
+				'<option ' . ( ( strtolower( $selected ) == 'female' ) ? 'selected="selected"' : '' ) . ' value="female">' . SwpmUtils::_('Female') . '</option>' .
+				'<option ' . ( ( strtolower( $selected ) == 'not specified' ) ? 'selected="selected"' : '' ) . ' value="not specified">' . SwpmUtils::_('Not Specified') . '</option>';
+	}
+
+	public static function get_account_state_options() {
+		return array(
+			'active'              => self::_( 'Active' ),
+			'inactive'            => self::_( 'Inactive' ),
+			'activation_required' => self::_( 'Activation Required' ),
+			'pending'             => self::_( 'Pending' ),
+			'expired'             => self::_( 'Expired' ),
+		);
+	}
+
+	public static function account_state_dropdown( $selected = 'active' ) {
+		$options = self::get_account_state_options();
+		$html    = '';
+		foreach ( $options as $key => $value ) {
+			$html .= '<option ' . ( ( strtolower( $selected ) == $key ) ? 'selected="selected"' : '' ) . '  value="' . $key . '"> ' . $value . '</option>';
+		}
+		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 .= '<option ' . ( $selected == $level->id ? 'selected="selected"' : '' ) . ' value="' . $level->id . '" >' . $level->alias . '</option>';
+		}
+		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_membership_level_row_by_id( $level_id ) {
+		global $wpdb;
+		$query           = $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'swpm_membership_tbl WHERE id=%d', $level_id );
+		$level_resultset = $wpdb->get_row( $query );
+		return $level_resultset;
+	}
+
+	public static function membership_level_id_exists( $level_id ) {
+		//Returns true if the specified membership level exists in the system. Returns false if the level has been deleted (or doesn't exist).
+		$all_level_ids = self::get_all_membership_level_ids();
+		if ( in_array( $level_id, $all_level_ids ) ) {
+			//Valid level ID
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	public static function get_registration_complete_prompt_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  = SwpmSettings::get_instance();
+		$separator = '?';
+		$url       = $settings->get_value( 'registration-page-url' );
+		if ( strpos( $url, '?' ) !== false ) {
+			$separator = '&';
+		}
+
+		$links = array();
+		foreach ( $members as $member ) {
+			$reg_url = $url . $separator . 'member_id=' . $member->member_id . '&code=' . $member->reg_code;
+			if ( $send_email && empty( $member->user_name ) ) {
+				$tags = array( '{first_name}', '{last_name}', '{reg_link}' );
+				$vals = array( $member->first_name, $member->last_name, $reg_url );
+
+				$subject = $settings->get_value( 'reg-prompt-complete-mail-subject' );
+				if ( empty( $subject ) ) {
+					$subject = 'Please complete your registration';
+				}
+
+				$body = $settings->get_value( 'reg-prompt-complete-mail-body' );
+				if ( empty( $body ) ) {
+					$body = "Please use the following link to complete your registration. \n {reg_link}";
+				}
+				$body       = html_entity_decode( $body );
+				$email_body = str_replace( $tags, $vals, $body );
+
+				$from_address = $settings->get_value( 'email-from' );
+				$headers      = 'From: ' . $from_address . "\r\n";
+
+				$subject    = apply_filters( 'swpm_email_complete_your_registration_subject', $subject );
+				$email_body = apply_filters( 'swpm_email_complete_your_registration_body', $email_body );
+				SwpmMiscUtils::mail( $member->email, $subject, $email_body, $headers );
+				SwpmLog::log_simple_debug( 'Prompt to complete registration email sent to: ' . $member->email . '. From email address value used: ' . $from_address, true );
+			}
+			$links[] = $reg_url;
+		}
+		return $links;
+	}
+
+	/* This function is deprecated and will be removed in the future. Use SwpmMemberUtils::update_wp_user_role() instead */
+
+	public static function update_wp_user_Role( $wp_user_id, $role ) {
+		// Deprecated function.
+		SwpmMemberUtils::update_wp_user_role( $wp_user_id, $role );
+	}
+
+	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 ) {
+
+                //First, check if email or username belongs to an existing admin user.
+                SwpmMemberUtils::check_and_die_if_email_belongs_to_admin_user($wp_user_data['user_email']);
+                SwpmMemberUtils::check_and_die_if_username_belongs_to_admin_user($wp_user_data['user_login']);
+
+                //At this point, the username or the email is not taken by any existing wp user with admin role.
+                //Lets continue the normal registration process.
+
+		//Check if the email belongs to an existing wp user account.
+		$wp_user_id = email_exists( $wp_user_data['user_email'] );
+		if ( $wp_user_id ) {
+			//A wp user account exist with this email.
+                        //For signle site WP install, no new user will be created. The existing user ID will be returned.
+		} else {
+                    //Check if the username belongs to an existing wp user account.
+                    $wp_user_id = username_exists( $wp_user_data['user_login'] );
+                    if ( $wp_user_id ) {
+                        //A wp user account exist with this username.
+                        //For signle site WP install, no new user will be created. The existing user ID will be returned.
+                    }
+                }
+
+		//At this point 1) A WP User with this email or username doesn't exist. Or 2) The associated wp user doesn't have admin role
+		//Lets create a new wp user record or attach the SWPM profile to an existing user accordingly.
+
+		if ( self::is_multisite_install() ) {
+			//WP Multi-Site install
+			global $blog_id;
+			if ( $wp_user_id ) {
+				//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;
+			}
+                        //No existing user. Create a new one.
+			$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 );
+                        //End of WPMS
+		} else {
+			//This is a WP Single Site install.
+
+                        //Lets see if an existing WP user exist from the email_exists() or username_exists() check earlier.
+			if ( $wp_user_id ) {
+                            return $wp_user_id;
+			}
+
+                        //No existing user. Try to create a brand new WP user entry.
+			$wp_user_id = wp_create_user( $wp_user_data['user_login'], $wp_user_data['password'], $wp_user_data['user_email'] );
+
+                        //Update that newly created user's profile with additional data.
+                        $wp_user_data['ID'] = $wp_user_id;
+                        wp_update_user( $wp_user_data ); //Core WP function. Updates/Syncs the user info and 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, 'simple-membership' );
+	}
+
+	public static function e( $msg ) {
+		_e( $msg, 'simple-membership' );
+	}
+
+	/*
+	 * Deprecated. Instead use SwpmUtils::has_admin_management_permission()
+	 */
+
+	public static function is_admin() {
+		//This function returns true if the current user has WordPress admin management permission (not to be mistaken with SWPM admin permission.
+		//This function is NOT like the WordPress's is_admin() function which determins if we are on the admin end of the site.
+		//TODO - rename this function to something like is_admin_user()
+		return current_user_can( 'manage_options' );
+	}
+
+	public static function has_admin_management_permission() {
+		if ( current_user_can( SWPM_MANAGEMENT_PERMISSION ) ) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+        /*
+         * Returns the current date timestamp value suitable for debug log file.
+         */
+        public static function get_current_timestamp_for_debug_log(){
+            $current_wp_time = current_time('mysql');
+            $dt = new DateTime($current_wp_time);
+            $current_date = $dt->format('Y/m/d H:i:s');
+            return $current_date;
+        }
+
+        /*
+         * Returns the current date value in (Y-m-d) format in the timzeone set for this WordPress install.
+         */
+        public static function get_current_date_in_wp_zone(){
+            $current_wp_time = current_time('mysql');
+            $dt = new DateTime($current_wp_time);
+            $current_date = $dt->format('Y-m-d');
+            return $current_date;
+        }
+
+	/*
+	 * Formats the given date value according to the WP date format settings. This function is useful for displaying a human readable date value to the user.
+	 */
+	public static function get_formatted_date_according_to_wp_settings( $date ) {
+		$date_format = get_option( 'date_format' );
+		if ( empty( $date_format ) ) {
+			//WordPress's date form settings is not set. Lets set a default format.
+			$date_format = 'Y-m-d';
+		}
+
+		$date_obj       = new DateTime( $date );
+		$formatted_date = $date_obj->format( $date_format ); //Format the date value using date format settings
+		return $formatted_date;
+	}
+
+	/*
+	 * Formats and Translates the given date value according to the WP date format settings. This function is useful for displaying a human readable date value to the user.
+	 * The $date argument value must be in nromal date format (2025-01-15). The function will use strtotime() function to convert it to unix time then use it.
+	 */
+	public static function get_formatted_and_translated_date_according_to_wp_settings( $date ) {
+		$date_format = get_option( 'date_format' );
+		if ( empty( $date_format ) ) {
+			//WordPress's date form settings is not set. Lets set a default format.
+			$date_format = 'Y-m-d';
+		}
+
+		$formatted_translated_date = date_i18n( $date_format, strtotime( $date ) );
+		return $formatted_translated_date;
+	}
+
+	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' );
+		if ( ! empty( $encrypted ) ) {
+			return SwpmPermission::get_instance( $encrypted )->get( 'id' );
+		}
+
+		$is_free    = SwpmSettings::get_instance()->get_value( 'enable-free-membership' );
+		$free_level = absint( SwpmSettings::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 );
+		if ( ! empty( $member_id ) && ! empty( $code ) ) {
+			return true;
+		}
+		return false;
+	}
+
+	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 get_incomplete_paid_member_info_by_ip() {
+		global $wpdb;
+		$user_ip = self::get_user_ip_address();
+		if ( ! empty( $user_ip ) ) {
+			//Lets check if a payment has been confirmed from this user's IP and the profile needs to be completed (where username is empty).
+			$username = '';
+			$query    = 'SELECT * FROM ' . $wpdb->prefix . 'swpm_members_tbl WHERE last_accessed_from_ip=%s AND user_name=%s';
+			$query    = $wpdb->prepare( $query, $user_ip, $username );
+			$result   = $wpdb->get_row( $query );
+			return $result;
+		}
+		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 = SwpmSettings::get_instance()->get_value( 'allow-account-deletion' );
+		if ( empty( $allow_account_deletion ) ) {
+			return '';
+		}
+
+		$account_delete_link  = '<div class="swpm-profile-account-delete-section">';
+		$account_delete_link .= '<a href="' . SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL . '/?swpm_delete_account=1"><div class="swpm-account-delete-button">' . self::_( 'Delete Account' ) . '</div></a>';
+		$account_delete_link .= '</div>';
+		return $account_delete_link;
+	}
+
+	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
+	}
+
+	public static function get_user_ip_address() {
+		$user_ip = '';
+		if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) && ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
+			$user_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
+		} else {
+			$user_ip = $_SERVER['REMOTE_ADDR'];
+		}
+
+		if ( strstr( $user_ip, ',' ) ) {
+			$ip_values = explode( ',', $user_ip );
+			$user_ip   = $ip_values['0'];
+		}
+
+		return apply_filters( 'swpm_get_user_ip_address', $user_ip );
+	}
+
+	public static function is_first_click_free( &$content ) {
+		$is_first_click                 = false;
+		$args                           = array( $is_first_click, $content );
+		$filtered                       = apply_filters( 'swpm_first_click_free', $args );
+		list($is_first_click, $content) = $filtered;
+		return $is_first_click;
+	}
+
+	private static function crypt_fallback( $string, $action = 'e' ) {
+		if ( $action === 'e' ) {
+			return base64_encode( $string );
+		} else {
+			return base64_decode( $string );
+		}
+	}
+
+	public static function crypt( $string, $action = 'e' ) {
+		//check if openssl module is enabled
+		if ( ! extension_loaded( 'openssl' ) ) {
+			// no openssl extension loaded. Can't ecnrypt
+			return self::crypt_fallback( $string, $action );
+		}
+		//check if encrypt method is supported
+		$encrypt_method    = 'aes-256-ctr';
+		$available_methods = openssl_get_cipher_methods();
+		if ( ! in_array( $encrypt_method, $available_methods ) ) {
+			// no ecryption method supported. Can't encrypt
+			return self::crypt_fallback( $string, $action );
+		}
+
+		$output     = false;
+		$secret_key = wp_salt( 'auth' );
+		$secret_iv  = wp_salt( 'secure_auth' );
+		$key        = hash( 'sha256', $secret_key );
+		$iv         = substr( hash( 'sha256', $secret_iv ), 0, 16 );
+
+		if ( $action == 'e' ) {
+			$output = base64_encode( openssl_encrypt( $string, $encrypt_method, $key, 0, $iv ) );
+		} elseif ( $action == 'd' ) {
+			$output = openssl_decrypt( base64_decode( $string ), $encrypt_method, $key, 0, $iv );
+		}
+
+		return $output;
+	}
+
+}

+ 165 - 0
simple-membership/classes/class.swpm-wp-loaded-tasks.php

@@ -0,0 +1,165 @@
+<?php
+
+class SwpmWpLoadedTasks {
+
+	public function __construct() {
+
+	}
+
+	/*
+	 * This is triggered after all plugins, themes and WP has loaded.
+	 * It is triggered after init, plugins_loaded etc.
+	 */
+	public function do_wp_loaded_tasks() {
+		$this->synchronise_swpm_logout_for_wp_users();
+
+		//IPN listener
+		$this->swpm_ipn_listener();
+
+		//Cancel subscirption action listener
+		$cancel_sub_action = filter_input( INPUT_POST, 'swpm_do_cancel_sub', FILTER_SANITIZE_NUMBER_INT );
+
+		if ( ! empty( $cancel_sub_action ) ) {
+			$this->do_cancel_sub();
+		}
+
+	}
+
+	/*
+	 * Logs out the user from the swpm session if they are logged out of the WP user session
+	 */
+	public function synchronise_swpm_logout_for_wp_users() {
+		if ( ! is_user_logged_in() ) {
+			/* WP user is logged out. So logout the SWPM user (if applicable) */
+			if ( SwpmMemberUtils::is_member_logged_in() ) {
+
+				//Check if force WP user login sync is enabled or not
+				$force_wp_user_sync = SwpmSettings::get_instance()->get_value( 'force-wp-user-sync' );
+				if ( empty( $force_wp_user_sync ) ) {
+					return '';
+				}
+				/* Force WP user login sync is enabled. */
+				/* SWPM user is logged in the system. Log him out. */
+				SwpmLog::log_auth_debug( 'synchronise_swpm_logout_for_wp_users() - Force wp user login sync is enabled. ', true );
+				SwpmLog::log_auth_debug( 'WP user session is logged out for this user. So logging out of the swpm session also.', true );
+				wp_logout();
+			}
+		}
+	}
+
+	/* Payment Gateway IPN listener */
+
+	public function swpm_ipn_listener() {
+
+		//Listen and handle PayPal IPN
+		$swpm_process_ipn = filter_input( INPUT_GET, 'swpm_process_ipn' );
+		if ( $swpm_process_ipn == '1' ) {
+			include SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm_handle_pp_ipn.php';
+			exit;
+		}
+
+		//Listen and handle Stripe Buy Now IPN
+		$swpm_process_stripe_buy_now = filter_input( INPUT_GET, 'swpm_process_stripe_buy_now' );
+		if ( $swpm_process_stripe_buy_now == '1' ) {
+			include SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm-stripe-buy-now-ipn.php';
+			exit;
+		}
+
+		//Listen and handle Stripe SCA Buy Now IPN
+		$swpm_process_stripe_sca_buy_now = filter_input( INPUT_GET, 'swpm_process_stripe_sca_buy_now' );
+		if ( $swpm_process_stripe_sca_buy_now == '1' ) {
+			include SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm-stripe-sca-buy-now-ipn.php';
+			exit;
+		}
+
+		//Listen and handle Stripe Subscription IPN
+		$swpm_process_stripe_subscription = filter_input( INPUT_GET, 'swpm_process_stripe_subscription' );
+		if ( $swpm_process_stripe_subscription == '1' ) {
+			include SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm-stripe-subscription-ipn.php';
+			exit;
+		}
+
+		//Listen and handle Stripe SCA Subscription IPN
+		$swpm_process_stripe_sca_subscription = filter_input( INPUT_GET, 'swpm_process_stripe_sca_subscription' );
+		$hook                                 = filter_input( INPUT_GET, 'hook', FILTER_SANITIZE_NUMBER_INT );
+		if ( $swpm_process_stripe_sca_subscription == '1' ) {
+						//$hook == 1 means it is a background post via webshooks. Otherwise it is direct post to the script after payment (at the time of payment).
+			if ( $hook ) {
+				include SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm-stripe-subscription-ipn.php';
+			} else {
+				include SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm-stripe-sca-subscription-ipn.php';
+			}
+			exit;
+		}
+
+		//Listen and handle Braintree Buy Now IPN
+		$swpm_process_braintree_buy_now = filter_input( INPUT_GET, 'swpm_process_braintree_buy_now' );
+		if ( $swpm_process_braintree_buy_now == '1' ) {
+			include SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm-braintree-buy-now-ipn.php';
+			exit;
+		}
+
+		if ( wp_doing_ajax() ) {
+			//Listen and handle smart paypal checkout IPN
+			include SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm-smart-checkout-ipn.php';
+			add_action( 'wp_ajax_swpm_process_pp_smart_checkout', 'swpm_pp_smart_checkout_ajax_hanlder' );
+			add_action( 'wp_ajax_nopriv_swpm_process_pp_smart_checkout', 'swpm_pp_smart_checkout_ajax_hanlder' );
+
+			//Listed and handle Stripe SCA checkout session create requests
+			require_once SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm-stripe-sca-buy-now-ipn.php';
+		}
+	}
+
+	private function do_cancel_sub() {
+
+		function msg( $msg, $is_error = true ) {
+			echo $msg;
+			echo '<br><br>';
+			echo SwpmUtils::_( 'You will be redirected to the previous page in a few seconds. If not, please <a href="">click here</a>.' );
+			echo '<script>function toPrevPage(){window.location = window.location.href;}setTimeout(toPrevPage,5000);</script>';
+			if ( ! $is_error ) {
+				wp_die( '', SwpmUtils::_( 'Success!' ), array( 'response' => 200 ) );
+			}
+			wp_die();
+		}
+
+		$token = filter_input( INPUT_POST, 'swpm_cancel_sub_token', FILTER_SANITIZE_STRING );
+		if ( empty( $token ) ) {
+			//no token
+			msg( SwpmUtils::_( 'No token provided.' ) );
+		}
+
+		//check nonce
+		$nonce = filter_input( INPUT_POST, 'swpm_cancel_sub_nonce', FILTER_SANITIZE_STRING );
+		if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, $token ) ) {
+			//nonce check failed
+			msg( SwpmUtils::_( 'Nonce check failed.' ) );
+		}
+
+		if ( ! SwpmMemberUtils::is_member_logged_in() ) {
+			//member not logged in
+			msg( SwpmUtils::_( 'You are not logged in.' ) );
+		}
+
+		$member_id = SwpmMemberUtils::get_logged_in_members_id();
+
+		$subs = new SWPM_Member_Subscriptions( $member_id );
+
+		$sub = $subs->find_by_token( $token );
+
+		if ( empty( $sub ) ) {
+			//no subscription found
+			return false;
+		}
+
+		$res = $subs->cancel( $sub['sub_id'] );
+
+		if ( $res !== true ) {
+			msg( $res );
+		}
+
+		msg( SwpmUtils::_( 'Subscription has been cancelled.' ), false );
+
+	}
+
+}

+ 7 - 0
simple-membership/classes/common/class.swpm-list-table.php

@@ -0,0 +1,7 @@
+<?php
+
+/*
+ * Base class for handling list tables.
+ */
+
+/*** This class is currently not being used ***/

+ 0 - 0
simple-membership/classes/index.html


+ 226 - 0
simple-membership/classes/shortcode-related/class.swpm-shortcodes-handler.php

@@ -0,0 +1,226 @@
+<?php
+
+class SwpmShortcodesHandler {
+
+	public function __construct() {
+		//Register all the shortcodes here
+		add_shortcode( 'swpm_payment_button', array( &$this, 'swpm_payment_button_sc' ) );
+		add_shortcode( 'swpm_thank_you_page_registration', array( &$this, 'swpm_ty_page_rego_sc' ) );
+
+		add_shortcode( 'swpm_show_expiry_date', array( &$this, 'swpm_show_expiry_date_sc' ) );
+
+		add_shortcode( 'swpm_mini_login', array( &$this, 'swpm_show_mini_login_sc' ) );
+
+		add_shortcode( 'swpm_paypal_subscription_cancel_link', array( &$this, 'swpm_pp_cancel_subs_link_sc' ) );
+
+		add_shortcode( 'swpm_stripe_subscription_cancel_link', array( $this, 'swpm_stripe_cancel_subs_link_sc' ) );
+	}
+
+	public function swpm_payment_button_sc( $args ) {
+		extract(
+			shortcode_atts(
+				array(
+					'id'          => '',
+					'button_text' => '',
+					'new_window'  => '',
+					'class'       => '',
+				),
+				$args
+			)
+		);
+
+		if ( empty( $id ) ) {
+			return '<p class="swpm-red-box">Error! You must specify a button ID with this shortcode. Check the usage documentation.</p>';
+		}
+
+		$button_id = $id;
+		//$button = get_post($button_id); //Retrieve the CPT for this button
+		$button_type = get_post_meta( $button_id, 'button_type', true );
+		if ( empty( $button_type ) ) {
+			$error_msg  = '<p class="swpm-red-box">';
+			$error_msg .= 'Error! The button ID (' . $button_id . ') you specified in the shortcode does not exist. You may have deleted this payment button. ';
+			$error_msg .= 'Go to the Manage Payment Buttons interface then copy and paste the correct button ID in the shortcode.';
+			$error_msg .= '</p>';
+			return $error_msg;
+		}
+
+		include_once( SIMPLE_WP_MEMBERSHIP_PATH . 'views/payments/payment-gateway/paypal_button_shortcode_view.php' );
+		include_once( SIMPLE_WP_MEMBERSHIP_PATH . 'views/payments/payment-gateway/stripe_button_shortcode_view.php' );
+		include_once( SIMPLE_WP_MEMBERSHIP_PATH . 'views/payments/payment-gateway/stripe_sca_button_shortcode_view.php' );
+		include_once( SIMPLE_WP_MEMBERSHIP_PATH . 'views/payments/payment-gateway/braintree_button_shortcode_view.php' );
+		include_once( SIMPLE_WP_MEMBERSHIP_PATH . 'views/payments/payment-gateway/paypal_smart_checkout_button_shortcode_view.php' );
+
+		$button_code = '';
+		$button_code = apply_filters( 'swpm_payment_button_shortcode_for_' . $button_type, $button_code, $args );
+
+		$output  = '';
+		$output .= '<div class="swpm-payment-button">' . $button_code . '</div>';
+
+		return $output;
+	}
+
+	public function swpm_ty_page_rego_sc( $args ) {
+		$output   = '';
+		$settings = SwpmSettings::get_instance();
+
+		//If user is logged in then the purchase will be applied to the existing profile
+		if ( SwpmMemberUtils::is_member_logged_in() ) {
+			$username = SwpmMemberUtils::get_logged_in_members_username();
+			$output  .= '<div class="swpm-ty-page-registration-logged-in swpm-yellow-box">';
+			$output  .= '<p>' . SwpmUtils::_( 'Your membership profile will be updated to reflect the payment.' ) . '</p>';
+			$output  .= SwpmUtils::_( 'Your profile username: ' ) . $username;
+			$output  .= '</div>';
+			return $output;
+		}
+
+		$output     .= '<div class="swpm-ty-page-registration">';
+		$member_data = SwpmUtils::get_incomplete_paid_member_info_by_ip();
+		if ( $member_data ) {
+			//Found a member profile record for this IP that needs to be completed
+			$reg_page_url      = $settings->get_value( 'registration-page-url' );
+			$rego_complete_url = add_query_arg(
+				array(
+					'member_id' => $member_data->member_id,
+					'code'      => $member_data->reg_code,
+				),
+				$reg_page_url
+			);
+			$output           .= '<div class="swpm-ty-page-registration-link swpm-yellow-box">';
+			$output           .= '<p>' . SwpmUtils::_( 'Click on the following link to complete the registration.' ) . '</p>';
+			$output           .= '<p><a href="' . $rego_complete_url . '">' . SwpmUtils::_( 'Click here to complete your paid registration' ) . '</a></p>';
+			$output           .= '</div>';
+		} else {
+			//Nothing found. Check again later.
+			$output .= '<div class="swpm-ty-page-registration-link swpm-yellow-box">';
+			$output .= SwpmUtils::_( 'If you have just made a membership payment then your payment is yet to be processed. Please check back in a few minutes. An email will be sent to you with the details shortly.' );
+			$output .= '</div>';
+		}
+
+		$output .= '</div>'; //end of .swpm-ty-page-registration
+
+		return $output;
+	}
+
+	public function swpm_show_expiry_date_sc( $args ) {
+		$output = '<div class="swpm-show-expiry-date">';
+		if ( SwpmMemberUtils::is_member_logged_in() ) {
+			$auth        = SwpmAuth::get_instance();
+			$expiry_date = $auth->get_expire_date();
+			$output     .= SwpmUtils::_( 'Expiry: ' ) . $expiry_date;
+		} else {
+			$output .= SwpmUtils::_( 'You are not logged-in as a member' );
+		}
+		$output .= '</div>';
+		return $output;
+	}
+
+	public function swpm_show_mini_login_sc( $args ) {
+
+		$login_page_url   = SwpmSettings::get_instance()->get_value( 'login-page-url' );
+		$join_page_url    = SwpmSettings::get_instance()->get_value( 'join-us-page-url' );
+		$profile_page_url = SwpmSettings::get_instance()->get_value( 'profile-page-url' );
+		$logout_url       = SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL . '?swpm-logout=true';
+
+		$filtered_login_url = apply_filters( 'swpm_get_login_link_url', $login_page_url ); //Addons can override the login URL value using this filter.
+
+		$output = '<div class="swpm_mini_login_wrapper">';
+
+		//Check if the user is logged in or not
+		$auth = SwpmAuth::get_instance();
+		if ( $auth->is_logged_in() ) {
+			//User is logged in
+			$username = $auth->get( 'user_name' );
+			$output  .= '<span class="swpm_mini_login_label">' . SwpmUtils::_( 'Logged in as: ' ) . '</span>';
+			$output  .= '<span class="swpm_mini_login_username">' . $username . '</span>';
+			$output  .= '<span class="swpm_mini_login_profile"> | <a href="' . $profile_page_url . '">' . SwpmUtils::_( 'Profile' ) . '</a></span>';
+			$output  .= '<span class="swpm_mini_login_logout"> | <a href="' . $logout_url . '">' . SwpmUtils::_( 'Logout' ) . '</a></span>';
+		} else {
+			//User not logged in.
+			$output .= '<span class="swpm_mini_login_login_here"><a href="' . $filtered_login_url . '">' . SwpmUtils::_( 'Login Here' ) . '</a></span>';
+			$output .= '<span class="swpm_mini_login_no_membership"> | ' . SwpmUtils::_( 'Not a member? ' ) . '</span>';
+			$output .= '<span class="swpm_mini_login_join_now"><a href="' . $join_page_url . '">' . SwpmUtils::_( 'Join Now' ) . '</a></span>';
+		}
+
+		$output .= '</div>';
+
+		$output = apply_filters( 'swpm_mini_login_output', $output );
+
+		return $output;
+	}
+
+	public function swpm_stripe_cancel_subs_link_sc( $args ) {
+                //Shortcode parameters: ['anchor_text']
+
+		if ( ! SwpmMemberUtils::is_member_logged_in() ) {
+			//member not logged in
+			return SwpmUtils::_( 'You are not logged-in as a member' );
+		}
+		$member_id = SwpmMemberUtils::get_logged_in_members_id();
+
+		$subs = new SWPM_Member_Subscriptions( $member_id );
+
+		if ( empty( $subs->get_active_subs_count() ) ) {
+			//no active subscriptions found
+			return SwpmUtils::_( 'No active subscriptions' );
+		}
+
+                $output = $subs->get_stripe_subs_cancel_url($args, false);
+
+		return $output;
+	}
+
+	public function swpm_pp_cancel_subs_link_sc( $args ) {
+                //Shortcode parameters: ['anchor_text'], ['merchant_id']
+
+		extract(
+			shortcode_atts(
+				array(
+					'merchant_id' => '',
+					'anchor_text' => '',
+				),
+				$args
+			)
+		);
+
+		if ( empty( $merchant_id ) ) {
+			return '<p class="swpm-red-box">Error! You need to specify your secure PayPal merchant ID in the shortcode using the "merchant_id" parameter.</p>';
+		}
+
+		$output   = '';
+		$settings = SwpmSettings::get_instance();
+
+		//Check if the member is logged-in
+		if ( SwpmMemberUtils::is_member_logged_in() ) {
+			$user_id = SwpmMemberUtils::get_logged_in_members_id();
+		}
+
+		if ( ! empty( $user_id ) ) {
+			//The user is logged-in
+
+			//Set the default anchor text (if one is provided via teh shortcode).
+			if ( empty( $anchor_text ) ) {
+				$anchor_text = SwpmUtils::_( 'Unsubscribe from PayPal' );
+			}
+
+			$output         .= '<div class="swpm-paypal-subscription-cancel-link">';
+			$sandbox_enabled = $settings->get_value( 'enable-sandbox-testing' );
+			if ( $sandbox_enabled ) {
+				//Sandbox mode
+				$output .= '<a href="https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_subscr-find&alias=' . $merchant_id . '" _fcksavedurl="https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_subscr-find&alias=' . $merchant_id . '">';
+				$output .= $anchor_text;
+				$output .= '</a>';
+			} else {
+				//Live mode
+				$output .= '<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_subscr-find&alias=' . $merchant_id . '" _fcksavedurl="https://www.paypal.com/cgi-bin/webscr?cmd=_subscr-find&alias=' . $merchant_id . '">';
+				$output .= $anchor_text;
+				$output .= '</a>';
+			}
+			$output .= '</div>';
+
+		} else {
+			//The user is NOT logged-in
+			$output .= '<p>' . SwpmUtils::_( 'You are not logged-in as a member' ) . '</p>';
+		}
+		return $output;
+	}
+}

+ 0 - 0
simple-membership/css/images/index.html


BIN
simple-membership/css/images/ui-icons_444444_256x240.png


BIN
simple-membership/css/images/ui-icons_555555_256x240.png


BIN
simple-membership/css/images/ui-icons_777620_256x240.png


BIN
simple-membership/css/images/ui-icons_777777_256x240.png


BIN
simple-membership/css/images/ui-icons_cc0000_256x240.png


BIN
simple-membership/css/images/ui-icons_ffffff_256x240.png


+ 0 - 0
simple-membership/css/index.html


File diff suppressed because it is too large
+ 0 - 0
simple-membership/css/jquery-ui.min.css


+ 34 - 0
simple-membership/css/swpm.addons.listing.css

@@ -0,0 +1,34 @@
+.swpm_addon_item_canvas{
+    background-color:#fff;
+    font-family:sans-serif,arial;
+    font-size:12px;
+    border:1px solid #ccc;
+    display:block;
+    float:left;
+    margin:3px 12px 12px 0;
+    padding:10px 0px 10px 10px;
+    position:relative;
+    width:222px;
+    height:340px;
+}
+.swpm_addon_item_canvas:hover{border-color:#999;}
+.swpm_addon_item_thumb img {height: 150px; width: 200px; padding: 5px; border: 1px solid #ccc;}
+.swpm_addon_item_thumb a img {border: 1px solid #ccc;}
+.swpm_addon_item_body{line-height:22px;height:170px;overflow:hidden;}
+.swpm_addon_item_name{font-size:16px;font-weight:bold;text-align: center;margin:10px 10px 10px 0px;}
+.swpm_addon_item_description{margin:10px 10px 5px 0px;text-align:justify;overflow:hidden;height:70px;}
+.swpm_addon_clear{clear:both;}
+.swpm_addon_item_details_link{
+    text-align: center;
+}
+.swpm_addon_item_details_link a{
+    border: 3px solid #2d3140;
+    color: #2d3140;
+    display: inline-block;
+    padding: 5px 15px;
+    text-decoration: none !important;
+}
+.swpm_addon_item_details_link a:hover{
+    background-color: #2d3140;
+    color: #FFF;    
+}

+ 167 - 0
simple-membership/css/swpm.common.css

@@ -0,0 +1,167 @@
+/* General CSS */
+.swpm-margin-10{
+    margin: 10px;
+}
+.swpm-margin-top-10{
+    margin-top: 10px;
+}
+.swpm-margin-bottom-10{
+    margin-bottom: 10px;
+}
+.swpm-hidden{
+    display: none;
+}
+
+.swpm-yellow-box{
+    margin: 10px 0px;
+    padding: 10px;
+    background-color: #FFFFE0;
+    border-color: #E6DB55;
+    border-radius: 3px 3px 3px 3px;
+    border-style: solid;
+    border-width: 1px;    
+}
+
+.swpm-red-box {
+    margin: 10px 0px;
+    padding: 10px;
+    background-color: #FFEBE8;
+    border-color: #CC0000;
+    color: #333333;
+    border-radius: 3px 3px 3px 3px;
+    border-style: solid;
+    border-width: 1px;
+}
+
+/* Wrap directly with this class (not to be used with a paragraph tag) */
+.swpm-orange-box{
+    margin: 10px 0px;
+    padding: 15px 10px;
+    color: #3F2502;
+    text-shadow: 1px 1px #FFFFFF;
+    background-color: #FFF6D5;
+    border-color: #D1B655;
+    border-radius: 3px 3px 3px 3px;
+    border-style: solid;
+    border-width: 1px;
+}
+
+/* Wrap directly with this class (not to be used with a paragraph tag) */
+.swpm-grey-box{
+    margin: 10px 0px;
+    padding: 15px 10px;
+    background-color: #DDDDDD;
+    border-color: #CCCCCC;
+    border-radius: 3px 3px 3px 3px;
+    border-style: solid;
+    border-width: 1px;
+}
+
+/* Wrap directly with this class (not to be used with a paragraph tag) */
+.swpm-green-box {
+    margin: 10px 0px;
+    padding: 15px 10px;
+    background-color: #CCF4D6;
+    border-color: #059B53;
+    color: #043B14;
+    border-radius: 3px 3px 3px 3px;
+    border-style: solid;
+    border-width: 1px;
+}
+
+/* Membership buy buttons */
+.swpm-button-wrapper input[type="submit"]{
+    width: auto !important;
+    height: auto !important;
+}
+.swpm-button-wrapper input[type="image"]{
+    width: auto !important;
+    height: auto !important;    
+}
+
+/* Login form CSS */
+.swpm-login-widget-form input,.swpm-login-widget-form checkbox{
+    width: auto;
+}
+.swpm-username-input, .swpm-password-input{
+    margin-bottom: 10px;
+}
+.swpm-login-submit{
+    margin-bottom: 10px;    
+}
+.swpm-login-widget-action-msg{
+    font-weight: bold;    
+}
+.swpm-logged-label{
+    font-weight: bold;
+}
+
+/* Password reset form CSS */
+.swpm-pw-reset-widget-form table{
+    border: none;
+}
+.swpm-pw-reset-widget-form tr{
+    border: none;
+}
+.swpm-pw-reset-widget-form td{
+    border: none;
+}
+.swpm-reset-pw-error{
+    font-weight: bold;
+    color: red;
+}
+.swpm-reset-pw-success-box{
+    margin: 10px 0px;
+    padding: 15px 10px;
+    background-color: #CCF4D6;
+    border-color: #059B53;
+    color: #043B14;
+    border-radius: 3px 3px 3px 3px;
+    border-style: solid;
+    border-width: 1px;    
+}
+
+/* Registration form CSS */
+.swpm-registration-widget-form td{
+    min-width: 100px;
+}
+
+.swpm-registration-widget-form input[type="text"], .swpm-registration-widget-form input[type="password"]{
+    width: 95%;
+    position: relative;
+}
+
+/* Edit profile form CSS */
+.swpm-edit-profile-form input[type="text"], .swpm-edit-profile-form input[type="password"] {
+    width: 95%;
+}
+.swpm-edit-profile-form select {
+    width: 95%;
+}
+.swpm-edit-profile-submit-section{
+    text-align: center;
+}
+
+.swpm-profile-account-delete-section{
+    text-align: center;
+}
+.swpm-profile-account-delete-section a{
+    color: red !important;
+}
+.swpm-profile-update-success{
+    font-weight: bold;
+    color: green;    
+}
+.swpm-profile-update-error{
+    font-weight: bold;
+    color: red;    
+}
+/* Misc CSS */
+.swpm-restricted{
+    font-weight: bold;
+    color:red;
+}
+.swpm-select-box-left{
+    margin: 0;
+    padding-bottom: 5px;
+}

+ 181 - 0
simple-membership/css/validationEngine.jquery.css

@@ -0,0 +1,181 @@
+
+
+/* Z-INDEX */
+ .formError { z-index: 990; }
+    .formError .formErrorContent { z-index: 991; }
+    .formError .formErrorArrow { z-index: 996; }
+
+    .ui-dialog .formError { z-index: 5000; }
+    .ui-dialog .formError .formErrorContent { z-index: 5001; }
+    .ui-dialog .formError .formErrorArrow { z-index: 5006; }
+
+
+
+
+.inputContainer {
+	position: relative;
+	float: left;
+}
+
+.formError {
+	position: absolute;
+	top: 300px;
+	left: 300px;
+	display: block;
+	cursor: pointer;
+	text-align: left;
+}
+
+.formError.inline {
+	position: relative;
+	top: 0;
+	left: 0;
+	display: inline-block;
+}
+
+.ajaxSubmit {
+	padding: 20px;
+	background: #55ea55;
+	border: 1px solid #999;
+	display: none;
+}
+
+.formError .formErrorContent {
+	width: 100%;
+	background: #ee0101;
+	position:relative;
+	color: #fff;
+	min-width: 120px;
+	font-size: 11px;
+	border: 2px solid #ddd;
+	box-shadow: 0 0 6px #000;
+	-moz-box-shadow: 0 0 6px #000;
+	-webkit-box-shadow: 0 0 6px #000;
+	-o-box-shadow: 0 0 6px #000;
+	padding: 4px 10px 4px 10px;
+	border-radius: 6px;
+	-moz-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	-o-border-radius: 6px;
+}
+
+.formError.inline .formErrorContent {
+	box-shadow: none;
+	-moz-box-shadow: none;
+	-webkit-box-shadow: none;
+	-o-box-shadow: none;
+	border: none;
+	border-radius: 0;
+	-moz-border-radius: 0;
+	-webkit-border-radius: 0;
+	-o-border-radius: 0;
+}
+
+.greenPopup .formErrorContent {
+	background: #33be40;
+}
+
+.blackPopup .formErrorContent {
+	background: #393939;
+	color: #FFF;
+}
+
+.formError .formErrorArrow {
+	width: 15px;
+	margin: -2px 0 0 13px;
+	position:relative;
+}
+body[dir='rtl'] .formError .formErrorArrow,
+body.rtl .formError .formErrorArrow {
+	margin: -2px 13px 0 0;
+}
+
+.formError .formErrorArrowBottom {
+	box-shadow: none;
+	-moz-box-shadow: none;
+	-webkit-box-shadow: none;
+	-o-box-shadow: none;
+	margin: 0px 0 0 12px;
+	top:2px;
+}
+
+.formError .formErrorArrow div {
+	border-left: 2px solid #ddd;
+	border-right: 2px solid #ddd;
+	box-shadow: 0 2px 3px #444;
+	-moz-box-shadow: 0 2px 3px #444;
+	-webkit-box-shadow: 0 2px 3px #444;
+	-o-box-shadow: 0 2px 3px #444;
+	font-size: 0px;
+	height: 1px;
+	background: #ee0101;
+	margin: 0 auto;
+	line-height: 0;
+	font-size: 0;
+	display: block;
+}
+
+.formError .formErrorArrowBottom div {
+	box-shadow: none;
+	-moz-box-shadow: none;
+	-webkit-box-shadow: none;
+	-o-box-shadow: none;
+}
+
+.greenPopup .formErrorArrow div {
+	background: #33be40;
+}
+
+.blackPopup .formErrorArrow div {
+	background: #393939;
+	color: #FFF;
+}
+
+.formError .formErrorArrow .line10 {
+	width: 13px;
+	border: none;
+}
+
+.formError .formErrorArrow .line9 {
+	width: 11px;
+	border: none;
+}
+
+.formError .formErrorArrow .line8 {
+	width: 11px;
+}
+
+.formError .formErrorArrow .line7 {
+	width: 9px;
+}
+
+.formError .formErrorArrow .line6 {
+	width: 7px;
+}
+
+.formError .formErrorArrow .line5 {
+	width: 5px;
+}
+
+.formError .formErrorArrow .line4 {
+	width: 3px;
+}
+
+.formError .formErrorArrow .line3 {
+	width: 1px;
+	border-left: 2px solid #ddd;
+	border-right: 2px solid #ddd;
+	border-bottom: 0 solid #ddd;
+}
+
+.formError .formErrorArrow .line2 {
+	width: 3px;
+	border: none;
+	background: #ddd;
+}
+
+.formError .formErrorArrow .line1 {
+	width: 1px;
+	border: none;
+	background: #ddd;
+}

BIN
simple-membership/images/addons/2fa-addon-icon.png


BIN
simple-membership/images/addons/affiliate-platform-integration.png


BIN
simple-membership/images/addons/affiliates-manager-integration.png


BIN
simple-membership/images/addons/custom-post-type-protection-enhanced.png


BIN
simple-membership/images/addons/email-notification-and-broadcast-addon.png


BIN
simple-membership/images/addons/form-shortcode-generator.png


BIN
simple-membership/images/addons/full-page-protection-addon.png


BIN
simple-membership/images/addons/google-first-click-free-addon.png


BIN
simple-membership/images/addons/google-recaptcha-addon.png


BIN
simple-membership/images/addons/idevaffiliate-integration.png


BIN
simple-membership/images/addons/mailchimp-integration.png


BIN
simple-membership/images/addons/show-member-info.png


BIN
simple-membership/images/addons/swpm-aweber-integration-addon.png


BIN
simple-membership/images/addons/swpm-bbpress-integration.png


BIN
simple-membership/images/addons/swpm-bulk-member-importer-from-csv-addon.png


BIN
simple-membership/images/addons/swpm-convertkit-integration-addon.png


BIN
simple-membership/images/addons/swpm-custom-messages.png


BIN
simple-membership/images/addons/swpm-data-exporter-addon.png


BIN
simple-membership/images/addons/swpm-form-builder.png


BIN
simple-membership/images/addons/swpm-login-redirection.png


BIN
simple-membership/images/addons/swpm-member-directory-listing-addon.png


BIN
simple-membership/images/addons/swpm-member-payments-addon.png


BIN
simple-membership/images/addons/swpm-misc-shortcodes-addon.png


BIN
simple-membership/images/addons/swpm-older-posts-protection.png


BIN
simple-membership/images/addons/swpm-partial-protection-addon.png


BIN
simple-membership/images/addons/swpm-woocommerce-addon.png


BIN
simple-membership/images/addons/wp-user-import.png


+ 0 - 0
simple-membership/images/index.html


BIN
simple-membership/images/join-now-button-image.gif


BIN
simple-membership/images/logo.png


BIN
simple-membership/images/logo2.png


BIN
simple-membership/images/next.gif


BIN
simple-membership/images/prev.gif


BIN
simple-membership/images/restricted-icon.png


BIN
simple-membership/images/simple-membership-content-protection-usage.png


+ 0 - 0
simple-membership/index.html


+ 0 - 0
simple-membership/ipn/index.html


+ 143 - 0
simple-membership/ipn/swpm-braintree-buy-now-ipn.php

@@ -0,0 +1,143 @@
+<?php
+
+include(SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm_handle_subsc_ipn.php');
+
+class SwpmBraintreeBuyNowIpnHandler {
+
+    public function __construct() {
+
+        $this->handle_braintree_ipn();
+    }
+
+    public function handle_braintree_ipn() {
+        SwpmLog::log_simple_debug("Braintree Buy Now IPN received. Processing request...", true);
+        //SwpmLog::log_simple_debug(print_r($_REQUEST, true), true);//Useful for debugging purpose
+        //Include the Braintree library.
+        require_once(SIMPLE_WP_MEMBERSHIP_PATH . 'lib/braintree/lib/autoload.php');
+
+        //Read and sanitize the request parameters.
+        $button_id = filter_input(INPUT_POST, 'item_number', FILTER_SANITIZE_NUMBER_INT);
+        $button_title = filter_input(INPUT_POST, 'item_name', FILTER_SANITIZE_STRING);
+        $payment_amount = filter_input(INPUT_POST, 'item_price', FILTER_SANITIZE_STRING);
+
+        //Retrieve the CPT for this button
+        $button_cpt = get_post($button_id);
+        if (!$button_cpt) {
+            //Fatal error. Could not find this payment button post object.
+            SwpmLog::log_simple_debug("Fatal Error! Failed to retrieve the payment button post object for the given button ID: " . $button_id, false);
+            wp_die("Fatal Error! Payment button (ID: " . $button_id . ") does not exist. This request will fail.");
+        }
+
+        $membership_level_id = get_post_meta($button_id, 'membership_level_id', true);
+
+        //Validate and verify some of the main values.
+        $true_payment_amount = get_post_meta($button_id, 'payment_amount', true);
+        $true_payment_amount = apply_filters('swpm_payment_amount_filter',$true_payment_amount,$button_id);
+        if ($payment_amount != $true_payment_amount) {
+            //Fatal error. Payment amount may have been tampered with.
+            $error_msg = 'Fatal Error! Received payment amount (' . $payment_amount . ') does not match with the original amount (' . $true_payment_amount . ')';
+            SwpmLog::log_simple_debug($error_msg, false);
+            wp_die($error_msg);
+        }
+
+        //Validation passed. Go ahead with the charge.
+        //Sandbox and other settings
+        $settings = SwpmSettings::get_instance();
+        $sandbox_enabled = $settings->get_value('enable-sandbox-testing');
+        if ($sandbox_enabled) {
+            SwpmLog::log_simple_debug("Sandbox payment mode is enabled. Using sandbox enviroment.", true);
+            $braintree_env = "sandbox"; //Use sandbox environment
+        } else {
+            $braintree_env = "production"; //Use production environment
+        }
+
+        //Set Braintree library environment and keys
+        try {
+            Braintree_Configuration::environment($braintree_env);
+            Braintree_Configuration::merchantId(get_post_meta($button_id, 'braintree_merchant_acc_id', true));
+            Braintree_Configuration::publicKey(get_post_meta($button_id, 'braintree_public_key', true));
+            Braintree_Configuration::privateKey(get_post_meta($button_id, 'braintree_private_key', true));
+
+            $braintree_merc_acc_name = get_post_meta($button_id, 'braintree_merchant_acc_name', true);
+
+
+            // Create the charge on Braintree's servers - this will charge the user's card
+
+            $nonce = filter_input(INPUT_POST, 'payment_method_nonce', FILTER_SANITIZE_STRING);
+
+            $result = Braintree_Transaction::sale([
+                        'amount' => $payment_amount,
+                        'paymentMethodNonce' => $nonce,
+                        'channel' => 'TipsandTricks_SP',
+                        'options' => [
+                            'submitForSettlement' => True
+                        ],
+                        'merchantAccountId' => $braintree_merc_acc_name,
+            ]);
+        } catch (Exception $e) {
+            SwpmLog::log_simple_debug("Braintree library error occurred: " . get_class($e) . ", button ID: " . $button_id, false);
+            wp_die('Braintree library error occurred: ' . get_class($e));
+        }
+
+        if (!$result->success) {
+            SwpmLog::log_simple_debug("Braintree transaction error occurred: " . $result->transaction->status . ", button ID: " . $button_id, false);
+            wp_die("Braintree transaction error occurred: " . $result->transaction->status);
+        } else {
+
+            //Everything went ahead smoothly with the charge.
+            SwpmLog::log_simple_debug("Braintree Buy Now charge successful.", true);
+
+            //Grab the transaction ID.
+            $txn_id = $result->transaction->id; //$charge->balance_transaction;
+
+            $custom = filter_input(INPUT_POST, 'custom', FILTER_SANITIZE_STRING);
+            $custom_var = SwpmTransactions::parse_custom_var($custom);
+            $swpm_id = isset($custom_var['swpm_id']) ? $custom_var['swpm_id'] : '';
+
+            //Create the $ipn_data array.
+            $ipn_data = array();
+            $ipn_data['mc_gross'] = $payment_amount;
+            $ipn_data['first_name'] = filter_input(INPUT_POST, 'first_name', FILTER_SANITIZE_STRING);
+            $ipn_data['last_name'] = filter_input(INPUT_POST, 'last_name', FILTER_SANITIZE_STRING);
+            $ipn_data['payer_email'] = filter_input(INPUT_POST, 'member_email', FILTER_SANITIZE_EMAIL);
+            $ipn_data['membership_level'] = $membership_level_id;
+            $ipn_data['txn_id'] = $txn_id;
+            $ipn_data['subscr_id'] = $txn_id;
+            $ipn_data['swpm_id'] = $swpm_id;
+            $ipn_data['ip'] = $custom_var['user_ip'];
+            $ipn_data['custom'] = $custom;
+            $ipn_data['gateway'] = 'braintree';
+            $ipn_data['status'] = 'completed';
+
+            $ipn_data['address_street'] = '';
+            $ipn_data['address_city'] = '';
+            $ipn_data['address_state'] = '';
+            $ipn_data['address_zipcode'] = '';
+            $ipn_data['country'] = '';
+
+            //Handle the membership signup related tasks.
+            swpm_handle_subsc_signup_stand_alone($ipn_data, $membership_level_id, $txn_id, $swpm_id);
+
+            //Save the transaction record
+            SwpmTransactions::save_txn_record($ipn_data);
+            SwpmLog::log_simple_debug('Transaction data saved.', true);
+
+            //Trigger the stripe IPN processed action hook (so other plugins can can listen for this event).
+            do_action('swpm_braintree_ipn_processed', $ipn_data);
+
+            do_action('swpm_payment_ipn_processed', $ipn_data);
+
+            //Redirect the user to the return URL (or to the homepage if a return URL is not specified for this payment button).
+            $return_url = get_post_meta($button_id, 'return_url', true);
+            if (empty($return_url)) {
+                $return_url = SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL;
+            }
+            SwpmLog::log_simple_debug("Redirecting customer to: " . $return_url, true);
+            SwpmLog::log_simple_debug("End of Braintree Buy Now IPN processing.", true, true);
+            SwpmMiscUtils::redirect_to_url($return_url);
+        }
+    }
+
+}
+
+$swpm_braintree_buy_ipn = new SwpmBraintreeBuyNowIpnHandler();

+ 410 - 0
simple-membership/ipn/swpm-smart-checkout-ipn.php

@@ -0,0 +1,410 @@
+<?php
+
+require_once 'swpm_handle_subsc_ipn.php';
+// Ignoring invalid class name PHPCS warning
+class swpm_smart_checkout_ipn_handler { // phpcs:ignore
+
+	public $ipn_log = false;                    // bool: log IPN results to text file?
+	public $ipn_log_file;               // filename of the IPN log.
+	public $ipn_response;               // holds the IPN response from paypal.
+	public $ipn_data     = array();         // array contains the POST values for IPN.
+	public $fields       = array();           // array holds the fields to submit to paypal.
+	public $sandbox_mode = false;
+
+	public function __construct() {
+		$this->paypal_url   = 'https://www.paypal.com/cgi-bin/webscr';
+		$this->ipn_log_file = 'ipn_handle_debug_swpm.log';
+		$this->ipn_response = '';
+	}
+
+	public function swpm_validate_and_create_membership() {
+		// Check Product Name , Price , Currency , Receivers email.
+		$error_msg = '';
+
+		// Read the IPN and validate.
+		$gross_total      = $this->ipn_data['mc_gross'];
+		$transaction_type = $this->ipn_data['txn_type'];
+		$txn_id           = $this->ipn_data['txn_id'];
+		$payment_status   = $this->ipn_data['payment_status'];
+
+		// Check payment status.
+		if ( ! empty( $payment_status ) ) {
+			if ( 'Denied' == $payment_status ) {
+				$this->debug_log( 'Payment status for this transaction is DENIED. You denied the transaction... most likely a cancellation of an eCheque. Nothing to do here.', false );
+				return false;
+			}
+			if ( 'Canceled_Reversal' == $payment_status ) {
+				$this->debug_log( 'This is a dispute closed notification in your favour. The plugin will not do anyting.', false );
+				return true;
+			}
+			if ( 'Completed' != $payment_status && 'Processed' != $payment_status && 'Refunded' != $payment_status && 'Reversed' != $payment_status ) {
+				$error_msg .= 'Funds have not been cleared yet. Transaction will be processed when the funds clear!';
+				$this->debug_log( $error_msg, false );
+				$this->debug_log( wp_json_encode( $this->ipn_data ), false );
+				return false;
+			}
+		}
+
+		// Check txn type.
+		if ( 'new_case' == $transaction_type ) {
+			$this->debug_log( 'This is a dispute case. Nothing to do here.', true );
+			return true;
+		}
+
+		$custom                   = urldecode( $this->ipn_data['custom'] );
+		$this->ipn_data['custom'] = $custom;
+		$customvariables          = SwpmTransactions::parse_custom_var( $custom );
+
+		// Handle refunds.
+		if ( $gross_total < 0 ) {
+			// This is a refund or reversal.
+			$this->debug_log( 'This is a refund notification. Refund amount: ' . $gross_total, true );
+			swpm_handle_subsc_cancel_stand_alone( $this->ipn_data, true );
+			return true;
+		}
+		if ( isset( $this->ipn_data['reason_code'] ) && 'refund' == $this->ipn_data['reason_code'] ) {
+			$this->debug_log( 'This is a refund notification. Refund amount: ' . $gross_total, true );
+			swpm_handle_subsc_cancel_stand_alone( $this->ipn_data, true );
+			return true;
+		}
+
+		if ( ( 'subscr_signup' == $transaction_type ) ) {
+			$this->debug_log( 'Subscription signup IPN received... (handled by the subscription IPN handler)', true );
+			// Code to handle the signup IPN for subscription.
+			$subsc_ref = $customvariables['subsc_ref'];
+
+			if ( ! empty( $subsc_ref ) ) {
+				$this->debug_log( 'Found a membership level ID. Creating member account...', true );
+				$swpm_id = $customvariables['swpm_id'];
+				swpm_handle_subsc_signup_stand_alone( $this->ipn_data, $subsc_ref, $this->ipn_data['subscr_id'], $swpm_id );
+				// Handle customized subscription signup.
+			}
+			return true;
+		} elseif ( ( 'subscr_cancel' == $transaction_type ) || ( 'subscr_eot' == $transaction_type ) || ( 'subscr_failed' == $transaction_type ) ) {
+			// Code to handle the IPN for subscription cancellation.
+			$this->debug_log( 'Subscription cancellation IPN received... (handled by the subscription IPN handler)', true );
+			swpm_handle_subsc_cancel_stand_alone( $this->ipn_data );
+			return true;
+		} else {
+			$cart_items = array();
+			$this->debug_log( 'Transaction Type: Buy Now/Subscribe', true );
+			$item_number = $this->ipn_data['item_number'];
+			$item_name   = $this->ipn_data['item_name'];
+			$quantity    = $this->ipn_data['quantity'];
+			$mc_gross    = $this->ipn_data['mc_gross'];
+			$mc_currency = $this->ipn_data['mc_currency'];
+
+			$current_item = array(
+				'item_number' => $item_number,
+				'item_name'   => $item_name,
+				'quantity'    => $quantity,
+				'mc_gross'    => $mc_gross,
+				'mc_currency' => $mc_currency,
+			);
+
+			array_push( $cart_items, $current_item );
+		}
+
+		$counter = 0;
+		foreach ( $cart_items as $current_cart_item ) {
+			$cart_item_data_num      = $current_cart_item['item_number'];
+			$cart_item_data_name     = trim( $current_cart_item['item_name'] );
+			$cart_item_data_quantity = $current_cart_item['quantity'];
+			$cart_item_data_total    = $current_cart_item['mc_gross'];
+			$cart_item_data_currency = $current_cart_item['mc_currency'];
+			if ( empty( $cart_item_data_quantity ) ) {
+				$cart_item_data_quantity = 1;
+			}
+			$this->debug_log( 'Item Number: ' . $cart_item_data_num, true );
+			$this->debug_log( 'Item Name: ' . $cart_item_data_name, true );
+			$this->debug_log( 'Item Quantity: ' . $cart_item_data_quantity, true );
+			$this->debug_log( 'Item Total: ' . $cart_item_data_total, true );
+			$this->debug_log( 'Item Currency: ' . $cart_item_data_currency, true );
+
+			// Get the button id.
+			$pp_hosted_button    = false;
+			$button_id           = $cart_item_data_num; // Button id is the item number.
+			$membership_level_id = get_post_meta( $button_id, 'membership_level_id', true );
+			if ( ! SwpmUtils::membership_level_id_exists( $membership_level_id ) ) {
+				$this->debug_log( 'This payment button was not created in the plugin. This is a paypal hosted button.', true );
+				$pp_hosted_button = true;
+			}
+
+			// Price check.
+			$check_price = true;
+			$msg         = '';
+			$msg         = apply_filters( 'swpm_before_price_check_filter', $msg, $current_cart_item );
+			if ( ! empty( $msg ) && 'price-check-override' == $msg ) {// This filter allows an extension to do a customized version of price check (if needed).
+				$check_price = false;
+				$this->debug_log( 'Price and currency check has been overridden by an addon/extension.', true );
+			}
+			if ( $check_price && ! $pp_hosted_button ) {
+				// Check according to buy now payment or subscription payment.
+				$button_type = get_post_meta( $button_id, 'button_type', true );
+				if ( 'pp_smart_checkout' == $button_type ) {// This is a PayPal Smart Checkout type button.
+					$expected_amount = ( get_post_meta( $button_id, 'payment_amount', true ) ) * $cart_item_data_quantity;
+					$expected_amount = round( $expected_amount, 2 );
+					$expected_amount = apply_filters( 'swpm_payment_amount_filter', $expected_amount, $button_id );
+					$received_amount = $cart_item_data_total;
+				} else {
+					$this->debug_log( 'Error! Unexpected button type: ' . $button_type, false );
+					return false;
+				}
+
+				if ( $received_amount < $expected_amount ) {
+					// Error! amount received is less than expected. This is invalid.
+					$this->debug_log( 'Expected amount: ' . $expected_amount, true );
+					$this->debug_log( 'Received amount: ' . $received_amount, true );
+					$this->debug_log( 'Price check failed. Amount received is less than the amount expected. This payment will not be processed.', false );
+					return false;
+				}
+			}
+
+			// *** Handle Membership Payment ***
+			// --------------------------------------------------------------------------------------
+			// ========= Need to find the (level ID) in the custom variable ============
+			$subsc_ref = $customvariables['subsc_ref']; // Membership level ID.
+			$this->debug_log( 'Membership payment paid for membership level ID: ' . $subsc_ref, true );
+			if ( ! empty( $subsc_ref ) ) {
+				$swpm_id = '';
+				if ( isset( $customvariables['swpm_id'] ) ) {
+					$swpm_id = $customvariables['swpm_id'];
+				}
+				if ( 'smart_checkout' == $transaction_type ) {
+					$this->debug_log( 'Transaction type: web_accept. Creating member account...', true );
+					swpm_handle_subsc_signup_stand_alone( $this->ipn_data, $subsc_ref, $this->ipn_data['txn_id'], $swpm_id );
+				}
+			} else {
+				$this->debug_log( 'Membership level ID is missing in the payment notification! Cannot process this notification.', false );
+			}
+			// == End of Membership payment handling ==
+			$counter++;
+		}
+
+		/*
+				 * * Do Post payment operation and cleanup * *
+		*/
+		// Save the transaction data.
+		$this->debug_log( 'Saving transaction data to the database table.', true );
+		$this->ipn_data['gateway'] = 'pp_smart_checkout';
+		$this->ipn_data['status']  = $this->ipn_data['payment_status'];
+		SwpmTransactions::save_txn_record( $this->ipn_data, $cart_items );
+		$this->debug_log( 'Transaction data saved.', true );
+
+		// Trigger the PayPal IPN processed action hook (so other plugins can can listen for this event).
+		do_action( 'swpm_pp_smart_checkout_ipn_processed', $this->ipn_data );
+
+		do_action( 'swpm_payment_ipn_processed', $this->ipn_data );
+
+		return true;
+	}
+
+	public function create_ipn_from_smart_checkout( $data ) {
+		$ipn['custom']              = $data['custom_field'];
+		$ipn['item_number']         = $data['button_id'];
+		$ipn['item_name']           = $data['item_name'];
+		$ipn['pay_id']              = $data['id'];
+		$ipn['create_time']         = $data['create_time'];
+		$ipn['txn_id']              = $data['transactions'][0]['related_resources'][0]['sale']['id'];
+		$ipn['reason_code']         = ! empty( $data['transactions'][0]['related_resources'][0]['sale']['reason_code'] ) ? $data['transactions'][0]['related_resources'][0]['sale']['reason_code'] : '';
+		$ipn['txn_type']            = 'smart_checkout';
+		$ipn['payment_status']      = ucfirst( $data['transactions'][0]['related_resources'][0]['sale']['state'] );
+		$ipn['transaction_subject'] = '';
+		$ipn['mc_currency']         = $data['transactions'][0]['amount']['currency'];
+		$ipn['mc_gross']            = $data['transactions'][0]['amount']['total'];
+		$ipn['quantity']            = 1;
+		$ipn['receiver_email']      = get_option( 'cart_paypal_email' );
+		// customer info.
+		$ipn['first_name']      = $data['payer']['payer_info']['first_name'];
+		$ipn['last_name']       = $data['payer']['payer_info']['last_name'];
+		$ipn['payer_email']     = $data['payer']['payer_info']['email'];
+		$ipn['address_street']  = $data['payer']['payer_info']['shipping_address']['line1'];
+		$ipn['address_city']    = $data['payer']['payer_info']['shipping_address']['city'];
+		$ipn['address_state']   = $data['payer']['payer_info']['shipping_address']['state'];
+		$ipn['address_zip']     = $data['payer']['payer_info']['shipping_address']['postal_code'];
+		$ipn['address_country'] = $data['payer']['payer_info']['shipping_address']['country_code'];
+
+		$this->ipn_data = $ipn;
+		return true;
+	}
+
+	public function validate_ipn_smart_checkout() {
+
+		if ( $this->sandbox_mode ) {
+			$client_id = get_post_meta( $this->ipn_data['item_number'], 'pp_smart_checkout_test_id', true );
+			$secret    = get_post_meta( $this->ipn_data['item_number'], 'pp_smart_checkout_test_sec', true );
+			$api_base  = 'https://api.sandbox.paypal.com';
+		} else {
+			$client_id = get_post_meta( $this->ipn_data['item_number'], 'pp_smart_checkout_live_id', true );
+			$secret    = get_post_meta( $this->ipn_data['item_number'], 'pp_smart_checkout_live_sec', true );
+			$api_base  = 'https://api.paypal.com';
+		}
+
+		$wp_request_headers = array(
+			'Accept'        => 'application/json',
+			// Ignoring base64_encode() PHPCS warning as it's being properly used in this case.
+			'Authorization' => 'Basic ' . base64_encode( $client_id . ':' . $secret ), // phpcs:ignore
+		);
+
+		$res = wp_remote_request(
+			$api_base . '/v1/oauth2/token',
+			array(
+				'method'  => 'POST',
+				'headers' => $wp_request_headers,
+				'body'    => 'grant_type=client_credentials',
+			)
+		);
+
+		$code = wp_remote_retrieve_response_code( $res );
+
+		if ( 200 !== $code ) {
+			// Some error occured.
+			$body = wp_remote_retrieve_body( $res );
+			// translators: %1$d is error code; %2$s is error message.
+			return sprintf( __( 'Error occured during payment verification. Error code: %1$d. Message: %2$s', 'simple-membership' ), $code, $body );
+		}
+
+		$body = wp_remote_retrieve_body( $res );
+		$body = json_decode( $body );
+
+		$token = $body->access_token;
+
+		$wp_request_headers = array(
+			'Accept'        => 'application/json',
+			'Authorization' => 'Bearer ' . $token,
+		);
+
+		$res = wp_remote_request(
+			$api_base . '/v1/payments/payment/' . $this->ipn_data['pay_id'],
+			array(
+				'method'  => 'GET',
+				'headers' => $wp_request_headers,
+			)
+		);
+
+		$code = wp_remote_retrieve_response_code( $res );
+
+		if ( 200 !== $code ) {
+			// Some error occured.
+			$body = wp_remote_retrieve_body( $res );
+			// translators: %1$d is error code; %2$s is error message.
+			return sprintf( __( 'Error occured during payment verification. Error code: %1$d. Message: %2$s', 'simple-membership' ), $code, $body );
+		}
+
+		$body = wp_remote_retrieve_body( $res );
+		$body = json_decode( $body );
+
+		// check payment details.
+		if ( $body->transactions[0]->amount->total === $this->ipn_data['mc_gross'] &&
+				$body->transactions[0]->amount->currency === $this->ipn_data['mc_currency'] ) {
+			// payment is valid.
+			return true;
+		} else {
+			// payment is invalid.
+			// translators:  %1$s is expected amount, %2$s is expected currency.
+			return sprintf( __( 'Payment check failed: invalid amount received. Expected %1$s %2$s, got %3$s %4$s.', 'simple-membership' ), $this->ipn_data['mc_gross'], $this->ipn_data['mc_currency'], $body->transactions[0]->amount->total, $body->transactions[0]->amount->currency );
+		}
+	}
+
+	public function debug_log( $message, $success, $end = false ) {
+		SwpmLog::log_simple_debug( $message, $success, $end );
+	}
+
+}
+
+function swpm_pp_smart_checkout_ajax_hanlder() {
+	// Start of IPN handling (script execution).
+
+	// check nonce.
+	$uniqid = filter_input( INPUT_POST, 'uniqid', FILTER_SANITIZE_STRING );
+
+	if ( ! check_ajax_referer( 'swpm-pp-smart-checkout-ajax-nonce-' . $uniqid, 'nonce', false ) ) {
+		wp_send_json(
+			array(
+				'success' => false,
+				'errMsg'  => __(
+					'Nonce check failed. Please reload page.',
+					'simple-membership'
+				),
+			)
+		);
+		exit;
+	}
+
+	$data = filter_input( INPUT_POST, 'swpm_pp_smart_checkout_payment_data', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
+
+	if ( empty( $data ) ) {
+		wp_send_json(
+			array(
+				'success' => false,
+				'errMsg'  => __(
+					'Empty payment data received.',
+					'simple-membership'
+				),
+			)
+		);
+	}
+
+	$ipn_handler_instance = new swpm_smart_checkout_ipn_handler();
+
+	$ipn_data_success = $ipn_handler_instance->create_ipn_from_smart_checkout( $data );
+
+	if ( true !== $ipn_data_success ) {
+		// error occured during IPN array creation.
+		wp_send_json(
+			array(
+				'success' => false,
+				'errMsg'  => $ipn_data_success,
+			)
+		);
+	}
+
+	$settings      = SwpmSettings::get_instance();
+	$debug_enabled = $settings->get_value( 'enable-debug' );
+	if ( ! empty( $debug_enabled ) ) {// debug is enabled in the system.
+		$debug_log                          = 'log.txt'; // Debug log file name.
+		$ipn_handler_instance->ipn_log      = true;
+		$ipn_handler_instance->ipn_log_file = $debug_log;
+	}
+
+	$sandbox_enabled = $settings->get_value( 'enable-sandbox-testing' );
+	if ( ! empty( $sandbox_enabled ) ) { // Sandbox testing enabled.
+		$ipn_handler_instance->paypal_url   = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
+		$ipn_handler_instance->sandbox_mode = true;
+	}
+
+	$ip = filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_SANITIZE_STRING );
+
+	$ipn_handler_instance->debug_log( 'Paypal Smart Checkout Class Initiated by ' . $ip, true );
+
+	// Validate the IPN.
+	$res = $ipn_handler_instance->validate_ipn_smart_checkout();
+
+	if ( true !== $res ) {
+		wp_send_json(
+			array(
+				'success' => false,
+				'errMsg'  => $res,
+			)
+		);
+	}
+
+	$ipn_handler_instance->debug_log( 'Creating product Information to send.', true );
+
+	if ( ! $ipn_handler_instance->swpm_validate_and_create_membership() ) {
+		$ipn_handler_instance->debug_log( 'IPN product validation failed.', false );
+		wp_send_json(
+			array(
+				'success' => false,
+				'errMsg'  => __(
+					'IPN product validation failed. Check debug log for more details.',
+					'simple-membership'
+				),
+			)
+		);
+	}
+
+	$ipn_handler_instance->debug_log( 'Paypal class finished.', true, true );
+
+	wp_send_json( array( 'success' => true ) );
+}

+ 155 - 0
simple-membership/ipn/swpm-stripe-buy-now-ipn.php

@@ -0,0 +1,155 @@
+<?php
+
+require SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm_handle_subsc_ipn.php';
+
+class SwpmStripeBuyNowIpnHandler {
+
+	public function __construct() {
+
+		$this->handle_stripe_ipn();
+	}
+
+	public function handle_stripe_ipn() {
+		SwpmLog::log_simple_debug( 'Stripe Buy Now IPN received. Processing request...', true );
+		// SwpmLog::log_simple_debug(print_r($_REQUEST, true), true);//Useful for debugging purpose
+
+		// Include the Stripe library.
+		SwpmMiscUtils::load_stripe_lib();
+
+		// Read and sanitize the request parameters.
+		$button_id      = sanitize_text_field( $_REQUEST['item_number'] );
+		$button_id      = absint( $button_id );
+		$button_title   = sanitize_text_field( $_REQUEST['item_name'] );
+		$payment_amount = sanitize_text_field( $_REQUEST['item_price'] );
+		$currency_code  = sanitize_text_field( $_REQUEST['currency_code'] );
+		$zero_cents     = unserialize( SIMPLE_WP_MEMBERSHIP_STRIPE_ZERO_CENTS );
+		if ( in_array( $currency_code, $zero_cents ) ) {
+			$price_in_cents = $payment_amount;
+		} else {
+			$price_in_cents = $payment_amount * 100;// The amount (in cents). This value is used in Stripe API.
+		}
+		$stripe_token      = filter_input( INPUT_POST, 'stripeToken', FILTER_SANITIZE_STRING );
+		$stripe_token_type = filter_input( INPUT_POST, 'stripeTokenType', FILTER_SANITIZE_STRING );
+		$stripe_email      = filter_input( INPUT_POST, 'stripeEmail', FILTER_SANITIZE_EMAIL );
+
+		// Retrieve the CPT for this button
+		$button_cpt = get_post( $button_id );
+		if ( ! $button_cpt ) {
+			// Fatal error. Could not find this payment button post object.
+			SwpmLog::log_simple_debug( 'Fatal Error! Failed to retrieve the payment button post object for the given button ID: ' . $button_id, false );
+			wp_die( esc_html( sprintf( 'Fatal Error! Payment button (ID: %d) does not exist. This request will fail.', $button_id ) ) );
+		}
+
+		$membership_level_id = get_post_meta( $button_id, 'membership_level_id', true );
+
+		// Validate and verify some of the main values.
+		$true_payment_amount = get_post_meta( $button_id, 'payment_amount', true );
+		$true_payment_amount = apply_filters( 'swpm_payment_amount_filter', $true_payment_amount, $button_id );
+
+		if ( $payment_amount != $true_payment_amount ) {
+			// Fatal error. Payment amount may have been tampered with.
+			$error_msg = 'Fatal Error! Received payment amount (' . $payment_amount . ') does not match with the original amount (' . $true_payment_amount . ')';
+			SwpmLog::log_simple_debug( $error_msg, false );
+			wp_die( esc_html( $error_msg ) );
+		}
+		$true_currency_code = get_post_meta( $button_id, 'payment_currency', true );
+		if ( $currency_code != $true_currency_code ) {
+			// Fatal error. Currency code may have been tampered with.
+			$error_msg = 'Fatal Error! Received currency code (' . $currency_code . ') does not match with the original code (' . $true_currency_code . ')';
+			SwpmLog::log_simple_debug( $error_msg, false );
+			wp_die( esc_html( $error_msg ) );
+		}
+
+		// Validation passed. Go ahead with the charge.
+
+		// Sandbox and other settings
+		$settings        = SwpmSettings::get_instance();
+		$sandbox_enabled = $settings->get_value( 'enable-sandbox-testing' );
+
+		//API keys
+		$api_keys = SwpmMiscUtils::get_stripe_api_keys_from_payment_button( $button_id, ! $sandbox_enabled );
+
+		// Set secret API key in the Stripe library
+		\Stripe\Stripe::setApiKey( $api_keys['secret'] );
+
+		// Get the credit card details submitted by the form
+		$token = $stripe_token;
+
+		// Create the charge on Stripe's servers - this will charge the user's card
+		try {
+			$charge = \Stripe\Charge::create(
+				array(
+					'amount'        => $price_in_cents, // Amount in cents
+					'currency'      => strtolower( $currency_code ),
+					'source'        => $token,
+					'description'   => $button_title,
+					'receipt_email' => $stripe_email,
+				)
+			);
+		} catch ( \Stripe\Error\Card $e ) {
+			// The card has been declined
+			SwpmLog::log_simple_debug( 'Stripe Charge Error! The card has been declined. ' . $e->getMessage(), false );
+			$body         = $e->getJsonBody();
+			$error        = $body['error'];
+			$error_string = print_r( $error, true );
+			SwpmLog::log_simple_debug( 'Error details: ' . $error_string, false );
+			wp_die( esc_html( 'Stripe Charge Error! Card charge has been declined. ' . $e->getMessage() . $error_string ) );
+		}
+
+		// Everything went ahead smoothly with the charge.
+		SwpmLog::log_simple_debug( 'Stripe Buy Now charge successful.', true );
+
+		// Grab the charge ID and set it as the transaction ID.
+		$txn_id = $charge->id;// $charge->balance_transaction;
+		// The charge ID can be used to retrieve the transaction details using hte following call.
+		// \Stripe\Charge::retrieve($charge->id);
+		$custom     = sanitize_text_field( $_REQUEST['custom'] );
+		$custom_var = SwpmTransactions::parse_custom_var( $custom );
+		$swpm_id    = isset( $custom_var['swpm_id'] ) ? $custom_var['swpm_id'] : '';
+
+		// Create the $ipn_data array.
+		$ipn_data                     = array();
+		$ipn_data['mc_gross']         = $payment_amount;
+		$ipn_data['first_name']       = '';
+		$ipn_data['last_name']        = '';
+		$ipn_data['payer_email']      = $stripe_email;
+		$ipn_data['membership_level'] = $membership_level_id;
+		$ipn_data['txn_id']           = $txn_id;
+		$ipn_data['subscr_id']        = $txn_id;/* Set the txn_id as subscriber_id so it is similar to PayPal buy now. Also, it can connect to the profile in the "payments" menu. */
+		$ipn_data['swpm_id']          = $swpm_id;
+		$ipn_data['ip']               = $custom_var['user_ip'];
+		$ipn_data['custom']           = $custom;
+		$ipn_data['gateway']          = 'stripe';
+		$ipn_data['status']           = 'completed';
+
+		$ipn_data['address_street']  = '';
+		$ipn_data['address_city']    = '';
+		$ipn_data['address_state']   = '';
+		$ipn_data['address_zipcode'] = '';
+		$ipn_data['country']         = '';
+
+		// Handle the membership signup related tasks.
+		swpm_handle_subsc_signup_stand_alone( $ipn_data, $membership_level_id, $txn_id, $swpm_id );
+
+		// Save the transaction record
+		SwpmTransactions::save_txn_record( $ipn_data );
+		SwpmLog::log_simple_debug( 'Transaction data saved.', true );
+
+		// Trigger the stripe IPN processed action hook (so other plugins can can listen for this event).
+		do_action( 'swpm_stripe_ipn_processed', $ipn_data );
+
+		do_action( 'swpm_payment_ipn_processed', $ipn_data );
+
+		// Redirect the user to the return URL (or to the homepage if a return URL is not specified for this payment button).
+		$return_url = get_post_meta( $button_id, 'return_url', true );
+		if ( empty( $return_url ) ) {
+			$return_url = SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL;
+		}
+		SwpmLog::log_simple_debug( 'Redirecting customer to: ' . $return_url, true );
+		SwpmLog::log_simple_debug( 'End of Stripe Buy Now IPN processing.', true, true );
+		SwpmMiscUtils::redirect_to_url( $return_url );
+
+	}
+}
+
+$swpm_stripe_buy_ipn = new SwpmStripeBuyNowIpnHandler();

+ 381 - 0
simple-membership/ipn/swpm-stripe-sca-buy-now-ipn.php

@@ -0,0 +1,381 @@
+<?php
+
+class SwpmStripeSCABuyNowIpnHandler {
+
+	public function __construct() {
+		//check if this is session create request
+		if ( wp_doing_ajax() ) {
+			$action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_STRING );
+			if ( 'swpm_stripe_sca_create_checkout_session' === $action ) {
+				add_action( 'wp_ajax_swpm_stripe_sca_create_checkout_session', array( $this, 'handle_session_create' ) );
+				add_action( 'wp_ajax_nopriv_swpm_stripe_sca_create_checkout_session', array( $this, 'handle_session_create' ) );
+			}
+			return;
+		}
+
+		require_once SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm_handle_subsc_ipn.php';
+		$this->handle_stripe_ipn();
+	}
+
+	public function handle_stripe_ipn() {
+		SwpmLog::log_simple_debug( 'Stripe SCA Buy Now IPN received. Processing request...', true );
+		// SwpmLog::log_simple_debug(print_r($_REQUEST, true), true);//Useful for debugging purpose
+
+		// Read and sanitize the request parameters.
+
+		$ref_id = filter_input( INPUT_GET, 'ref_id', FILTER_SANITIZE_STRING );
+
+		if ( empty( $ref_id ) ) {
+			//no ref id provided, cannot proceed
+			SwpmLog::log_simple_debug( 'Fatal Error! No ref_id provied.', false );
+			wp_die( esc_html( 'Fatal Error! No ref_id provied.' ) );
+
+		}
+
+		$trans_info = explode( '|', $ref_id );
+		$button_id  = isset( $trans_info[1] ) ? absint( $trans_info[1] ) : false;
+
+		// Retrieve the CPT for this button
+		$button_cpt = get_post( $button_id );
+		if ( ! $button_cpt ) {
+			// Fatal error. Could not find this payment button post object.
+			SwpmLog::log_simple_debug( 'Fatal Error! Failed to retrieve the payment button post object for the given button ID: ' . $button_id, false );
+			wp_die( esc_html( sprintf( 'Fatal Error! Payment button (ID: %d) does not exist. This request will fail.', $button_id ) ) );
+		}
+
+		$settings        = SwpmSettings::get_instance();
+		$sandbox_enabled = $settings->get_value( 'enable-sandbox-testing' );
+
+		//API keys
+		$api_keys = SwpmMiscUtils::get_stripe_api_keys_from_payment_button( $button_id, ! $sandbox_enabled );
+
+		// Include the Stripe library.
+		SwpmMiscUtils::load_stripe_lib();
+
+		try {
+			\Stripe\Stripe::setApiKey( $api_keys['secret'] );
+
+			$events = \Stripe\Event::all(
+				array(
+					'type'    => 'checkout.session.completed',
+					'created' => array(
+						'gte' => time() - 60 * 60,
+					),
+				)
+			);
+
+			$sess = false;
+
+			foreach ( $events->autoPagingIterator() as $event ) {
+				$session = $event->data->object;
+				if ( isset( $session->client_reference_id ) && $session->client_reference_id === $ref_id ) {
+					$sess = $session;
+					break;
+				}
+			}
+
+			if ( false === $sess ) {
+				// Can't find session.
+				$error_msg = sprintf( "Fatal error! Payment with ref_id %s can't be found", $ref_id );
+				SwpmLog::log_simple_debug( $error_msg, false );
+				wp_die( esc_html( $error_msg ) );
+			}
+
+			$pi_id = $sess->payment_intent;
+
+			$pi = \Stripe\PaymentIntent::retrieve( $pi_id );
+		} catch ( Exception $e ) {
+			$error_msg = 'Error occurred: ' . $e->getMessage();
+			SwpmLog::log_simple_debug( $error_msg, false );
+			wp_die( esc_html( $error_msg ) );
+		}
+
+		$charge = $pi->charges;
+
+		// Grab the charge ID and set it as the transaction ID.
+		$txn_id = $charge->data[0]->id;
+		// The charge ID can be used to retrieve the transaction details using hte following call.
+		// \Stripe\Charge::retrieve($charge->$data[0]->id);
+
+		//check if this payment has already been processed
+		$payment = get_posts(
+			array(
+				'meta_key'       => 'txn_id',
+				'meta_value'     => $txn_id,
+				'posts_per_page' => 1,
+				'offset'         => 0,
+				'post_type'      => 'swpm_transactions',
+			)
+		);
+		wp_reset_postdata();
+
+		if ( $payment ) {
+			//payment has already been processed. Redirecting user to return_url
+			$return_url = get_post_meta( $button_id, 'return_url', true );
+			if ( empty( $return_url ) ) {
+				$return_url = SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL;
+			}
+			SwpmMiscUtils::redirect_to_url( $return_url );
+			return;
+		}
+
+		$price_in_cents = floatval( $pi->amount_received );
+		$currency_code  = strtoupper( $pi->currency );
+
+		$zero_cents = unserialize( SIMPLE_WP_MEMBERSHIP_STRIPE_ZERO_CENTS );
+		if ( in_array( $currency_code, $zero_cents, true ) ) {
+			$payment_amount = $price_in_cents;
+		} else {
+			$payment_amount = $price_in_cents / 100;// The amount (in cents). This value is used in Stripe API.
+		}
+
+		$payment_amount = floatval( $payment_amount );
+
+		$stripe_email = $charge->data[0]->billing_details->email;
+
+		$membership_level_id = get_post_meta( $button_id, 'membership_level_id', true );
+
+		// Validate and verify some of the main values.
+		$true_payment_amount = get_post_meta( $button_id, 'payment_amount', true );
+		$true_payment_amount = apply_filters( 'swpm_payment_amount_filter', $true_payment_amount, $button_id );
+		$true_payment_amount = floatval( $true_payment_amount );
+
+		if ( $payment_amount !== $true_payment_amount ) {
+			// Fatal error. Payment amount may have been tampered with.
+			$error_msg = 'Fatal Error! Received payment amount (' . $payment_amount . ') does not match with the original amount (' . $true_payment_amount . ')';
+			SwpmLog::log_simple_debug( $error_msg, false );
+			wp_die( esc_html( $error_msg ) );
+		}
+		$true_currency_code = get_post_meta( $button_id, 'payment_currency', true );
+		if ( $currency_code !== $true_currency_code ) {
+			// Fatal error. Currency code may have been tampered with.
+			$error_msg = 'Fatal Error! Received currency code (' . $currency_code . ') does not match with the original code (' . $true_currency_code . ')';
+			SwpmLog::log_simple_debug( $error_msg, false );
+			wp_die( esc_html( $error_msg ) );
+		}
+
+		// Everything went ahead smoothly with the charge.
+		SwpmLog::log_simple_debug( 'Stripe SCA Buy Now charge successful.', true );
+
+		$user_ip = SwpmUtils::get_user_ip_address();
+
+		//Custom field data
+		$custom_field_value  = 'subsc_ref=' . $membership_level_id;
+		$custom_field_value .= '&user_ip=' . $user_ip;
+		if ( SwpmMemberUtils::is_member_logged_in() ) {
+			$custom_field_value .= '&swpm_id=' . SwpmMemberUtils::get_logged_in_members_id();
+		}
+		$custom_field_value = apply_filters( 'swpm_custom_field_value_filter', $custom_field_value );
+
+		$custom = $custom_field_value;
+
+		$custom_var = SwpmTransactions::parse_custom_var( $custom );
+		$swpm_id    = isset( $custom_var['swpm_id'] ) ? $custom_var['swpm_id'] : '';
+
+		// Let's try to get first_name and last_name from full name
+		$name       = trim( $charge->data[0]->billing_details->name );
+		$last_name  = ( strpos( $name, ' ' ) === false ) ? '' : preg_replace( '#.*\s([\w-]*)$#', '$1', $name );
+		$first_name = trim( preg_replace( '#' . $last_name . '#', '', $name ) );
+
+		// Create the $ipn_data array.
+		$ipn_data                     = array();
+		$ipn_data['mc_gross']         = $payment_amount;
+		$ipn_data['first_name']       = $first_name;
+		$ipn_data['last_name']        = $last_name;
+		$ipn_data['payer_email']      = $stripe_email;
+		$ipn_data['membership_level'] = $membership_level_id;
+		$ipn_data['txn_id']           = $txn_id;
+		$ipn_data['subscr_id']        = $txn_id;/* Set the txn_id as subscriber_id so it is similar to PayPal buy now. Also, it can connect to the profile in the "payments" menu. */
+		$ipn_data['swpm_id']          = $swpm_id;
+		$ipn_data['ip']               = $custom_var['user_ip'];
+		$ipn_data['custom']           = $custom;
+		$ipn_data['gateway']          = 'stripe-sca';
+		$ipn_data['status']           = 'completed';
+
+		$bd_addr = $charge->data[0]->billing_details->address;
+
+		$ipn_data['address_street']  = isset( $bd_addr->line1 ) ? $bd_addr->line1 : '';
+		$ipn_data['address_city']    = isset( $bd_addr->city ) ? $bd_addr->city : '';
+		$ipn_data['address_state']   = isset( $bd_addr->state ) ? $bd_addr->state : '';
+		$ipn_data['address_zipcode'] = isset( $bd_addr->postal_code ) ? $bd_addr->postal_code : '';
+		$ipn_data['address_country'] = isset( $bd_addr->country ) ? $bd_addr->country : '';
+
+		$ipn_data['payment_button_id'] = $button_id;
+		$ipn_data['is_live']           = ! $sandbox_enabled;
+
+		// Handle the membership signup related tasks.
+		swpm_handle_subsc_signup_stand_alone( $ipn_data, $membership_level_id, $txn_id, $swpm_id );
+
+		// Save the transaction record
+		SwpmTransactions::save_txn_record( $ipn_data );
+		SwpmLog::log_simple_debug( 'Transaction data saved.', true );
+
+		// Trigger the stripe IPN processed action hook (so other plugins can can listen for this event).
+		do_action( 'swpm_stripe_sca_ipn_processed', $ipn_data );
+
+		do_action( 'swpm_payment_ipn_processed', $ipn_data );
+
+		// Redirect the user to the return URL (or to the homepage if a return URL is not specified for this payment button).
+		$return_url = get_post_meta( $button_id, 'return_url', true );
+		if ( empty( $return_url ) ) {
+			$return_url = SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL;
+		}
+		SwpmLog::log_simple_debug( 'Redirecting customer to: ' . $return_url, true );
+		SwpmLog::log_simple_debug( 'End of Stripe SCA Buy Now IPN processing.', true, true );
+		SwpmMiscUtils::redirect_to_url( $return_url );
+
+	}
+
+	public function handle_session_create() {
+		$button_id = filter_input( INPUT_POST, 'swpm_button_id', FILTER_SANITIZE_NUMBER_INT );
+		if ( empty( $button_id ) ) {
+			wp_send_json( array( 'error' => 'No button ID provided' ) );
+		}
+
+		$uniqid = filter_input( INPUT_POST, 'swpm_uniqid', FILTER_SANITIZE_STRING );
+		$uniqid = ! empty( $uniqid ) ? $uniqid : '';
+
+		$settings   = SwpmSettings::get_instance();
+		$button_cpt = get_post( $button_id ); //Retrieve the CPT for this button
+		$item_name  = htmlspecialchars( $button_cpt->post_title );
+
+		$plan_id = get_post_meta( $button_id, 'stripe_plan_id', true );
+
+		if ( empty( $plan_id ) ) {
+			//Payment amount and currency
+			$payment_amount = get_post_meta( $button_id, 'payment_amount', true );
+			if ( ! is_numeric( $payment_amount ) ) {
+				wp_send_json( array( 'error' => 'Error! The payment amount value of the button must be a numeric number. Example: 49.50' ) );
+			}
+
+			$payment_currency = get_post_meta( $button_id, 'payment_currency', true );
+			$payment_amount   = round( $payment_amount, 2 ); //round the amount to 2 decimal place.
+
+			$payment_amount = apply_filters( 'swpm_payment_amount_filter', $payment_amount, $button_id );
+
+			$zero_cents = unserialize( SIMPLE_WP_MEMBERSHIP_STRIPE_ZERO_CENTS );
+			if ( in_array( $payment_currency, $zero_cents ) ) {
+				//this is zero-cents currency, amount shouldn't be multiplied by 100
+				$price_in_cents = $payment_amount;
+			} else {
+				$price_in_cents = $payment_amount * 100; //The amount (in cents). This value is passed to Stripe API.
+			}
+			$payment_amount_formatted = SwpmMiscUtils::format_money( $payment_amount, $payment_currency );
+		}
+
+		//$button_image_url = get_post_meta($button_id, 'button_image_url', true);//Stripe doesn't currenty support button image for their standard checkout.
+		//User's IP address
+		$user_ip                                     = SwpmUtils::get_user_ip_address();
+		$_SESSION['swpm_payment_button_interaction'] = $user_ip;
+
+		//Custom field data
+		$custom_field_value  = 'subsc_ref=' . $membership_level_id;
+		$custom_field_value .= '&user_ip=' . $user_ip;
+		if ( SwpmMemberUtils::is_member_logged_in() ) {
+			$custom_field_value .= '&swpm_id=' . SwpmMemberUtils::get_logged_in_members_id();
+		}
+		$custom_field_value = apply_filters( 'swpm_custom_field_value_filter', $custom_field_value );
+
+		//Sandbox settings
+		$sandbox_enabled = $settings->get_value( 'enable-sandbox-testing' );
+
+		//API keys
+		$api_keys = SwpmMiscUtils::get_stripe_api_keys_from_payment_button( $button_id, ! $sandbox_enabled );
+
+		//Billing address
+		$billing_address = isset( $args['billing_address'] ) ? '1' : '';
+		//By default don't show the billing address in the checkout form.
+		//if billing_address parameter is not present in the shortcode, let's check button option
+		if ( $billing_address === '' ) {
+			$collect_address = get_post_meta( $button_id, 'stripe_collect_address', true );
+			if ( $collect_address === '1' ) {
+				//Collect Address enabled in button settings
+				$billing_address = 1;
+			}
+		}
+
+		$ref_id = 'swpm_' . $uniqid . '|' . $button_id;
+
+		//Return, cancel, notifiy URLs
+		if ( empty( $plan_id ) ) {
+			$notify_url = sprintf( SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL . '/?swpm_process_stripe_sca_buy_now=1&ref_id=%s', $ref_id );
+		} else {
+			$notify_url = sprintf( SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL . '/?swpm_process_stripe_sca_subscription=1&ref_id=%s', $ref_id );
+		}
+
+		$current_url_posted = filter_input( INPUT_POST, 'swpm_page_url', FILTER_SANITIZE_URL );
+
+		$current_url = ! empty( $current_url_posted ) ? $current_url_posted : SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL;
+
+		//prefill member email
+		$prefill_member_email = $settings->get_value( 'stripe-prefill-member-email' );
+
+		if ( $prefill_member_email ) {
+			$auth         = SwpmAuth::get_instance();
+			$member_email = $auth->get( 'email' );
+		}
+
+		SwpmMiscUtils::load_stripe_lib();
+
+		try {
+			\Stripe\Stripe::setApiKey( $api_keys['secret'] );
+
+			if ( empty( $plan_id ) ) {
+				//this is one-off payment
+				$opts = array(
+					'payment_method_types'       => array( 'card' ),
+					'client_reference_id'        => $ref_id,
+					'billing_address_collection' => $billing_address ? 'required' : 'auto',
+					'line_items'                 => array(
+						array(
+							'name'        => $item_name,
+							'description' => $payment_amount_formatted,
+							'amount'      => $price_in_cents,
+							'currency'    => $payment_currency,
+							'quantity'    => 1,
+						),
+					),
+					'success_url'                => $notify_url,
+					'cancel_url'                 => $current_url,
+				);
+			} else {
+				//this is subscription payment
+				$opts = array(
+					'payment_method_types'       => array( 'card' ),
+					'client_reference_id'        => $ref_id,
+					'billing_address_collection' => $billing_address ? 'required' : 'auto',
+					'subscription_data'          => array(
+						'items' => array( array( 'plan' => $plan_id ) ),
+					),
+					'success_url'                => $notify_url,
+					'cancel_url'                 => $current_url,
+				);
+
+				$trial_period = get_post_meta( $button_id, 'stripe_trial_period', true );
+				$trial_period = absint( $trial_period );
+				if ( $trial_period ) {
+					$opts['subscription_data']['trial_period_days'] = $trial_period;
+				}
+			}
+
+			if ( ! empty( $item_logo ) ) {
+				$opts['line_items'][0]['images'] = array( $item_logo );
+			}
+
+			if ( ! empty( $member_email ) ) {
+				$opts['customer_email'] = $member_email;
+			}
+
+			$opts = apply_filters( 'swpm_stripe_sca_session_opts', $opts, $button_id );
+
+			$session = \Stripe\Checkout\Session::create( $opts );
+		} catch ( Exception $e ) {
+			$err = $e->getMessage();
+			wp_send_json( array( 'error' => 'Error occurred: ' . $err ) );
+		}
+		wp_send_json( array( 'session_id' => $session->id ) );
+	}
+
+}
+
+new SwpmStripeSCABuyNowIpnHandler();

+ 210 - 0
simple-membership/ipn/swpm-stripe-sca-subscription-ipn.php

@@ -0,0 +1,210 @@
+<?php
+
+require SIMPLE_WP_MEMBERSHIP_PATH . 'ipn/swpm_handle_subsc_ipn.php';
+
+class SwpmStripeSCASubscriptionIpnHandler {
+
+	public function __construct() {
+
+		$this->handle_stripe_ipn();
+	}
+
+	public function handle_stripe_ipn() {
+				//This will get executed only for direct post (not webhooks). So it is executed at the time of payment in the browser (via HTTP POST). When the "hook" query arg is not set.
+				//The webhooks are handled by the "swpm-stripe-subscription-ipn.php" script.
+
+		SwpmLog::log_simple_debug( 'Stripe SCA Subscription IPN (HTTP POST) received. Processing request...', true );
+		// SwpmLog::log_simple_debug(print_r($_REQUEST, true), true);//Useful for debugging purpose
+
+		// Read and sanitize the request parameters.
+
+		$ref_id = filter_input( INPUT_GET, 'ref_id', FILTER_SANITIZE_STRING );
+
+		if ( empty( $ref_id ) ) {
+			//no ref id provided, cannot proceed
+			SwpmLog::log_simple_debug( 'Fatal Error! No ref_id provied.', false );
+			wp_die( esc_html( 'Fatal Error! No ref_id provied.' ) );
+
+		}
+
+		$trans_info = explode( '|', $ref_id );
+		$button_id  = isset( $trans_info[1] ) ? absint( $trans_info[1] ) : false;
+
+		// Retrieve the CPT for this button
+		$button_cpt = get_post( $button_id );
+		if ( ! $button_cpt ) {
+			// Fatal error. Could not find this payment button post object.
+			SwpmLog::log_simple_debug( 'Fatal Error! Failed to retrieve the payment button post object for the given button ID: ' . $button_id, false );
+			wp_die( esc_html( sprintf( 'Fatal Error! Payment button (ID: %d) does not exist. This request will fail.', $button_id ) ) );
+		}
+
+		$settings        = SwpmSettings::get_instance();
+		$sandbox_enabled = $settings->get_value( 'enable-sandbox-testing' );
+
+		//API keys
+		$api_keys = SwpmMiscUtils::get_stripe_api_keys_from_payment_button( $button_id, ! $sandbox_enabled );
+
+		// Include the Stripe library.
+		SwpmMiscUtils::load_stripe_lib();
+
+		try {
+			\Stripe\Stripe::setApiKey( $api_keys['secret'] );
+
+			$events = \Stripe\Event::all(
+				array(
+					'type'    => 'checkout.session.completed',
+					'created' => array(
+						'gte' => time() - 60 * 60,
+					),
+				)
+			);
+
+			$sess = false;
+
+			foreach ( $events->autoPagingIterator() as $event ) {
+				$session = $event->data->object;
+				if ( isset( $session->client_reference_id ) && $session->client_reference_id === $ref_id ) {
+					$sess = $session;
+					break;
+				}
+			}
+
+			if ( false === $sess ) {
+				// Can't find session.
+				$error_msg = sprintf( "Fatal error! Payment with ref_id %s can't be found", $ref_id );
+				SwpmLog::log_simple_debug( $error_msg, false );
+				wp_die( esc_html( $error_msg ) );
+			}
+
+			$sub_id = $sess->subscription;
+
+			$sub = \Stripe\Subscription::retrieve( $sub_id );
+		} catch ( Exception $e ) {
+			$error_msg = 'Error occurred: ' . $e->getMessage();
+			SwpmLog::log_simple_debug( $error_msg, false );
+			wp_die( esc_html( $error_msg ) );
+		}
+
+		$pm = \Stripe\PaymentMethod::retrieve( $sub->default_payment_method );
+
+		// Grab the charge ID and set it as the transaction ID.
+		$txn_id = $sub->customer;
+		// The charge ID can be used to retrieve the transaction details using hte following call.
+		// \Stripe\Charge::retrieve($charge->$data[0]->id);
+
+		//check if this payment has already been processed
+		$payment = get_posts(
+			array(
+				'meta_key'       => 'txn_id',
+				'meta_value'     => $txn_id,
+				'posts_per_page' => 1,
+				'offset'         => 0,
+				'post_type'      => 'swpm_transactions',
+			)
+		);
+		wp_reset_postdata();
+
+		if ( $payment ) {
+			//payment has already been processed. Redirecting user to return_url
+			$return_url = get_post_meta( $button_id, 'return_url', true );
+			if ( empty( $return_url ) ) {
+				$return_url = SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL;
+			}
+			SwpmMiscUtils::redirect_to_url( $return_url );
+			return;
+		}
+
+		$price_in_cents = $sub->plan->amount;
+		$currency_code  = strtoupper( $sub->plan->currency );
+
+		$zero_cents = unserialize( SIMPLE_WP_MEMBERSHIP_STRIPE_ZERO_CENTS );
+		if ( in_array( $currency_code, $zero_cents, true ) ) {
+			$payment_amount = $price_in_cents;
+		} else {
+			$payment_amount = $price_in_cents / 100;// The amount (in cents). This value is used in Stripe API.
+		}
+
+		$payment_amount = floatval( $payment_amount );
+
+		$payment_amount = apply_filters( 'swpm_payment_amount_filter', $payment_amount, $button_id );
+
+		$membership_level_id = get_post_meta( $button_id, 'membership_level_id', true );
+
+		// Everything went ahead smoothly with the charge.
+		SwpmLog::log_simple_debug( 'Stripe SCA Subscription charge successful.', true );
+
+		$customer = \Stripe\Customer::retrieve( $txn_id );
+
+		$stripe_email = $customer->email;
+
+		$user_ip = SwpmUtils::get_user_ip_address();
+
+		//Custom field data
+		$custom_field_value  = 'subsc_ref=' . $membership_level_id;
+		$custom_field_value .= '&user_ip=' . $user_ip;
+		if ( SwpmMemberUtils::is_member_logged_in() ) {
+			$custom_field_value .= '&swpm_id=' . SwpmMemberUtils::get_logged_in_members_id();
+		}
+		$custom_field_value = apply_filters( 'swpm_custom_field_value_filter', $custom_field_value );
+
+		$custom = $custom_field_value;
+
+		$custom_var = SwpmTransactions::parse_custom_var( $custom );
+		$swpm_id    = isset( $custom_var['swpm_id'] ) ? $custom_var['swpm_id'] : '';
+
+		// Let's try to get first_name and last_name from full name
+		$name       = $pm->billing_details->name;
+		$last_name  = ( strpos( $name, ' ' ) === false ) ? '' : preg_replace( '#.*\s([\w-]*)$#', '$1', $name );
+		$first_name = trim( preg_replace( '#' . $last_name . '#', '', $name ) );
+
+		// Create the $ipn_data array.
+		$ipn_data                     = array();
+		$ipn_data['mc_gross']         = $payment_amount;
+		$ipn_data['first_name']       = $first_name;
+		$ipn_data['last_name']        = $last_name;
+		$ipn_data['payer_email']      = $stripe_email;
+		$ipn_data['membership_level'] = $membership_level_id;
+		$ipn_data['txn_id']           = $txn_id;
+		$ipn_data['subscr_id']        = $sub_id;
+		$ipn_data['swpm_id']          = $swpm_id;
+		$ipn_data['ip']               = $custom_var['user_ip'];
+		$ipn_data['custom']           = $custom;
+		$ipn_data['gateway']          = 'stripe-sca-subs';
+		$ipn_data['status']           = 'completed';
+
+		$bd_addr = $pm->billing_details->address;
+
+		$ipn_data['address_street']  = isset( $bd_addr->line1 ) ? $bd_addr->line1 : '';
+		$ipn_data['address_city']    = isset( $bd_addr->city ) ? $bd_addr->city : '';
+		$ipn_data['address_state']   = isset( $bd_addr->state ) ? $bd_addr->state : '';
+		$ipn_data['address_zipcode'] = isset( $bd_addr->postal_code ) ? $bd_addr->postal_code : '';
+		$ipn_data['address_country'] = isset( $bd_addr->country ) ? $bd_addr->country : '';
+
+		$ipn_data['payment_button_id'] = $button_id;
+		$ipn_data['is_live']           = ! $sandbox_enabled;
+
+		// Handle the membership signup related tasks.
+		swpm_handle_subsc_signup_stand_alone( $ipn_data, $membership_level_id, $txn_id, $swpm_id );
+
+		// Save the transaction record
+		SwpmTransactions::save_txn_record( $ipn_data );
+		SwpmLog::log_simple_debug( 'Transaction data saved.', true );
+
+		// Trigger the stripe IPN processed action hook (so other plugins can can listen for this event).
+		do_action( 'swpm_stripe_sca_ipn_processed', $ipn_data );
+
+		do_action( 'swpm_payment_ipn_processed', $ipn_data );
+
+		// Redirect the user to the return URL (or to the homepage if a return URL is not specified for this payment button).
+		$return_url = get_post_meta( $button_id, 'return_url', true );
+		if ( empty( $return_url ) ) {
+			$return_url = SIMPLE_WP_MEMBERSHIP_SITE_HOME_URL;
+		}
+		SwpmLog::log_simple_debug( 'Redirecting customer to: ' . $return_url, true );
+		SwpmLog::log_simple_debug( 'End of Stripe SCA Subscription IPN processing.', true, true );
+		SwpmMiscUtils::redirect_to_url( $return_url );
+
+	}
+}
+
+new SwpmStripeSCASubscriptionIpnHandler();

Some files were not shown because too many files changed in this diff