Changeset 2458244
- Timestamp:
- 01/18/2021 12:04:59 PM (5 years ago)
- Location:
- e20r-members-list
- Files:
-
- 44 added
- 28 deleted
- 20 edited
- 1 copied
-
tags/6.0 (copied) (copied from e20r-members-list/trunk)
-
tags/6.0/README.txt (modified) (2 diffs)
-
tags/6.0/class.e20r-members-list.php (modified) (3 diffs)
-
tags/6.0/class/members-list/class.bulk-cancel.php (modified) (11 diffs)
-
tags/6.0/class/members-list/class.bulk-update.php (modified) (30 diffs)
-
tags/6.0/class/members-list/class.export-members.php (modified) (40 diffs)
-
tags/6.0/class/members-list/class.members-list-page.php (modified) (21 diffs)
-
tags/6.0/class/members-list/class.members-list.php (modified) (11 diffs)
-
tags/6.0/class/members-list/class.sort-by-meta.php (modified) (5 diffs)
-
tags/6.0/class/members-list/css/e20r-memberslist-page.css (modified) (1 diff)
-
tags/6.0/class/members-list/js/e20r-memberslist-page.js (modified) (1 diff)
-
tags/6.0/class/utilities/README.txt (added)
-
tags/6.0/class/utilities/bin (added)
-
tags/6.0/class/utilities/bin/build-plugin.sh (added)
-
tags/6.0/class/utilities/bin/changelog.sh (added)
-
tags/6.0/class/utilities/class-utility-loader.php (added)
-
tags/6.0/class/utilities/class.cache-object.php (deleted)
-
tags/6.0/class/utilities/class.cache.php (deleted)
-
tags/6.0/class/utilities/class.e20r-async-request.php (deleted)
-
tags/6.0/class/utilities/class.e20r-background-process.php (deleted)
-
tags/6.0/class/utilities/class.gdpr-enablement.php (deleted)
-
tags/6.0/class/utilities/class.message.php (deleted)
-
tags/6.0/class/utilities/class.support.php (deleted)
-
tags/6.0/class/utilities/class.utilities.php (deleted)
-
tags/6.0/class/utilities/licensing/class-license-client.php (added)
-
tags/6.0/class/utilities/licensing/class-license-page.php (added)
-
tags/6.0/class/utilities/licensing/class-license-server.php (added)
-
tags/6.0/class/utilities/licensing/class-license-settings.php (added)
-
tags/6.0/class/utilities/licensing/class-licensing.php (added)
-
tags/6.0/class/utilities/licensing/class.license-client.php (deleted)
-
tags/6.0/class/utilities/licensing/class.license-page.php (deleted)
-
tags/6.0/class/utilities/licensing/class.license-server.php (deleted)
-
tags/6.0/class/utilities/licensing/class.license-settings.php (deleted)
-
tags/6.0/class/utilities/licensing/class.licensing.php (deleted)
-
tags/6.0/class/utilities/metadata.json (added)
-
tags/6.0/class/utilities/policies (deleted)
-
tags/6.0/class/utilities/utilities (added)
-
tags/6.0/class/utilities/utilities/class-cache-object.php (added)
-
tags/6.0/class/utilities/utilities/class-cache.php (added)
-
tags/6.0/class/utilities/utilities/class-e20r-async-request.php (added)
-
tags/6.0/class/utilities/utilities/class-e20r-background-process.php (added)
-
tags/6.0/class/utilities/utilities/class-gdpr-enablement.php (added)
-
tags/6.0/class/utilities/utilities/class-message.php (added)
-
tags/6.0/class/utilities/utilities/class-support.php (added)
-
tags/6.0/class/utilities/utilities/class-utilities.php (added)
-
tags/6.0/class/utilities/utilities/policies (added)
-
tags/6.0/class/utilities/utilities/policies/e20r-privacy-policy.html (added)
-
trunk/README.txt (modified) (2 diffs)
-
trunk/class.e20r-members-list.php (modified) (3 diffs)
-
trunk/class/members-list/class.bulk-cancel.php (modified) (11 diffs)
-
trunk/class/members-list/class.bulk-update.php (modified) (30 diffs)
-
trunk/class/members-list/class.export-members.php (modified) (40 diffs)
-
trunk/class/members-list/class.members-list-page.php (modified) (21 diffs)
-
trunk/class/members-list/class.members-list.php (modified) (11 diffs)
-
trunk/class/members-list/class.sort-by-meta.php (modified) (5 diffs)
-
trunk/class/members-list/css/e20r-memberslist-page.css (modified) (1 diff)
-
trunk/class/members-list/js/e20r-memberslist-page.js (modified) (1 diff)
-
trunk/class/utilities/README.txt (added)
-
trunk/class/utilities/bin (added)
-
trunk/class/utilities/bin/build-plugin.sh (added)
-
trunk/class/utilities/bin/changelog.sh (added)
-
trunk/class/utilities/class-utility-loader.php (added)
-
trunk/class/utilities/class.cache-object.php (deleted)
-
trunk/class/utilities/class.cache.php (deleted)
-
trunk/class/utilities/class.e20r-async-request.php (deleted)
-
trunk/class/utilities/class.e20r-background-process.php (deleted)
-
trunk/class/utilities/class.gdpr-enablement.php (deleted)
-
trunk/class/utilities/class.message.php (deleted)
-
trunk/class/utilities/class.support.php (deleted)
-
trunk/class/utilities/class.utilities.php (deleted)
-
trunk/class/utilities/licensing/class-license-client.php (added)
-
trunk/class/utilities/licensing/class-license-page.php (added)
-
trunk/class/utilities/licensing/class-license-server.php (added)
-
trunk/class/utilities/licensing/class-license-settings.php (added)
-
trunk/class/utilities/licensing/class-licensing.php (added)
-
trunk/class/utilities/licensing/class.license-client.php (deleted)
-
trunk/class/utilities/licensing/class.license-page.php (deleted)
-
trunk/class/utilities/licensing/class.license-server.php (deleted)
-
trunk/class/utilities/licensing/class.license-settings.php (deleted)
-
trunk/class/utilities/licensing/class.licensing.php (deleted)
-
trunk/class/utilities/metadata.json (added)
-
trunk/class/utilities/policies (deleted)
-
trunk/class/utilities/utilities (added)
-
trunk/class/utilities/utilities/class-cache-object.php (added)
-
trunk/class/utilities/utilities/class-cache.php (added)
-
trunk/class/utilities/utilities/class-e20r-async-request.php (added)
-
trunk/class/utilities/utilities/class-e20r-background-process.php (added)
-
trunk/class/utilities/utilities/class-gdpr-enablement.php (added)
-
trunk/class/utilities/utilities/class-message.php (added)
-
trunk/class/utilities/utilities/class-support.php (added)
-
trunk/class/utilities/utilities/class-utilities.php (added)
-
trunk/class/utilities/utilities/policies (added)
-
trunk/class/utilities/utilities/policies/e20r-privacy-policy.html (added)
Legend:
- Unmodified
- Added
- Removed
-
e20r-members-list/tags/6.0/README.txt
r2306383 r2458244 12 12 13 13 Extensible, sortable & bulk action capable members listing tool for Paid Memberships Pro. This plugin is a complete replacement for the "Members List" functionality in PMPro and supports most of the same filters and hooks. The key differences have to do with managing columns. Now you can also use the [standard WordPress filters](https://developer.wordpress.org/reference/classes/wp_list_table/) to columns you can add/remove/make sortable, additional bulk actions, etc. 14 15  14 16 15 17 == Installation == … … 189 191 == Changelog == 190 192 193 == 6.0 == 194 * BUG FIX: Didn't paginate correctly because the LIMIT logic caused us to not return the full number of records for the level/status 195 * BUG FIX: Potentially a fatal PHP error 196 * BUG FIX: Bad path to downloadable archive 197 * BUG FIX: Didn't (always) import the database needed for testing 198 * BUG FIX: Readme directions for installation were imprecise 199 * BUG FIX: Removed copy/paste Utilities module and using git subtree instead 200 * BUG FIX: Updated copyright notice 201 202 191 203 == 5.10 == 192 204 * BUG FIX: Fatal error in Utilities library -
e20r-members-list/tags/6.0/class.e20r-members-list.php
r2306383 r2458244 4 4 Plugin URI: https://wordpress.org/plugins/e20r-members-list 5 5 Description: Extensible, sortable & bulk action capable members listing + export to CSV tool for Paid Memberships Pro. 6 Version: 5.106 Version: 6.0 7 7 Author: Thomas Sjolshagen @ Eighty / 20 Results by Wicked Strong Chicks, LLC <[email protected]> 8 8 Author URI: https://eighty20results.com/thomas-sjolshagen/ … … 11 11 License: 12 12 13 Copyright 2016 - 202 0(c) Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected])13 Copyright 2016 - 2021 (c) Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected]) 14 14 15 15 This program is free software; you can redistribute it and/or modify … … 33 33 34 34 if ( ! defined( 'E20R_MEMBERSLIST_VER' ) ) { 35 define( 'E20R_MEMBERSLIST_VER', ' 5.10' );35 define( 'E20R_MEMBERSLIST_VER', '6.0' ); 36 36 } 37 37 -
e20r-members-list/tags/6.0/class/members-list/class.bulk-cancel.php
r2025537 r2458244 1 1 <?php 2 2 /** 3 * Copyright (c) 2018-20 19- Eighty / 20 Results by Wicked Strong Chicks.3 * Copyright (c) 2018-2021 - Eighty / 20 Results by Wicked Strong Chicks. 4 4 * ALL RIGHTS RESERVED 5 5 * … … 23 23 24 24 class Bulk_Cancel { 25 25 26 26 /** 27 27 * Instance of this class … … 36 36 */ 37 37 private $operation = null; 38 38 39 39 /** 40 40 * List of member IDs to update … … 43 43 */ 44 44 private $members_to_update = array(); 45 45 46 46 /** 47 47 * Bulk_Cancel constructor (singleton) … … 51 51 private function __construct() { 52 52 } 53 53 54 54 /** 55 55 * The __clone() method for Bulk_Cancel() (singleton class) … … 58 58 */ 59 59 private function __clone() {} 60 60 61 61 /** 62 62 * Process cancellations for all members/membership_ids 63 63 */ 64 64 public function cancel() { 65 65 66 66 // Process all User & level ID for the single action. 67 67 $failed = array(); 68 68 69 69 $utils = Utilities::get_instance(); 70 70 $utils->log("Cancelling " . count( $this->members_to_update ) . " members"); 71 71 72 72 // Process all selected records/members 73 73 foreach ( $this->members_to_update as $key => $cancel_info ) { 74 74 75 75 if ( false == $this->cancel_member( $cancel_info['user_id'], $cancel_info['level_id'] ) ) { 76 76 $failed[] = $cancel_info['user_id']; // FIXME: Add level info for multiple membership levels 77 77 } 78 78 } 79 79 80 80 //Check for errors & display error banner if we got one. 81 81 if ( ! empty( $failed ) ) { 82 82 83 83 $message = sprintf( 84 84 __( "Unable to cancel membership(s) for the following user IDs: %s", "e20r-members-list" ), 85 85 implode( ', ', $failed ) 86 86 ); 87 87 88 88 if ( function_exists( 'pmpro_setMessage' ) ) { 89 89 pmpro_setMessage( $message, "error" ); … … 91 91 global $msg; 92 92 global $msgt; 93 93 94 94 $msg = $message; 95 95 $msgt = 'error'; 96 96 } 97 97 98 98 } 99 99 } 100 100 101 101 /** 102 102 * The cancel member action … … 108 108 */ 109 109 public static function cancel_member( $id, $level_id = null ) { 110 110 111 111 if ( function_exists( 'pmpro_cancelMembershipLevel' ) ) { 112 112 return pmpro_cancelMembershipLevel( $level_id, $id, 'admin_cancelled' ); … … 114 114 return false; 115 115 } 116 116 117 117 } 118 118 119 119 /** 120 120 * Get or create an instance of the Bulk_Cancel class … … 123 123 */ 124 124 public static function get_instance() { 125 125 126 126 if ( is_null( self::$instance ) ) { 127 127 self::$instance = new self; 128 128 } 129 129 130 130 return self::$instance; 131 131 } 132 132 133 133 /** 134 134 * Set the list of members & their levels to update … … 139 139 $this->members_to_update = $member_info; 140 140 } 141 141 142 142 /** 143 143 * Return the list of members being updated -
e20r-members-list/tags/6.0/class/members-list/class.bulk-update.php
r2025537 r2458244 1 1 <?php 2 2 /** 3 * Copyright (c) 2018-20 19- Eighty / 20 Results by Wicked Strong Chicks.3 * Copyright (c) 2018-2021 - Eighty / 20 Results by Wicked Strong Chicks. 4 4 * ALL RIGHTS RESERVED 5 5 * … … 24 24 25 25 class Bulk_Update { 26 26 27 27 /** 28 28 * Instance of this class … … 31 31 */ 32 32 private static $instance = null; 33 33 34 34 /** 35 35 * Update operation to perform … … 38 38 */ 39 39 private $operation = null; 40 40 41 41 /** 42 42 * List of members to update … … 45 45 */ 46 46 private $members_to_update = array(); 47 47 48 48 /** 49 49 * Bulk_Update constructor (singleton) … … 53 53 private function __construct() { 54 54 } 55 55 56 56 /** 57 57 * __clone() method for Bulk_Update() (singleton class) … … 60 60 */ 61 61 private function __clone(){} 62 62 63 63 /** 64 64 * Get or create an instance of the Bulk_Update class … … 67 67 */ 68 68 public static function get_instance() { 69 69 70 70 if ( is_null( self::$instance ) ) { 71 71 self::$instance = new self; 72 72 } 73 73 74 74 return self::$instance; 75 75 } 76 76 77 77 /** 78 78 * Handle bulk update (for core member list columns). Triggers action for external bulk update activities/fields … … 81 81 */ 82 82 public function update() { 83 83 84 84 $utils = Utilities::get_instance(); 85 85 $update_errors = array(); 86 86 $level_failed = array(); 87 87 88 88 $utils->log("User count to update: " . count( $this->members_to_update) ); 89 89 $utils->log("Request: " . print_r( $_REQUEST, true )); 90 90 91 91 /** 92 92 * Process build-in edit fields for the specified members to update 93 93 */ 94 94 foreach ( $this->members_to_update as $key => $user_info ) { 95 95 96 96 $level_changed = false; 97 97 $old_user_level = $utils->get_variable( "e20r-members-list-db_membership_id_{$user_info['user_id']}", 0 ); 98 98 $new_user_level = $utils->get_variable( "e20r-members-list-new_membership_id_{$user_info['user_id']}", 0 ); 99 99 $record_id = $utils->get_variable( "e20r-members-list-db_record_id_{$user_info['user_id']}", 0 ); 100 100 101 101 if ( empty( $user_info['user_id'] ) ) { 102 102 $utils->log("User ID is NULL. Returning!" ); 103 103 return false; 104 104 } 105 105 106 106 $utils->log("Have to update level for {$user_info['user_id']}? {$new_user_level}" ); 107 107 108 108 // Update the membership level for the user we're processing 109 109 if ( !empty( $new_user_level) && $old_user_level !== $new_user_level ) { 110 110 111 111 if ( false === $this->update_membership( $user_info['user_id'], $old_user_level, $new_user_level ) ) { 112 112 113 113 // Add to list of failed updates 114 114 $level_failed[] = array( … … 122 122 } 123 123 } 124 124 125 125 $old_startdate = $utils->get_variable( "e20r-members-list-db_startdate_{$user_info['user_id']}", '' ); 126 126 $new_startdate = $utils->get_variable( "e20r-members-list-new_startdate_{$user_info['user_id']}", '' ); 127 127 128 128 $utils->log("Have to update start date for {$user_info['user_id']}? N:{$new_startdate} vs O:{$old_startdate}" ); 129 129 130 130 if ( $old_startdate !== $new_startdate ) { 131 131 132 132 if ( empty( $new_startdate ) ) { 133 133 $utils->log("Error: Start date cannot be empty!, using old start date"); 134 134 $new_startdate = $old_startdate; 135 135 } 136 136 137 137 if ( false === $this->update_date( 'startdate', $user_info['user_id'], $old_user_level, $new_user_level, $new_startdate, $record_id, $level_changed ) ) { 138 138 $startdate_failed[] = array( … … 144 144 } 145 145 } 146 146 147 147 $old_enddate = $utils->get_variable( "e20r-members-list-db_enddate_{$user_info['user_id']}", '' ); 148 148 $new_enddate = $utils->get_variable( "e20r-members-list-new_enddate_{$user_info['user_id']}", '' ); 149 149 150 150 $utils->log("Have to update end date for {$user_info['user_id']}? N:{$new_enddate} vs O:{$old_enddate}"); 151 151 152 152 if ( $old_enddate !== $new_enddate ) { 153 153 154 154 $utils->log("Updating end date to {$new_enddate}"); 155 155 156 156 if ( false === $this->update_date( 'enddate', $user_info['user_id'], $old_user_level, $new_user_level, $new_enddate, $record_id, $level_changed ) ) { 157 157 $enddate_failed[] = array( … … 163 163 } 164 164 } 165 165 166 166 $old_status = $utils->get_variable( "e20r-members-list-db_status_{$user_info['user_id']}", '' ); 167 167 $new_status = $utils->get_variable( "e20r-members-list-new_status_{$user_info['user_id']}", '' ); 168 168 169 169 $utils->log("Have to update status for {$user_info['user_id']}? {$new_status}" ); 170 170 171 171 if ( $old_status !== $new_status ) { 172 172 173 173 if ( false === $this->update_status( $record_id, $new_status ) ) { 174 174 $status_failed[] = array( … … 181 181 } 182 182 } 183 183 184 184 /** 185 185 * Trigger action for bulk update (allows external handling of bulk update if needed/desired) … … 190 190 */ 191 191 do_action( 'e20r_memberslist_process_bulk_updates', $this->members_to_update ); 192 192 193 193 /** 194 194 * Error handling for build-in edit fields 195 195 */ 196 196 $msg_template = __( 'Error updating data for %1$s (ID: %2$d). Could not update %3$s from %4$s to %5$s (current membership level: \'%6$s\')', 'e20r-members-list' ); 197 197 198 198 if ( ! empty( $level_failed ) ) { 199 199 200 200 foreach ( $level_failed as $info ) { 201 201 $user = get_user_by( 'ID', $info['user_id'] ); 202 202 $new_user_level = pmpro_getLevel( $info['new_level'] ); 203 203 $old_user_level = pmpro_getLevel( $info['old_level'] ); 204 204 205 205 $update_errors[] = sprintf( 206 206 $msg_template, … … 213 213 ); 214 214 } 215 216 } 217 215 216 } 217 218 218 if ( ! empty( $enddate_failed ) ) { 219 219 foreach ( $enddate_failed as $info ) { 220 220 $user = get_user_by( 'ID', $info['user_id'] ); 221 221 $user_level = pmpro_getLevel( $info['level_id'] ); 222 222 223 223 $update_errors[] = sprintf( 224 224 $msg_template, … … 232 232 } 233 233 } 234 234 235 235 if ( ! empty( $startdate_failed ) ) { 236 236 foreach ( $startdate_failed as $info ) { 237 237 $user = get_user_by( 'ID', $info['user_id'] ); 238 238 $user_level = pmpro_getLevel( $info['level_id'] ); 239 239 240 240 $update_errors[] = sprintf( 241 241 $msg_template, … … 249 249 } 250 250 } 251 251 252 252 if ( ! empty( $status_failed ) ) { 253 253 254 254 foreach ( $status_failed as $info ) { 255 255 $user = get_user_by( 'ID', $info['user_id'] ); 256 256 $user_level = pmpro_getLevel( $info['level_id'] ); 257 257 258 258 $update_errors[] = sprintf( 259 259 $msg_template, … … 267 267 } 268 268 } 269 269 270 270 /** 271 271 * Add error messages to back-end info display 272 272 */ 273 273 if ( ! empty( $update_errors ) ) { 274 274 275 275 $utils->log("Generated " . count( $update_errors ) . " errors during bulk update!"); 276 276 277 277 foreach ( $update_errors as $e_msg ) { 278 278 $utils->log("Error: {$e_msg}"); 279 279 $utils->add_message( $e_msg, 'error', 'backend' ); 280 280 } 281 281 282 282 // And return false (error) 283 283 return false; 284 284 } 285 285 286 286 // All's good! 287 287 return true; 288 288 } 289 289 290 290 /** 291 291 * Change the membership level for the specified User ID … … 298 298 */ 299 299 public function update_membership( $user_id, $current_level_id, $new_level_id ) { 300 300 301 301 // Execute the membership level change for the specified user ID/Level ID 302 302 if ( function_exists( 'pmpro_changeMembershipLevel' ) ) { … … 306 306 } 307 307 } 308 308 309 309 /** 310 310 * Update the specified field name (a date field) … … 321 321 */ 322 322 public function update_date( $field_name, $user_id, $current_level, $new_level, $new_date, $record_id = null, $use_new = false ) { 323 323 324 324 $utils = Utilities::get_instance(); 325 325 $date = null; 326 326 327 327 // Make sure we received a valid date 328 328 if ( !empty($new_date) && false === $this->validate_date_format( $new_date, 'Y-m-d' ) ) { 329 329 330 330 $user = get_user_by('ID', $user_id ); 331 331 332 332 $msg = sprintf( 333 333 __("Invalid date format for %s (record: %d/email: %s)", 'e20r-members-list' ), … … 336 336 $user->user_email 337 337 ); 338 338 339 339 $utils->log( $msg ); 340 340 $utils->add_message( $msg, 'error', 'backend' ); 341 341 342 342 return false; 343 343 } 344 344 345 345 global $wpdb; 346 346 347 347 if ( true === $use_new ) { 348 348 $where = array( 'membership_id' => $new_level, 'user_id' => $user_id, 'status' => 'active' ); … … 355 355 $where_format = array( '%d', '%d', '%s' ); 356 356 } 357 357 358 358 if ( !empty( $new_date ) ) { 359 359 if ( true === apply_filters( 'e20r_memberslist_membership_starts_at_midnight', __return_true() ) ) { … … 364 364 } 365 365 $retval = $wpdb->update( $wpdb->pmpro_memberships_users, array( $field_name => $date ), $where, array( '%s' ), $where_format ); 366 366 367 367 if ( false === $retval ) { 368 368 return $retval; 369 369 } 370 370 371 371 return true; 372 372 } 373 373 374 374 /** 375 375 * Update the user's Membership status for the specified record … … 381 381 */ 382 382 public function update_status( $record_id, $status ) { 383 383 384 384 global $wpdb; 385 385 386 386 $retval = $wpdb->update( $wpdb->pmpro_memberships_users, array( 'status' => $status ), array( 'id' => $record_id ), array( '%s' ), array( '%d' ) ); 387 387 388 388 if ( ! empty( $retval ) ) { 389 389 return true; 390 390 } 391 391 392 392 return false; 393 393 } 394 394 395 395 /** 396 396 * Set the list of members to update … … 401 401 $this->members_to_update = $member_info; 402 402 } 403 403 404 404 /** 405 405 * Return the list of members being updated … … 410 410 return $this->members_to_update; 411 411 } 412 412 413 413 /** 414 414 * Return the ongoing operation … … 419 419 return $this->operation; 420 420 } 421 421 422 422 /** 423 423 * Configure/set the Operation for the Bulk Update … … 428 428 $this->operation = $operation; 429 429 } 430 430 431 431 /** 432 432 * Test the date supplied for MySQL compliance … … 440 440 */ 441 441 private function validate_date_format( $date, $format = 'Y-m-d' ) { 442 442 443 443 $check_date = \DateTime::createFromFormat( $format, $date ); 444 444 445 445 return $check_date && $check_date->format( $format ) == $date; 446 446 } -
e20r-members-list/tags/6.0/class/members-list/class.export-members.php
r2228121 r2458244 1 1 <?php 2 2 /** 3 * Copyright (c) 2018-20 19- Eighty / 20 Results by Wicked Strong Chicks.3 * Copyright (c) 2018-2021 - Eighty / 20 Results by Wicked Strong Chicks. 4 4 * ALL RIGHTS RESERVED 5 5 * … … 25 25 26 26 class Export_Members { 27 27 28 28 /** 29 29 * @var array $member_list 30 30 */ 31 31 private $member_list = array(); 32 32 33 33 /** 34 34 * @var string|null $sql 35 35 */ 36 36 private $sql = null; 37 37 38 38 /** 39 39 * @var array $headers 40 40 */ 41 41 private $headers = array(); 42 42 43 43 /** 44 44 * @var array $csv_headers 45 45 */ 46 46 private $csv_headers = array(); 47 47 48 48 /** 49 49 * @var array $csv_rows 50 50 */ 51 51 private $csv_rows = array(); 52 52 53 53 /** 54 54 * @var bool|null|string 55 55 */ 56 56 private $file_name = null; 57 57 58 58 /** 59 59 * Cached $member discount information … … 62 62 */ 63 63 private $member_discount_info = null; 64 64 65 65 /** 66 66 * Export_Members constructor. … … 69 69 */ 70 70 public function __construct( $db_records ) { 71 71 72 72 $this->member_list = $db_records; 73 73 74 74 $this->file_name = $this->create_temp_file(); 75 75 76 76 $this->default_columns = array( 77 77 array( "wp_user", "ID" ), … … 106 106 array( "member_level", 'enddate' ), 107 107 ); 108 108 109 109 $this->default_columns = apply_filters( 'pmpro_members_list_csv_default_columns', $this->default_columns ); 110 110 $this->default_columns = apply_filters( 'e20r-members-list-default-csv-columns', $this->default_columns ); 111 111 112 112 /** 113 113 * Map of Database column keys and CSV export column keys … … 257 257 ) 258 258 ); 259 259 260 260 // Generate the header for the .csv file 261 261 $this->csv_header(); 262 262 $this->set_upload_headers(); 263 263 264 264 /** 265 265 * Trigger the default 'e20r-members-list-load-export-value' filter handler first … … 267 267 add_filter( 'e20r-members-list-load-export-value', array( $this, 'load_export_value' ), - 1, 3 ); 268 268 } 269 269 270 270 /** 271 271 * Create the temporary file name for this export operation … … 274 274 */ 275 275 private function create_temp_file() { 276 276 277 277 // Generate a temporary file to store the data in. 278 278 $tmp_dir = sys_get_temp_dir(); 279 279 $file_name = tempnam( $tmp_dir, 'pmpro_ml_' ); 280 280 281 281 return $file_name; 282 282 } 283 283 284 284 /** 285 285 * Create the export file header (DB columns) 286 286 */ 287 287 private function csv_header() { 288 288 289 289 $utils = Utilities::get_instance(); 290 290 $level = $utils->get_variable( 'membership_id', '' ); 291 291 292 292 $extra_cols = apply_filters( "pmpro_members_list_csv_extra_columns", array() ); 293 293 294 294 if ( ! empty( $extra_cols ) ) { 295 295 296 296 foreach ( $extra_cols as $col_header => $callback ) { 297 297 // $this->header_map[] = $col_header; … … 304 304 } 305 305 } 306 306 307 307 $header_list = apply_filters( 'pmpro_members_list_csv_heading', implode( ',', array_keys( $this->header_map ) ) ); 308 308 $this->csv_headers = array_map( 'trim', explode( ',', $header_list ) ); 309 309 310 310 $utils->log( "Using " . count( $this->csv_headers ) . " header columns" ); 311 311 } 312 312 313 313 /** 314 314 * Set headers for .CSV file upload 315 315 */ 316 316 private function set_upload_headers() { 317 317 318 318 $this->headers[] = "Content-Type: text/csv"; 319 319 $this->headers[] = "Cache-Control: max-age=0, no-cache, no-store"; … … 323 323 $this->headers = apply_filters( 'e20r-memberslist-http-headers', $this->headers ); 324 324 } 325 325 326 326 /** 327 327 * Clear old temporary files 328 328 */ 329 329 public static function clear_temp_files() { 330 330 331 331 $temp_dir_name = sys_get_temp_dir(); 332 332 $utils = Utilities::get_instance(); 333 333 334 334 $files = glob( "{$temp_dir_name}/pmpro_ml_*.csv" ); 335 335 $now = current_time( 'timestamp' ); 336 336 337 337 $utils->log( "Notice: Clearing temporary export files (if needed)" ); 338 338 339 339 foreach ( $files as $file_name ) { 340 340 341 341 if ( is_file( $file_name ) ) { 342 342 343 343 if ( $now - filemtime( $file_name ) >= DAY_IN_SECONDS ) { // Delete after 1 day 344 344 unlink( $file_name ); … … 347 347 } 348 348 } 349 349 350 350 /** 351 351 * Fetches the data for the export operation 352 352 */ 353 353 public function get_data_list() { 354 354 355 355 $utils = Utilities::get_instance(); 356 356 $utils->log( "Headers have been sent already..?!? " . ( headers_sent() ? 'Yes' : 'No' ) ); 357 357 358 358 /** 359 359 * Filter to set max number of records to process at a time … … 370 370 */ 371 371 $max_users_per_loop = apply_filters( 'pmpro_set_max_user_per_export_loop', 2000 ); 372 372 373 373 /** 374 374 * Pre-export actions … … 381 381 */ 382 382 do_action( 'pmpro_before_members_list_csv_export', $this->member_list ); 383 383 384 384 $extra_columns = apply_filters( "pmpro_members_list_csv_extra_columns", array() ); 385 385 386 386 $this->add_csv_header_to_file(); 387 387 388 388 $i_start = 0; 389 389 $iterations = 1; 390 390 $users_found = count( $this->member_list ); 391 391 392 392 if ( $users_found >= $max_users_per_loop ) { 393 393 $iterations = ceil( $users_found / $max_users_per_loop ); 394 394 } 395 395 396 396 $start = current_time( 'timestamp' ); 397 397 $end = 0; 398 398 $time_limit = (int) get_cfg_var( 'max_execution_time' ); 399 399 400 400 // Split up the export operation in multiple rounds/iterations 401 401 for ( $ic = 1; $ic <= $iterations; $ic ++ ) { 402 402 403 403 // Try to avoid timing out during the export operation 404 404 if ( 0 !== $end ) { 405 405 406 406 $iteration_diff = $end - $start; 407 407 $new_time_limit = ceil( $iteration_diff * $iterations * 1.2 ); 408 408 409 409 if ( $time_limit < $new_time_limit ) { 410 410 $time_limit = $new_time_limit; … … 412 412 } 413 413 } 414 414 415 415 $start = current_time( 'timestamp' ); 416 416 417 417 $utils->log( "For iterations: {$i_start} -> " . ( $i_start + $max_users_per_loop ) ); 418 418 419 419 $member_list = array_slice( $this->member_list, $i_start, $max_users_per_loop ); 420 420 421 421 $utils->log( "Will process " . count( $member_list ) . " member records in iteration {$ic}" ); 422 422 423 423 // Increment starting position 424 424 if ( 0 < $iterations ) { 425 425 $i_start += $max_users_per_loop; 426 426 } 427 427 428 428 foreach ( $member_list as $member ) { 429 429 430 430 $csv_record = array(); 431 431 432 432 // Cast the Member array to an object 433 433 $member = (object) $member; 434 434 435 435 /** 436 436 * Fetch any user metadata for the user 437 437 */ 438 438 $member->meta_values = $this->load_user_meta( $member->ID ); 439 439 440 440 /** 441 441 * Fetch/update the CSV export entry for the $member … … 446 446 */ 447 447 $csv_record = apply_filters( 'e20r-members-list-load-export-value', $csv_record, $member ); 448 448 449 449 // Add data from extra columns 450 450 if ( ! empty( $extra_columns ) ) { 451 451 452 452 foreach ( $extra_columns as $col_heading => $callback ) { 453 453 454 454 $val = call_user_func( $callback, $member, $col_heading ); 455 455 $val = ! empty( $val ) ? $val : null; 456 456 457 457 $csv_record[ $col_heading ] = $this->enclose( $val ); 458 458 } 459 459 } 460 460 461 461 // $utils->log( "Exportable info : " . print_r( $csv_record, true ) ); 462 462 463 463 // Add the data to the list of 464 464 $this->csv_rows[] = $csv_record; 465 466 } 467 465 466 } 467 468 468 wp_cache_flush(); 469 469 470 470 //need to increase max running time? 471 471 $end = current_time( 'timestamp' ); 472 472 } 473 473 474 474 do_action( 'pmpro_after_members_list_csv_export' ); 475 475 } 476 476 477 477 /** 478 478 * Add the CSV header to the (new) file 479 479 */ 480 480 private function add_csv_header_to_file() { 481 481 482 482 $utils = Utilities::get_instance(); 483 483 484 484 // Open our designated temporary file 485 485 $file_handle = fopen( $this->file_name, 'a' ); 486 486 $header_type = 'header_key'; 487 487 488 488 $utils->log( "Adding " . count( $this->csv_headers ) . " header columns to {$this->file_name}" ); 489 489 490 490 //Add the CSV header to the file 491 491 fprintf( $file_handle, '%s', … … 497 497 $this->csv_headers ) 498 498 ) . "\n" ); 499 499 500 500 // Close the CSV file for now 501 501 fclose( $file_handle ); 502 502 } 503 503 504 504 /** 505 505 * Returns the expected header key for the requested DB Key … … 512 512 */ 513 513 private function map_keys( $key, $requested, $column_type = null ) { 514 514 515 515 $utils = Utilities::get_instance(); 516 516 517 517 foreach ( $this->header_map as $map_key => $field_def ) { 518 518 519 519 if ( $key === $map_key ) { 520 520 return $field_def[ $requested ]; 521 521 } 522 522 523 523 if ( 'header_key' === $requested && $field_def['header_key'] == $key ) { 524 524 525 525 return $map_key; 526 526 } 527 527 528 528 if ( 'db_key' === $requested && $field_def['db_key'] == $key ) { 529 530 529 530 531 531 if ( 'username' === $key ) { 532 532 return $map_key; 533 533 } 534 534 535 535 return $field_def[ $requested ]; 536 536 } 537 537 } 538 538 539 539 $utils->log( "No value (key) found for {$requested} key {$key}" ); 540 540 541 541 return null; 542 542 } 543 543 544 544 /** 545 545 * Return all of the user's metadata that we (may) care about … … 550 550 */ 551 551 private function load_user_meta( $user_id ) { 552 552 553 553 $meta_values = null; 554 554 555 555 // Returns array of meta keys containing array(s) of meta_values. 556 556 $um_values = get_user_meta( $user_id ); 557 557 558 558 // Process user metadata 559 559 if ( ! empty( $um_values ) ) { 560 560 561 561 $meta_values = new \stdClass(); 562 562 563 563 foreach ( $um_values as $key => $value ) { 564 564 565 565 $meta_values->{$key} = isset( $value[0] ) ? $value[0] : null; 566 566 } 567 567 } 568 568 569 569 return $meta_values; 570 571 } 572 570 571 } 572 573 573 /** 574 574 * Enclose the data we're adding to the export file … … 581 581 return "\"" . str_replace( "\"", "\\\"", $text ) . "\""; 582 582 } 583 583 584 584 /** 585 585 * @param array $csv_record … … 589 589 */ 590 590 public function load_export_value( $csv_record, $member ) { 591 591 592 592 $utils = Utilities::get_instance(); 593 593 594 594 $datetime_format = $this->set_datetime_format(); 595 595 $level = $utils->get_variable( 'membership_id', '' ); 596 596 597 597 // $utils->log( "For member: " . print_r( $member, true ) ); 598 598 599 599 // Process the membership data (by column) 600 600 foreach ( $this->default_columns as $field_def ) { 601 601 602 602 $column_type = $field_def[0]; 603 603 $column_name = $this->map_keys( $field_def[1], 'db_key', $column_type ); … … 607 607 $column_type 608 608 ); 609 609 610 610 // Process Join/Start dates for membership 611 611 if ( in_array( $column_name, array( 'user_registered', 'startdate' ) ) ) { 612 612 613 613 $column_value = date( 614 614 $datetime_format, … … 618 618 ); 619 619 } 620 620 621 621 // Process End of membership column 622 622 if ( 'enddate' == $column_name ) { 623 623 624 624 $enddate_value = $column_value; 625 625 626 626 if ( ! is_null( $member->membership_id ) && ! empty( $column_value ) ) { 627 627 628 628 // Membership is terminated or about to be terminated 629 629 if ( in_array( $level, array( "oldmembers", "expired", 'cancelled' ) ) && 630 630 ( ! empty( $column_value ) && '0000-00-00 00:00:00' !== $column_value ) ) { 631 631 632 632 $enddate_value = apply_filters( "pmpro_memberslist_expires_column", date( $datetime_format, strtotime( $column_value, current_time( 'timestamp' ) ) ), $member ); 633 633 634 634 } 635 635 636 636 if ( ! empty( $member->membership_id ) && ( empty( $column_value ) || '0000-00-00 00:00:00' === $column_value ) ) { 637 637 $enddate_value = apply_filters( "pmpro_memberslist_expires_column", null, $member ); 638 638 } 639 639 640 640 if ( ! empty( $member->membership_id ) && ( ! empty( $column_value ) && '0000-00-00 00:00:00' !== $column_value ) ) { 641 641 $enddate_value = date( $datetime_format, strtotime( $column_value, current_time( 'timestamp' ) ) ); 642 642 } 643 643 644 644 // Save record info for the column 645 645 $column_value = apply_filters( 'e20r-members-list-expires-col-value', $enddate_value, $member );; 646 646 } 647 647 } 648 648 649 649 /** 650 650 * Fetch the discount code data for this user/member 651 651 */ 652 652 if ( ! empty( $member->code_id ) && empty( $this->member_discount_info ) ) { 653 653 654 654 $utils->log( "Grab the discount code data for {$member->ID}/{$member->code_id}" ); 655 655 656 656 $this->member_discount_info = self::get_pmpro_discount_code( 657 657 $member->code_id, … … 660 660 ); 661 661 } 662 662 663 663 /** 664 664 * Fetch the discount code value for the user/member 665 665 */ 666 666 if ( $column_type === 'pmpro_discount_code' && ! empty( $this->member_discount_info ) ) { 667 667 668 668 $param = "pmpro_discount_{$column_name}"; 669 669 $column_value = $this->member_discount_info->{$param}; 670 670 } 671 672 671 672 673 673 if ( empty( $column_value ) ) { 674 674 $column_value = null; 675 675 } 676 676 677 677 // $utils->log( "Saving {$column_name} (looked for {$field_def[1]}): " . print_r( $column_value, true ) ); 678 678 679 679 // Save the entry info 680 680 $csv_record[ $column_name ] = $this->enclose( $column_value ); 681 681 } 682 682 683 683 // Clear the Discount Info (for the member) 684 684 $this->member_discount_info = null; 685 685 686 686 return $csv_record; 687 687 } 688 688 689 689 /** 690 690 * Configure the format to use for the export datetime value … … 693 693 */ 694 694 private function set_datetime_format() { 695 695 696 696 /** 697 697 * Filter to the format for the date (default is the WordPress General Setting value for Date) … … 705 705 $date_format = apply_filters( 'pmpro_memberslist_csv_dateformat', get_option( 'date_format' ) ); 706 706 $date_format = apply_filters( 'e20r-members-list-csv-fateformat', $date_format ); 707 707 708 708 /** 709 709 * Filter to the format for the time (default is the WordPress General Setting value for Time) … … 716 716 */ 717 717 $time_format = apply_filters( 'e20r-members-list-csv-timeformat', get_option( 'time_format' ) ); 718 718 719 719 // Assume that we want a valid MySQL DateTime format if date is Y-m-d 720 720 if ( 'Y-m-d' == $date_format && 'H:i' == $time_format ) { … … 723 723 $expected_format = "{$date_format} {$time_format}"; 724 724 } 725 725 726 726 /** 727 727 * Filter to the format for the time (default is the WordPress General Setting value for Time) … … 736 736 */ 737 737 $datetime_format = apply_filters( 'e20r-members-list-csv-datetime-format', $expected_format, $date_format, $time_format ); 738 738 739 739 return $datetime_format; 740 740 } 741 741 742 742 /** 743 743 * Return a value for the specified column (name) … … 750 750 */ 751 751 private function get_column_value( $member, $column_name, $column_type ) { 752 752 753 753 $utils = Utilities::get_instance(); 754 754 $dc_col_name = "pmpro_discount_code_"; 755 755 756 756 switch ( $column_type ) { 757 757 758 758 case 'wp_user': 759 759 case 'member_level': … … 762 762 $column_value = isset( $member->{$column_name} ) ? $member->{$column_name} : null; 763 763 break; 764 764 765 765 case 'pmpro_discount_code': 766 766 $dc_col_name = "{$dc_col_name}{$column_name}"; 767 767 $column_value = isset( $member->{$column_name} ) ? $member->{$column_name} : null; 768 768 break; 769 769 770 770 case 'meta_values': 771 771 $column_value = isset( $member->meta_values->{$column_name} ) ? $member->meta_values->{$column_name} : null; 772 772 break; 773 773 774 774 default: 775 775 $utils->log( "Using default type (type = {$column_type}) for {$column_name}" ); 776 776 $column_value = isset( $member->{$column_type}->{$column_name} ) ? $member->{$column_type}->{$column_name} : null; 777 777 778 778 /** 779 779 * Let 3rd party define the value to use for a 3rd party defined default column … … 790 790 $column_value = apply_filters( 'e20r-members-list-set-default-column-value', $column_value, $column_name, $column_type, $member ); 791 791 } 792 792 793 793 return $column_value; 794 794 } 795 795 796 796 /** 797 797 * Fetch discount code (ID and code) for the code ID/User ID/Level ID combination … … 804 804 */ 805 805 public static function get_pmpro_discount_code( $code_id, $user_id, $level_id ) { 806 806 807 807 global $wpdb; 808 808 809 809 $disSql = $wpdb->prepare( " 810 810 SELECT … … 819 819 $user_id 820 820 ); 821 821 822 822 $pmpro_discount_code = $wpdb->get_row( $disSql ); 823 823 824 824 // Make sure there's data for the discount code info 825 825 if ( empty( $pmpro_discount_code ) ) { … … 829 829 $pmpro_discount_code = $empty_dc; 830 830 } 831 831 832 832 return $pmpro_discount_code; 833 833 } 834 834 835 835 /** 836 836 * Save the data rows to the temporary Export file 837 837 */ 838 838 public function save_data_for_export() { 839 839 840 840 $utils = Utilities::get_instance(); 841 841 $utils->log( "Saving " . count( $this->csv_rows ) . " records to {$this->file_name}. " ); 842 842 843 843 $fh = fopen( $this->file_name, 'a' ); 844 844 845 845 /** 846 846 * @var $row_data -> $csv_entry[ $col_heading ] = $this->enclose( $val ); 847 847 */ 848 848 foreach ( $this->csv_rows as $row_id => $row_data ) { 849 849 850 850 $data = array(); 851 851 852 852 foreach ( $this->csv_headers as $col_key ) { 853 853 854 854 /* 855 855 if ( 'ID' == $col_key ) { … … 857 857 } 858 858 */ 859 859 860 860 $col_name = $this->map_keys( $col_key, 'db_key' ); 861 861 862 862 $value = isset( $row_data[ $col_name ] ) ? $row_data[ $col_name ] : $this->enclose( null ); 863 863 $data[] = $value; 864 864 } 865 865 866 866 $file_line = implode( ',', $data ) . "\r\n"; 867 867 fprintf( $fh, '%s', $file_line ); 868 868 } 869 869 870 870 fclose( $fh ); 871 871 $utils->log( "Saved data to {$this->file_name}" ); 872 872 } 873 873 874 874 /** 875 875 * Send the .CSV to the requesting browser 876 876 */ 877 877 public function return_content() { 878 878 879 879 $utils = Utilities::get_instance(); 880 880 $utils->log( "Headers have been sent already..?!? " . ( headers_sent() ? 'Yes' : 'No' ) ); … … 882 882 // Send the data to the recipient browser 883 883 if ( ! empty( $this->headers ) && false === headers_sent() && file_exists( $this->file_name ) ) { 884 884 885 885 $sent_content = ob_get_clean(); 886 886 $utils->log("Browser received: " . print_r( $sent_content, true) ); 887 887 888 888 if ( version_compare( phpversion(), '5.3.0', '>' ) ) { 889 889 890 890 //Clear the file cache for the export file 891 891 clearstatcache( true, $this->file_name ); … … 894 894 clearstatcache(); 895 895 } 896 896 897 897 //Set the download size for the file 898 898 $this->headers[] = "Content-Length: " . filesize( $this->file_name ); 899 899 900 900 //Set transmission (PHP) headers 901 901 foreach ( $this->headers as $header ) { 902 902 header( $header . "\r\n" ); 903 903 } 904 904 905 905 // Disable compression for the duration of file download 906 906 if ( ini_get( 'zlib.output_compression' ) ) { 907 907 ini_set( 'zlib.output_compression', 'Off' ); 908 908 } 909 909 910 910 // Bug fix for Flywheel Hosted like hosts where fpassthru() is disabled 911 911 if ( function_exists( 'fpassthru' ) ) { … … 917 917 readfile( $this->file_name ); 918 918 } 919 919 920 920 // Remove the temp file 921 921 unlink( $this->file_name ); 922 922 exit(); 923 923 924 924 } else { 925 925 $msg = __( "Unable to send the .CSV file to your browser!", 'e20r-members-list' ); … … 928 928 } 929 929 } 930 930 931 931 /** 932 932 * Returns the column name to use from the specified $header_name … … 937 937 */ 938 938 private function map_header_to_column( $header_name ) { 939 939 940 940 return isset( $this->header_map[ $header_name ]['header_key'] ) ? $this->header_map[ $header_name ]['header_key'] : null; 941 941 } -
e20r-members-list/tags/6.0/class/members-list/class.members-list-page.php
r2228121 r2458244 1 1 <?php 2 2 /** 3 * Copyright (c) 2018-20 19- Eighty / 20 Results by Wicked Strong Chicks.3 * Copyright (c) 2018-2021 - Eighty / 20 Results by Wicked Strong Chicks. 4 4 * ALL RIGHTS RESERVED 5 5 * … … 24 24 25 25 class Members_List_Page { 26 26 27 27 /** 28 28 * Instance of the Members_List_Page class (Singleton) … … 31 31 */ 32 32 private static $instance = null; 33 33 34 34 /** 35 35 * Holds the list of members (Members_List class) … … 37 37 */ 38 38 public $member_list; 39 39 40 40 /** 41 41 * Holds the standard Utilities class … … 44 44 */ 45 45 private $utils; 46 46 47 47 /** 48 48 * Members_List_Page constructor. Loads required menu(s) & screen options … … 50 50 private function __construct() { 51 51 } 52 52 53 53 /** 54 54 * Creates or returns an instance of the Members_List_Page class. … … 57 57 */ 58 58 public static function get_instance() { 59 59 60 60 if ( null === self::$instance ) { 61 61 self::$instance = new self; 62 62 } 63 63 64 64 return self::$instance; 65 65 } 66 66 67 67 /** 68 68 * Removes the old Member List page & appends a new one to the "Memberships" admin bar node 69 69 */ 70 70 public static function admin_bar_menu() { 71 71 72 72 $is_pmpro_v2 = version_compare( PMPRO_VERSION, '2.0', 'ge' ); 73 73 74 74 if ( true === $is_pmpro_v2 ) { 75 75 return; 76 76 } 77 77 78 78 global $wp_admin_bar; 79 80 79 80 81 81 if ( ! is_admin_bar_showing() || ( ! is_super_admin() && ( ! current_user_can( 'manage_options' ) ) && ! current_user_can( 'pmpro_memberslist' ) && ! current_user_can( 'e20r_memberslist' ) ) ) { 82 82 if ( ! is_null( self::$instance ) ) { 83 83 self::$instance->utils->log( "Unable to change admin bar (wrong capabilities for user)" ); 84 84 } 85 85 86 86 return; 87 87 } 88 88 89 89 $wp_admin_bar->remove_menu( 'pmpro-members-list' ); 90 90 $wp_admin_bar->remove_node( 'pmpro-members-list' ); 91 91 92 92 //Add the (new) Members List page to the admin_bar menu 93 93 $wp_admin_bar->add_menu( array( … … 102 102 ) ); 103 103 } 104 104 105 105 /** 106 106 * Screen Option option(s) for the Members List page. … … 112 112 */ 113 113 public static function set_screen( $status, $option, $value ) { 114 114 115 115 self::$instance->utils->log( "Saving screen option (page: {$option})? {$value} vs {$status}" ); 116 116 117 117 if ( 'per_page' == $option ) { 118 118 return $value; 119 119 } 120 120 121 121 return $status; 122 122 } 123 123 124 124 /** 125 125 * Load Action and Filter hooks for the Members List page 126 126 */ 127 127 public function load_hooks() { 128 128 129 129 $this->utils = Utilities::get_instance(); 130 130 131 131 // Filters 132 132 add_filter( 'set-screen-option', array( $this, 'set_screen' ), 10, 3 ); 133 133 add_filter( 'set_url_scheme', array( $this, 'add_to_pagination' ), 10, 3 ); 134 134 135 135 // Actions 136 136 add_action( 'admin_menu', array( $this, 'plugin_menu' ), 9999 ); … … 138 138 // add_action( 'admin_bar_menu', array( $this, 'admin_bar_menu' ), 9999 ); 139 139 add_action( 'admin_enqueue_scripts', array( $this, 'load_scripts_styles' ) ); 140 141 } 142 140 141 } 142 143 143 /** 144 144 * Load the custom CSS and JavaScript for the Members List 145 145 */ 146 146 public function load_scripts_styles( $hook_suffix ) { 147 147 148 148 if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || false == DOING_AJAX ) && 1 === preg_match( '/(pmpro|e20r)-memberslist/', $hook_suffix ) ) { 149 149 150 150 wp_enqueue_style( 'jquery-ui', '//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css' ); 151 151 wp_enqueue_script( 'jquery-ui-datepicker' ); 152 152 153 153 wp_enqueue_style( 'e20r-memberslist-page', plugins_url( "/css/e20r-memberslist-page.css", __FILE__ ), array( 'pmpro_admin' ), E20R_MEMBERSLIST_VER ); 154 154 155 155 wp_register_script( 'e20r-memberslist-page', plugins_url( "/js/e20r-memberslist-page.js", __FILE__ ), array( 'jquery' ), E20R_MEMBERSLIST_VER, true ); 156 156 157 157 // $is_pmpro_v2 = version_compare( PMPRO_VERSION, '2.0', 'ge' ); 158 158 // $url = ( false === $is_pmpro_v2 ) ? 'e20r-memberslist' : 'pmpro-memberslist'; 159 159 160 160 wp_localize_script( 'e20r-memberslist-page', 'e20rml', 161 161 array( … … 168 168 ) 169 169 ); 170 170 171 171 wp_enqueue_script( 'e20r-memberslist-page' ); 172 172 } 173 173 } 174 174 175 175 /** 176 176 * Point Members List menu handler(s) to this plugin 177 177 */ 178 178 public function plugin_menu() { 179 179 180 180 $this->utils->log( "Headers sent? " . ( headers_sent() ? 'Yes' : 'No' ) ); 181 182 181 182 183 183 $pmpro_menu_slug = 'pmpro-membershiplevels'; 184 184 $is_pmpro_v2 = version_compare( PMPRO_VERSION, '2.0', 'ge' ); 185 185 186 186 if ( defined( 'PMPRO_VERSION' ) && $is_pmpro_v2 ) { 187 187 $pmpro_menu_slug = 'pmpro-dashboard'; 188 188 } 189 189 190 190 $this->utils->log( "Remove the default members list page.. (under: {$pmpro_menu_slug})" ); 191 191 192 192 // Just replace the action that loads the PMPro Members List 193 193 194 194 $hookname = get_plugin_page_hookname( 'pmpro-memberslist', $pmpro_menu_slug ); 195 195 $this->utils->log("Found hook name: {$hookname}. Sent yet? " . (headers_sent() ? 'Yes' : 'No') ); … … 197 197 add_action( $hookname, array( $this, 'memberslist_settings_page' ), 11 ); 198 198 add_action( "load-memberships_page_pmpro-memberslist", array( $this, 'screen_option' ), 9999 ); 199 199 200 200 /* 201 201 // Unhook old members list functionality (pre v2.0 of PMPro) 202 202 if ( false == ( $page = remove_submenu_page( $pmpro_menu_slug, 'pmpro-memberslist' ) ) ) { 203 203 204 204 $this->utils->log( "Unable to remove the default membership levels page!" ); 205 205 pmpro_setMessage( __( 'Error while attempting to reassign member list menu', E20R_Members_List::plugin_slug ), 'error' ); 206 206 207 207 return; 208 208 } 209 209 210 210 // Load the (new) WP_Table_List based Members List 211 211 $hook = add_submenu_page( … … 217 217 array( $this, 'memberslist_settings_page' ) 218 218 ); 219 219 220 220 // Show error if we're unable to update the members list 221 221 if ( false === $hook ) { 222 222 223 223 $this->utils->log( "Unable to load the replacement Members List page!" ); 224 224 pmpro_setMessage( __( "Unable to load Members List menu entry", "e20r-members-list" ), "error" ); 225 225 226 226 return; 227 227 } 228 228 229 229 // Process the screen option 230 230 add_action( "load-memberships_page_pmpro-memberslist", array( $this, 'screen_option' ), 9999 ); 231 231 */ 232 232 } 233 233 234 234 /** 235 235 * Add parameters to limit/include records to any members list page URI … … 242 242 */ 243 243 public function add_to_pagination( $url, $scheme, $original_scheme ) { 244 244 245 245 $page = $this->utils->get_variable( 'page', '' ); 246 246 247 247 if ( 1 === preg_match( "/{$_SERVER['HTTP_HOST']}\/wp-admin\/admin.php\?page=pmpro-memberslist/i", $url ) ) { 248 248 249 249 $arg_list = array(); 250 250 251 251 $level = $this->utils->get_variable( 'level', '' ); 252 252 $find = $this->utils->get_variable( 'find', '' ); 253 253 254 254 if ( ! empty( $level ) ) { 255 255 $arg_list['level'] = $level; 256 256 } 257 257 258 258 if ( ! empty( $find ) ) { 259 259 $arg_list['find'] = $find; 260 260 } 261 261 262 262 /** 263 263 * @filter e20r_memberslist_pagination_args - Add filtering to the URI (to preserve it for pagination ,etc) … … 266 266 */ 267 267 $arg_list = apply_filters( 'e20r_memberslist_pagination_args', $arg_list ); 268 268 269 269 // Encode the new URI variables 270 270 foreach ( $arg_list as $a_key => $value ) { 271 271 $arg_list[ $a_key ] = urlencode_deep( urldecode_deep( $value ) ); 272 272 } 273 273 274 274 $url = add_query_arg( $arg_list, $url ); 275 275 } 276 276 277 277 return $url; 278 278 } 279 279 280 280 /** 281 281 * Configure options to use for "Screen Options" on Members_List_Page page. 282 282 */ 283 283 public function screen_option() { 284 284 285 285 $options = 'per_page'; 286 286 287 287 $args = array( 288 288 'label' => _x( "Members per page", "members per page (screen options)", "e20r-members-list" ), … … 290 290 'option' => $options, 291 291 ); 292 292 293 293 add_screen_option( $options, $args ); 294 294 295 295 $this->member_list = new Members_List(); 296 296 } 297 297 298 298 /** 299 299 * Load the e20rMembersList page content 300 300 */ 301 301 public function memberslist_settings_page() { 302 302 303 303 $this->utils->log("Have we sent content? " . (headers_sent() ? 'yes' : 'no')); 304 305 if ( ! function_exists( 'pmpro_loadTemplate' ) ) { 306 $this->utils->log("Fatal: Paid Memberships Pro is not loaded on site!"); 307 return ""; 308 } 309 304 310 global $pmpro_msg; 305 311 global $pmpro_msgt; 306 312 307 313 // TODO: Fix this to match required request variables. 308 314 $search = $this->utils->get_variable( 'find', '' ); 309 315 $level = $this->utils->get_variable( 'level', '' ); 310 316 311 317 echo pmpro_loadTemplate( 'admin_header', 'local', 'adminpages' ); 312 318 313 319 $search_array = apply_filters( 'e20r_memberslist_exportcsv_search_args', array( 314 320 'action' => 'memberslist_csv', … … 318 324 ) 319 325 ); 320 326 321 327 $csv_url = add_query_arg( 322 328 $search_array, 323 329 get_admin_url( get_current_blog_id(), 'admin-ajax.php' ) 324 330 ); 325 331 326 332 $e20r_error_msgs = $this->utils->get_message( 'error' ); 327 333 $e20r_warning_msgs = $this->utils->get_message( 'warning' ); 328 334 $e20r_info_msgs = $this->utils->get_message( 'info' ); 329 335 330 336 $top_list = array( 331 337 'active' => __( 'Active Members', E20R_Members_List::plugin_slug ), 332 338 'all' => __( 'All Members', E20R_Members_List::plugin_slug ), 333 339 ); 334 340 335 341 $bottom_list = array( 336 342 'cancelled' => __( 'Cancelled Members', 'paid-membership-pro' ), … … 338 344 'oldmembers' => __( 'Old Members', 'paid-membership-pro' ), 339 345 ); 340 346 341 347 $level_list = array(); 342 343 $list = function_exists( 'pmpro_getAllLevels' ) ? pmpro_getAllLevels( true, true ) : array(); 344 348 349 $list = function_exists( 'pmpro_getAllLevels' ) ? 350 pmpro_getAllLevels( true, true ) : 351 array(); 352 345 353 foreach ( $list as $item ) { 346 354 $level_list[ $item->id ] = $item->name; 347 355 } 348 356 349 357 $option_list = $top_list + $level_list + $bottom_list; 350 358 351 359 if ( ! empty( $pmpro_msg ) ) { ?> 352 360 … … 383 391 $label = __( 'Update List', E20R_Members_List::plugin_slug ); 384 392 $button_def = 'button'; 385 393 386 394 if ( isset( $_REQUEST['find'] ) && ! empty( $_REQUEST['find'] ) ) { 387 395 388 396 $label = __( 'Clear Search', E20R_Members_List::plugin_slug ); 389 397 $button_def .= " button-primary"; … … 421 429 </form> 422 430 </div> 423 431 424 432 <?php 425 433 echo pmpro_loadTemplate( 'admin_footer', 'local', 'adminpages' ); 426 434 } 427 435 428 436 /** 429 437 * Deactivated __clone() method for the Members_List_Page class … … 431 439 private function __clone() { 432 440 } 433 441 434 442 } -
e20r-members-list/tags/6.0/class/members-list/class.members-list.php
r2306383 r2458244 3 3 * License: 4 4 5 Copyright 2016-20 19- Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected])5 Copyright 2016-2021 - Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected]) 6 6 7 7 This program is free software; you can redistribute it and/or modify … … 73 73 */ 74 74 private $total_members_found = null; 75 76 /** 77 * The total number of records in the PMPro Membership DB 78 * 79 * @var int $total_member_records 80 */ 81 private $total_member_records = 0; 82 83 /** 84 * The status of the membership when searching for totals 85 * 86 * @var string $membership_status 87 */ 88 private $membership_status = 'active'; 89 75 90 /** 76 91 * The completed SQL query used to generate the membership list … … 176 191 $this->action = $this->utils->get_variable( 'action', '' ); 177 192 193 if ( ! empty( $level ) ) { 194 switch ($level) { 195 case 'cancelled': 196 $this->membership_status = array( 'cancelled' ); 197 break; 198 case 'expired': 199 $this->membership_status = array( 'expired' ); 200 break; 201 case 'old': 202 $this->membership_status = array( 'cancelled', 'expired' ); 203 break; 204 default: 205 $this->membership_status = array('active'); 206 } 207 } 178 208 /** 179 209 * The default Members List columns to display (with labels) … … 204 234 } 205 235 236 $this->total_member_records = $this->get_member_record_count(); 237 206 238 /** 207 239 * Prepare the Export bulk action … … 211 243 add_action( 'e20r_memberslist_process_action', array( $this, 'export_members' ), 10, 3 ); 212 244 } 245 } 246 247 /** 248 * Private function to capture the count of records in the membership database 249 * 250 * @return int|null 251 */ 252 private function get_member_record_count() { 253 254 $status = $this->utils->get_variable( 'level', 'active' ); 255 256 // Get SQL for all records in the paginated data 257 $this->generate_member_sql( $status ); 258 $records = $this->get_members( -1, -1, $status ); 259 260 return is_countable( $records ) ? count( $records ) : 0; 213 261 } 214 262 … … 294 342 295 343 // Do we need to limit? 296 $level = $this->utils->get_variable( 'level', '' ); 297 298 if ( empty( $level ) ) { 299 $level = 'active'; 300 } 344 $level = $this->utils->get_variable( 'level', 'active' ); 301 345 302 346 // Get the current page number … … 309 353 // BUG FIX: Handle situation(s) where there are no records found 310 354 if ( null !== $this->items ) { 311 $total_items = count($this->items );312 355 $this->utils->log( 313 356 sprintf( 314 357 "Configure pagination for %d total records and %d counted (returned) records", 315 $t otal_items,358 $this->total_member_records, 316 359 (is_countable( $this->items ) ? count( $this->items ) : 0) 317 360 ) … … 323 366 $this->set_pagination_args( 324 367 array( 325 'total_items' => $t otal_items,368 'total_items' => $this->total_member_records, 326 369 'per_page' => $per_page, 327 'total_pages' => ceil( $t otal_items / $per_page ),370 'total_pages' => ceil( $this->total_member_records / $per_page ), 328 371 ) 329 372 ); … … 593 636 global $wpdb; 594 637 595 $this->generate_member_sql( $per_page, $page_number, $status ); 638 // Get Pagination SQL 639 $this->sqlQuery = $this->generate_member_sql( $status, $per_page, $page_number, ); 596 640 597 641 // Fetch the data 598 $result = $wpdb->get_results( $this->sqlQuery, ARRAY_A ); 642 $result = $wpdb->get_results( $this->sqlQuery, ARRAY_A ); 643 599 644 if ( ! empty( $result ) ) { 600 645 $this->utils->log("Found records in DB..."); 601 646 $this->total_members_found = $wpdb->num_rows; 602 647 } 603 /*604 if ( ! empty( $result ) ) {605 $this->total_members_found = $result->num_rows;606 // $this->total_members_found = $wpdb->get_var( "SELECT FOUND_ROWS() AS found_rows" );607 }608 */609 648 610 649 // Return the result set unless an error occurred. … … 640 679 * @param int $page_number 641 680 * @param string $status 642 */ 643 private function generate_member_sql( $per_page, $page_number, $status = 'active' ) { 681 * 682 * @return string - Returns the SQL statement 683 */ 684 private function generate_member_sql( $status = 'active', $per_page = -1, $page_number = -1 ) { 644 685 645 686 $this->utils->log( "Called by: " . $this->utils->_who_called_me() ); … … 839 880 "; 840 881 841 // Define the SQL statement 842 $this->sqlQuery = $sql . self::$sql_from; 843 844 $this->utils->log( "SQL for fetching membership records:\n {$this->sqlQuery}" ); 882 // Created the SQL statement 883 $sqlQuery = $sql . self::$sql_from; 884 885 $this->utils->log( "SQL for fetching membership records:\n {$sqlQuery}" ); 886 return $sqlQuery; 845 887 } 846 888 -
e20r-members-list/tags/6.0/class/members-list/class.sort-by-meta.php
r2025537 r2458244 1 1 <?php 2 2 /** 3 * Copyright (c) 2018-20 19- Eighty / 20 Results by Wicked Strong Chicks.3 * Copyright (c) 2018-2021 - Eighty / 20 Results by Wicked Strong Chicks. 4 4 * ALL RIGHTS RESERVED 5 5 * … … 21 21 22 22 if ( ! class_exists( 'E20R\Members_List\Support\Sort_By_Meta' ) ) { 23 23 24 24 class Sort_By_Meta { 25 25 26 26 private $meta_key; 27 27 28 28 private $order = 'DESC'; 29 29 30 30 /** 31 31 * Sort_Meta constructor. … … 38 38 $this->order = strtoupper( $order ); 39 39 } 40 40 41 41 /** 42 42 * @param array $a … … 48 48 */ 49 49 public function sort_records( $a, $b ) { 50 50 51 51 // $utils = Utilities::get_instance(); 52 52 // $utils->log( "A: " . print_r( $a, true)); 53 53 // $utils->log("B: " . print_r( $b, true)); 54 54 55 55 $a_user_id = is_array( $a ) ? $a['user_id'] : ( is_a( $a, '\WP_User' ) ? $a->ID : null ); 56 56 $b_user_id = is_array( $b ) ? $b['user_id'] : ( is_a( $b, '\WP_User' ) ? $b->ID : null ); 57 57 58 58 if ( is_null($a_user_id ) || is_null( $b_user_id ) ) { 59 59 return false; 60 60 } 61 61 62 62 // Check if the field specified exists in the data 63 63 if ( ! isset( $a[ $this->meta_key ] ) ) { 64 64 $a_value = get_user_meta( $a_user_id, $this->meta_key, true ); 65 65 66 66 } else { 67 67 $a_value = $b[ $this->meta_key ]; 68 68 } 69 69 70 70 if ( ! isset( $b[ $this->meta_key ] ) ) { 71 71 $b_value = get_user_meta( $b_user_id, $this->meta_key, true ); … … 73 73 $b_value = $b[ $this->meta_key ]; 74 74 } 75 75 76 76 if ( $a_value == $b_value ) { 77 77 return 0; 78 78 } 79 79 80 80 if ( 'DESC' == $this->order ) { 81 81 return ( $a_value > $b_value ? 1 : - 1 ); 82 82 } 83 83 84 84 if ( 'ASC' == $this->order ) { 85 85 return ( $a_value < $b_value ? 1 : - 1 ); 86 86 } 87 88 87 88 89 89 } 90 90 91 91 } 92 92 } -
e20r-members-list/tags/6.0/class/members-list/css/e20r-memberslist-page.css
r2025537 r2458244 1 1 /* 2 * Copyright (c) 2018-20 19. - Eighty / 20 Results by Wicked Strong Chicks.2 * Copyright (c) 2018-2021. - Eighty / 20 Results by Wicked Strong Chicks. 3 3 * ALL RIGHTS RESERVED 4 4 * -
e20r-members-list/tags/6.0/class/members-list/js/e20r-memberslist-page.js
r2228121 r2458244 2 2 * License: 3 3 4 Copyright 2016-20 19- Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected])4 Copyright 2016-2021 - Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected]) 5 5 6 6 This program is free software; you can redistribute it and/or modify -
e20r-members-list/trunk/README.txt
r2306383 r2458244 12 12 13 13 Extensible, sortable & bulk action capable members listing tool for Paid Memberships Pro. This plugin is a complete replacement for the "Members List" functionality in PMPro and supports most of the same filters and hooks. The key differences have to do with managing columns. Now you can also use the [standard WordPress filters](https://developer.wordpress.org/reference/classes/wp_list_table/) to columns you can add/remove/make sortable, additional bulk actions, etc. 14 15  14 16 15 17 == Installation == … … 189 191 == Changelog == 190 192 193 == 6.0 == 194 * BUG FIX: Didn't paginate correctly because the LIMIT logic caused us to not return the full number of records for the level/status 195 * BUG FIX: Potentially a fatal PHP error 196 * BUG FIX: Bad path to downloadable archive 197 * BUG FIX: Didn't (always) import the database needed for testing 198 * BUG FIX: Readme directions for installation were imprecise 199 * BUG FIX: Removed copy/paste Utilities module and using git subtree instead 200 * BUG FIX: Updated copyright notice 201 202 191 203 == 5.10 == 192 204 * BUG FIX: Fatal error in Utilities library -
e20r-members-list/trunk/class.e20r-members-list.php
r2306383 r2458244 4 4 Plugin URI: https://wordpress.org/plugins/e20r-members-list 5 5 Description: Extensible, sortable & bulk action capable members listing + export to CSV tool for Paid Memberships Pro. 6 Version: 5.106 Version: 6.0 7 7 Author: Thomas Sjolshagen @ Eighty / 20 Results by Wicked Strong Chicks, LLC <[email protected]> 8 8 Author URI: https://eighty20results.com/thomas-sjolshagen/ … … 11 11 License: 12 12 13 Copyright 2016 - 202 0(c) Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected])13 Copyright 2016 - 2021 (c) Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected]) 14 14 15 15 This program is free software; you can redistribute it and/or modify … … 33 33 34 34 if ( ! defined( 'E20R_MEMBERSLIST_VER' ) ) { 35 define( 'E20R_MEMBERSLIST_VER', ' 5.10' );35 define( 'E20R_MEMBERSLIST_VER', '6.0' ); 36 36 } 37 37 -
e20r-members-list/trunk/class/members-list/class.bulk-cancel.php
r2025537 r2458244 1 1 <?php 2 2 /** 3 * Copyright (c) 2018-20 19- Eighty / 20 Results by Wicked Strong Chicks.3 * Copyright (c) 2018-2021 - Eighty / 20 Results by Wicked Strong Chicks. 4 4 * ALL RIGHTS RESERVED 5 5 * … … 23 23 24 24 class Bulk_Cancel { 25 25 26 26 /** 27 27 * Instance of this class … … 36 36 */ 37 37 private $operation = null; 38 38 39 39 /** 40 40 * List of member IDs to update … … 43 43 */ 44 44 private $members_to_update = array(); 45 45 46 46 /** 47 47 * Bulk_Cancel constructor (singleton) … … 51 51 private function __construct() { 52 52 } 53 53 54 54 /** 55 55 * The __clone() method for Bulk_Cancel() (singleton class) … … 58 58 */ 59 59 private function __clone() {} 60 60 61 61 /** 62 62 * Process cancellations for all members/membership_ids 63 63 */ 64 64 public function cancel() { 65 65 66 66 // Process all User & level ID for the single action. 67 67 $failed = array(); 68 68 69 69 $utils = Utilities::get_instance(); 70 70 $utils->log("Cancelling " . count( $this->members_to_update ) . " members"); 71 71 72 72 // Process all selected records/members 73 73 foreach ( $this->members_to_update as $key => $cancel_info ) { 74 74 75 75 if ( false == $this->cancel_member( $cancel_info['user_id'], $cancel_info['level_id'] ) ) { 76 76 $failed[] = $cancel_info['user_id']; // FIXME: Add level info for multiple membership levels 77 77 } 78 78 } 79 79 80 80 //Check for errors & display error banner if we got one. 81 81 if ( ! empty( $failed ) ) { 82 82 83 83 $message = sprintf( 84 84 __( "Unable to cancel membership(s) for the following user IDs: %s", "e20r-members-list" ), 85 85 implode( ', ', $failed ) 86 86 ); 87 87 88 88 if ( function_exists( 'pmpro_setMessage' ) ) { 89 89 pmpro_setMessage( $message, "error" ); … … 91 91 global $msg; 92 92 global $msgt; 93 93 94 94 $msg = $message; 95 95 $msgt = 'error'; 96 96 } 97 97 98 98 } 99 99 } 100 100 101 101 /** 102 102 * The cancel member action … … 108 108 */ 109 109 public static function cancel_member( $id, $level_id = null ) { 110 110 111 111 if ( function_exists( 'pmpro_cancelMembershipLevel' ) ) { 112 112 return pmpro_cancelMembershipLevel( $level_id, $id, 'admin_cancelled' ); … … 114 114 return false; 115 115 } 116 116 117 117 } 118 118 119 119 /** 120 120 * Get or create an instance of the Bulk_Cancel class … … 123 123 */ 124 124 public static function get_instance() { 125 125 126 126 if ( is_null( self::$instance ) ) { 127 127 self::$instance = new self; 128 128 } 129 129 130 130 return self::$instance; 131 131 } 132 132 133 133 /** 134 134 * Set the list of members & their levels to update … … 139 139 $this->members_to_update = $member_info; 140 140 } 141 141 142 142 /** 143 143 * Return the list of members being updated -
e20r-members-list/trunk/class/members-list/class.bulk-update.php
r2025537 r2458244 1 1 <?php 2 2 /** 3 * Copyright (c) 2018-20 19- Eighty / 20 Results by Wicked Strong Chicks.3 * Copyright (c) 2018-2021 - Eighty / 20 Results by Wicked Strong Chicks. 4 4 * ALL RIGHTS RESERVED 5 5 * … … 24 24 25 25 class Bulk_Update { 26 26 27 27 /** 28 28 * Instance of this class … … 31 31 */ 32 32 private static $instance = null; 33 33 34 34 /** 35 35 * Update operation to perform … … 38 38 */ 39 39 private $operation = null; 40 40 41 41 /** 42 42 * List of members to update … … 45 45 */ 46 46 private $members_to_update = array(); 47 47 48 48 /** 49 49 * Bulk_Update constructor (singleton) … … 53 53 private function __construct() { 54 54 } 55 55 56 56 /** 57 57 * __clone() method for Bulk_Update() (singleton class) … … 60 60 */ 61 61 private function __clone(){} 62 62 63 63 /** 64 64 * Get or create an instance of the Bulk_Update class … … 67 67 */ 68 68 public static function get_instance() { 69 69 70 70 if ( is_null( self::$instance ) ) { 71 71 self::$instance = new self; 72 72 } 73 73 74 74 return self::$instance; 75 75 } 76 76 77 77 /** 78 78 * Handle bulk update (for core member list columns). Triggers action for external bulk update activities/fields … … 81 81 */ 82 82 public function update() { 83 83 84 84 $utils = Utilities::get_instance(); 85 85 $update_errors = array(); 86 86 $level_failed = array(); 87 87 88 88 $utils->log("User count to update: " . count( $this->members_to_update) ); 89 89 $utils->log("Request: " . print_r( $_REQUEST, true )); 90 90 91 91 /** 92 92 * Process build-in edit fields for the specified members to update 93 93 */ 94 94 foreach ( $this->members_to_update as $key => $user_info ) { 95 95 96 96 $level_changed = false; 97 97 $old_user_level = $utils->get_variable( "e20r-members-list-db_membership_id_{$user_info['user_id']}", 0 ); 98 98 $new_user_level = $utils->get_variable( "e20r-members-list-new_membership_id_{$user_info['user_id']}", 0 ); 99 99 $record_id = $utils->get_variable( "e20r-members-list-db_record_id_{$user_info['user_id']}", 0 ); 100 100 101 101 if ( empty( $user_info['user_id'] ) ) { 102 102 $utils->log("User ID is NULL. Returning!" ); 103 103 return false; 104 104 } 105 105 106 106 $utils->log("Have to update level for {$user_info['user_id']}? {$new_user_level}" ); 107 107 108 108 // Update the membership level for the user we're processing 109 109 if ( !empty( $new_user_level) && $old_user_level !== $new_user_level ) { 110 110 111 111 if ( false === $this->update_membership( $user_info['user_id'], $old_user_level, $new_user_level ) ) { 112 112 113 113 // Add to list of failed updates 114 114 $level_failed[] = array( … … 122 122 } 123 123 } 124 124 125 125 $old_startdate = $utils->get_variable( "e20r-members-list-db_startdate_{$user_info['user_id']}", '' ); 126 126 $new_startdate = $utils->get_variable( "e20r-members-list-new_startdate_{$user_info['user_id']}", '' ); 127 127 128 128 $utils->log("Have to update start date for {$user_info['user_id']}? N:{$new_startdate} vs O:{$old_startdate}" ); 129 129 130 130 if ( $old_startdate !== $new_startdate ) { 131 131 132 132 if ( empty( $new_startdate ) ) { 133 133 $utils->log("Error: Start date cannot be empty!, using old start date"); 134 134 $new_startdate = $old_startdate; 135 135 } 136 136 137 137 if ( false === $this->update_date( 'startdate', $user_info['user_id'], $old_user_level, $new_user_level, $new_startdate, $record_id, $level_changed ) ) { 138 138 $startdate_failed[] = array( … … 144 144 } 145 145 } 146 146 147 147 $old_enddate = $utils->get_variable( "e20r-members-list-db_enddate_{$user_info['user_id']}", '' ); 148 148 $new_enddate = $utils->get_variable( "e20r-members-list-new_enddate_{$user_info['user_id']}", '' ); 149 149 150 150 $utils->log("Have to update end date for {$user_info['user_id']}? N:{$new_enddate} vs O:{$old_enddate}"); 151 151 152 152 if ( $old_enddate !== $new_enddate ) { 153 153 154 154 $utils->log("Updating end date to {$new_enddate}"); 155 155 156 156 if ( false === $this->update_date( 'enddate', $user_info['user_id'], $old_user_level, $new_user_level, $new_enddate, $record_id, $level_changed ) ) { 157 157 $enddate_failed[] = array( … … 163 163 } 164 164 } 165 165 166 166 $old_status = $utils->get_variable( "e20r-members-list-db_status_{$user_info['user_id']}", '' ); 167 167 $new_status = $utils->get_variable( "e20r-members-list-new_status_{$user_info['user_id']}", '' ); 168 168 169 169 $utils->log("Have to update status for {$user_info['user_id']}? {$new_status}" ); 170 170 171 171 if ( $old_status !== $new_status ) { 172 172 173 173 if ( false === $this->update_status( $record_id, $new_status ) ) { 174 174 $status_failed[] = array( … … 181 181 } 182 182 } 183 183 184 184 /** 185 185 * Trigger action for bulk update (allows external handling of bulk update if needed/desired) … … 190 190 */ 191 191 do_action( 'e20r_memberslist_process_bulk_updates', $this->members_to_update ); 192 192 193 193 /** 194 194 * Error handling for build-in edit fields 195 195 */ 196 196 $msg_template = __( 'Error updating data for %1$s (ID: %2$d). Could not update %3$s from %4$s to %5$s (current membership level: \'%6$s\')', 'e20r-members-list' ); 197 197 198 198 if ( ! empty( $level_failed ) ) { 199 199 200 200 foreach ( $level_failed as $info ) { 201 201 $user = get_user_by( 'ID', $info['user_id'] ); 202 202 $new_user_level = pmpro_getLevel( $info['new_level'] ); 203 203 $old_user_level = pmpro_getLevel( $info['old_level'] ); 204 204 205 205 $update_errors[] = sprintf( 206 206 $msg_template, … … 213 213 ); 214 214 } 215 216 } 217 215 216 } 217 218 218 if ( ! empty( $enddate_failed ) ) { 219 219 foreach ( $enddate_failed as $info ) { 220 220 $user = get_user_by( 'ID', $info['user_id'] ); 221 221 $user_level = pmpro_getLevel( $info['level_id'] ); 222 222 223 223 $update_errors[] = sprintf( 224 224 $msg_template, … … 232 232 } 233 233 } 234 234 235 235 if ( ! empty( $startdate_failed ) ) { 236 236 foreach ( $startdate_failed as $info ) { 237 237 $user = get_user_by( 'ID', $info['user_id'] ); 238 238 $user_level = pmpro_getLevel( $info['level_id'] ); 239 239 240 240 $update_errors[] = sprintf( 241 241 $msg_template, … … 249 249 } 250 250 } 251 251 252 252 if ( ! empty( $status_failed ) ) { 253 253 254 254 foreach ( $status_failed as $info ) { 255 255 $user = get_user_by( 'ID', $info['user_id'] ); 256 256 $user_level = pmpro_getLevel( $info['level_id'] ); 257 257 258 258 $update_errors[] = sprintf( 259 259 $msg_template, … … 267 267 } 268 268 } 269 269 270 270 /** 271 271 * Add error messages to back-end info display 272 272 */ 273 273 if ( ! empty( $update_errors ) ) { 274 274 275 275 $utils->log("Generated " . count( $update_errors ) . " errors during bulk update!"); 276 276 277 277 foreach ( $update_errors as $e_msg ) { 278 278 $utils->log("Error: {$e_msg}"); 279 279 $utils->add_message( $e_msg, 'error', 'backend' ); 280 280 } 281 281 282 282 // And return false (error) 283 283 return false; 284 284 } 285 285 286 286 // All's good! 287 287 return true; 288 288 } 289 289 290 290 /** 291 291 * Change the membership level for the specified User ID … … 298 298 */ 299 299 public function update_membership( $user_id, $current_level_id, $new_level_id ) { 300 300 301 301 // Execute the membership level change for the specified user ID/Level ID 302 302 if ( function_exists( 'pmpro_changeMembershipLevel' ) ) { … … 306 306 } 307 307 } 308 308 309 309 /** 310 310 * Update the specified field name (a date field) … … 321 321 */ 322 322 public function update_date( $field_name, $user_id, $current_level, $new_level, $new_date, $record_id = null, $use_new = false ) { 323 323 324 324 $utils = Utilities::get_instance(); 325 325 $date = null; 326 326 327 327 // Make sure we received a valid date 328 328 if ( !empty($new_date) && false === $this->validate_date_format( $new_date, 'Y-m-d' ) ) { 329 329 330 330 $user = get_user_by('ID', $user_id ); 331 331 332 332 $msg = sprintf( 333 333 __("Invalid date format for %s (record: %d/email: %s)", 'e20r-members-list' ), … … 336 336 $user->user_email 337 337 ); 338 338 339 339 $utils->log( $msg ); 340 340 $utils->add_message( $msg, 'error', 'backend' ); 341 341 342 342 return false; 343 343 } 344 344 345 345 global $wpdb; 346 346 347 347 if ( true === $use_new ) { 348 348 $where = array( 'membership_id' => $new_level, 'user_id' => $user_id, 'status' => 'active' ); … … 355 355 $where_format = array( '%d', '%d', '%s' ); 356 356 } 357 357 358 358 if ( !empty( $new_date ) ) { 359 359 if ( true === apply_filters( 'e20r_memberslist_membership_starts_at_midnight', __return_true() ) ) { … … 364 364 } 365 365 $retval = $wpdb->update( $wpdb->pmpro_memberships_users, array( $field_name => $date ), $where, array( '%s' ), $where_format ); 366 366 367 367 if ( false === $retval ) { 368 368 return $retval; 369 369 } 370 370 371 371 return true; 372 372 } 373 373 374 374 /** 375 375 * Update the user's Membership status for the specified record … … 381 381 */ 382 382 public function update_status( $record_id, $status ) { 383 383 384 384 global $wpdb; 385 385 386 386 $retval = $wpdb->update( $wpdb->pmpro_memberships_users, array( 'status' => $status ), array( 'id' => $record_id ), array( '%s' ), array( '%d' ) ); 387 387 388 388 if ( ! empty( $retval ) ) { 389 389 return true; 390 390 } 391 391 392 392 return false; 393 393 } 394 394 395 395 /** 396 396 * Set the list of members to update … … 401 401 $this->members_to_update = $member_info; 402 402 } 403 403 404 404 /** 405 405 * Return the list of members being updated … … 410 410 return $this->members_to_update; 411 411 } 412 412 413 413 /** 414 414 * Return the ongoing operation … … 419 419 return $this->operation; 420 420 } 421 421 422 422 /** 423 423 * Configure/set the Operation for the Bulk Update … … 428 428 $this->operation = $operation; 429 429 } 430 430 431 431 /** 432 432 * Test the date supplied for MySQL compliance … … 440 440 */ 441 441 private function validate_date_format( $date, $format = 'Y-m-d' ) { 442 442 443 443 $check_date = \DateTime::createFromFormat( $format, $date ); 444 444 445 445 return $check_date && $check_date->format( $format ) == $date; 446 446 } -
e20r-members-list/trunk/class/members-list/class.export-members.php
r2228121 r2458244 1 1 <?php 2 2 /** 3 * Copyright (c) 2018-20 19- Eighty / 20 Results by Wicked Strong Chicks.3 * Copyright (c) 2018-2021 - Eighty / 20 Results by Wicked Strong Chicks. 4 4 * ALL RIGHTS RESERVED 5 5 * … … 25 25 26 26 class Export_Members { 27 27 28 28 /** 29 29 * @var array $member_list 30 30 */ 31 31 private $member_list = array(); 32 32 33 33 /** 34 34 * @var string|null $sql 35 35 */ 36 36 private $sql = null; 37 37 38 38 /** 39 39 * @var array $headers 40 40 */ 41 41 private $headers = array(); 42 42 43 43 /** 44 44 * @var array $csv_headers 45 45 */ 46 46 private $csv_headers = array(); 47 47 48 48 /** 49 49 * @var array $csv_rows 50 50 */ 51 51 private $csv_rows = array(); 52 52 53 53 /** 54 54 * @var bool|null|string 55 55 */ 56 56 private $file_name = null; 57 57 58 58 /** 59 59 * Cached $member discount information … … 62 62 */ 63 63 private $member_discount_info = null; 64 64 65 65 /** 66 66 * Export_Members constructor. … … 69 69 */ 70 70 public function __construct( $db_records ) { 71 71 72 72 $this->member_list = $db_records; 73 73 74 74 $this->file_name = $this->create_temp_file(); 75 75 76 76 $this->default_columns = array( 77 77 array( "wp_user", "ID" ), … … 106 106 array( "member_level", 'enddate' ), 107 107 ); 108 108 109 109 $this->default_columns = apply_filters( 'pmpro_members_list_csv_default_columns', $this->default_columns ); 110 110 $this->default_columns = apply_filters( 'e20r-members-list-default-csv-columns', $this->default_columns ); 111 111 112 112 /** 113 113 * Map of Database column keys and CSV export column keys … … 257 257 ) 258 258 ); 259 259 260 260 // Generate the header for the .csv file 261 261 $this->csv_header(); 262 262 $this->set_upload_headers(); 263 263 264 264 /** 265 265 * Trigger the default 'e20r-members-list-load-export-value' filter handler first … … 267 267 add_filter( 'e20r-members-list-load-export-value', array( $this, 'load_export_value' ), - 1, 3 ); 268 268 } 269 269 270 270 /** 271 271 * Create the temporary file name for this export operation … … 274 274 */ 275 275 private function create_temp_file() { 276 276 277 277 // Generate a temporary file to store the data in. 278 278 $tmp_dir = sys_get_temp_dir(); 279 279 $file_name = tempnam( $tmp_dir, 'pmpro_ml_' ); 280 280 281 281 return $file_name; 282 282 } 283 283 284 284 /** 285 285 * Create the export file header (DB columns) 286 286 */ 287 287 private function csv_header() { 288 288 289 289 $utils = Utilities::get_instance(); 290 290 $level = $utils->get_variable( 'membership_id', '' ); 291 291 292 292 $extra_cols = apply_filters( "pmpro_members_list_csv_extra_columns", array() ); 293 293 294 294 if ( ! empty( $extra_cols ) ) { 295 295 296 296 foreach ( $extra_cols as $col_header => $callback ) { 297 297 // $this->header_map[] = $col_header; … … 304 304 } 305 305 } 306 306 307 307 $header_list = apply_filters( 'pmpro_members_list_csv_heading', implode( ',', array_keys( $this->header_map ) ) ); 308 308 $this->csv_headers = array_map( 'trim', explode( ',', $header_list ) ); 309 309 310 310 $utils->log( "Using " . count( $this->csv_headers ) . " header columns" ); 311 311 } 312 312 313 313 /** 314 314 * Set headers for .CSV file upload 315 315 */ 316 316 private function set_upload_headers() { 317 317 318 318 $this->headers[] = "Content-Type: text/csv"; 319 319 $this->headers[] = "Cache-Control: max-age=0, no-cache, no-store"; … … 323 323 $this->headers = apply_filters( 'e20r-memberslist-http-headers', $this->headers ); 324 324 } 325 325 326 326 /** 327 327 * Clear old temporary files 328 328 */ 329 329 public static function clear_temp_files() { 330 330 331 331 $temp_dir_name = sys_get_temp_dir(); 332 332 $utils = Utilities::get_instance(); 333 333 334 334 $files = glob( "{$temp_dir_name}/pmpro_ml_*.csv" ); 335 335 $now = current_time( 'timestamp' ); 336 336 337 337 $utils->log( "Notice: Clearing temporary export files (if needed)" ); 338 338 339 339 foreach ( $files as $file_name ) { 340 340 341 341 if ( is_file( $file_name ) ) { 342 342 343 343 if ( $now - filemtime( $file_name ) >= DAY_IN_SECONDS ) { // Delete after 1 day 344 344 unlink( $file_name ); … … 347 347 } 348 348 } 349 349 350 350 /** 351 351 * Fetches the data for the export operation 352 352 */ 353 353 public function get_data_list() { 354 354 355 355 $utils = Utilities::get_instance(); 356 356 $utils->log( "Headers have been sent already..?!? " . ( headers_sent() ? 'Yes' : 'No' ) ); 357 357 358 358 /** 359 359 * Filter to set max number of records to process at a time … … 370 370 */ 371 371 $max_users_per_loop = apply_filters( 'pmpro_set_max_user_per_export_loop', 2000 ); 372 372 373 373 /** 374 374 * Pre-export actions … … 381 381 */ 382 382 do_action( 'pmpro_before_members_list_csv_export', $this->member_list ); 383 383 384 384 $extra_columns = apply_filters( "pmpro_members_list_csv_extra_columns", array() ); 385 385 386 386 $this->add_csv_header_to_file(); 387 387 388 388 $i_start = 0; 389 389 $iterations = 1; 390 390 $users_found = count( $this->member_list ); 391 391 392 392 if ( $users_found >= $max_users_per_loop ) { 393 393 $iterations = ceil( $users_found / $max_users_per_loop ); 394 394 } 395 395 396 396 $start = current_time( 'timestamp' ); 397 397 $end = 0; 398 398 $time_limit = (int) get_cfg_var( 'max_execution_time' ); 399 399 400 400 // Split up the export operation in multiple rounds/iterations 401 401 for ( $ic = 1; $ic <= $iterations; $ic ++ ) { 402 402 403 403 // Try to avoid timing out during the export operation 404 404 if ( 0 !== $end ) { 405 405 406 406 $iteration_diff = $end - $start; 407 407 $new_time_limit = ceil( $iteration_diff * $iterations * 1.2 ); 408 408 409 409 if ( $time_limit < $new_time_limit ) { 410 410 $time_limit = $new_time_limit; … … 412 412 } 413 413 } 414 414 415 415 $start = current_time( 'timestamp' ); 416 416 417 417 $utils->log( "For iterations: {$i_start} -> " . ( $i_start + $max_users_per_loop ) ); 418 418 419 419 $member_list = array_slice( $this->member_list, $i_start, $max_users_per_loop ); 420 420 421 421 $utils->log( "Will process " . count( $member_list ) . " member records in iteration {$ic}" ); 422 422 423 423 // Increment starting position 424 424 if ( 0 < $iterations ) { 425 425 $i_start += $max_users_per_loop; 426 426 } 427 427 428 428 foreach ( $member_list as $member ) { 429 429 430 430 $csv_record = array(); 431 431 432 432 // Cast the Member array to an object 433 433 $member = (object) $member; 434 434 435 435 /** 436 436 * Fetch any user metadata for the user 437 437 */ 438 438 $member->meta_values = $this->load_user_meta( $member->ID ); 439 439 440 440 /** 441 441 * Fetch/update the CSV export entry for the $member … … 446 446 */ 447 447 $csv_record = apply_filters( 'e20r-members-list-load-export-value', $csv_record, $member ); 448 448 449 449 // Add data from extra columns 450 450 if ( ! empty( $extra_columns ) ) { 451 451 452 452 foreach ( $extra_columns as $col_heading => $callback ) { 453 453 454 454 $val = call_user_func( $callback, $member, $col_heading ); 455 455 $val = ! empty( $val ) ? $val : null; 456 456 457 457 $csv_record[ $col_heading ] = $this->enclose( $val ); 458 458 } 459 459 } 460 460 461 461 // $utils->log( "Exportable info : " . print_r( $csv_record, true ) ); 462 462 463 463 // Add the data to the list of 464 464 $this->csv_rows[] = $csv_record; 465 466 } 467 465 466 } 467 468 468 wp_cache_flush(); 469 469 470 470 //need to increase max running time? 471 471 $end = current_time( 'timestamp' ); 472 472 } 473 473 474 474 do_action( 'pmpro_after_members_list_csv_export' ); 475 475 } 476 476 477 477 /** 478 478 * Add the CSV header to the (new) file 479 479 */ 480 480 private function add_csv_header_to_file() { 481 481 482 482 $utils = Utilities::get_instance(); 483 483 484 484 // Open our designated temporary file 485 485 $file_handle = fopen( $this->file_name, 'a' ); 486 486 $header_type = 'header_key'; 487 487 488 488 $utils->log( "Adding " . count( $this->csv_headers ) . " header columns to {$this->file_name}" ); 489 489 490 490 //Add the CSV header to the file 491 491 fprintf( $file_handle, '%s', … … 497 497 $this->csv_headers ) 498 498 ) . "\n" ); 499 499 500 500 // Close the CSV file for now 501 501 fclose( $file_handle ); 502 502 } 503 503 504 504 /** 505 505 * Returns the expected header key for the requested DB Key … … 512 512 */ 513 513 private function map_keys( $key, $requested, $column_type = null ) { 514 514 515 515 $utils = Utilities::get_instance(); 516 516 517 517 foreach ( $this->header_map as $map_key => $field_def ) { 518 518 519 519 if ( $key === $map_key ) { 520 520 return $field_def[ $requested ]; 521 521 } 522 522 523 523 if ( 'header_key' === $requested && $field_def['header_key'] == $key ) { 524 524 525 525 return $map_key; 526 526 } 527 527 528 528 if ( 'db_key' === $requested && $field_def['db_key'] == $key ) { 529 530 529 530 531 531 if ( 'username' === $key ) { 532 532 return $map_key; 533 533 } 534 534 535 535 return $field_def[ $requested ]; 536 536 } 537 537 } 538 538 539 539 $utils->log( "No value (key) found for {$requested} key {$key}" ); 540 540 541 541 return null; 542 542 } 543 543 544 544 /** 545 545 * Return all of the user's metadata that we (may) care about … … 550 550 */ 551 551 private function load_user_meta( $user_id ) { 552 552 553 553 $meta_values = null; 554 554 555 555 // Returns array of meta keys containing array(s) of meta_values. 556 556 $um_values = get_user_meta( $user_id ); 557 557 558 558 // Process user metadata 559 559 if ( ! empty( $um_values ) ) { 560 560 561 561 $meta_values = new \stdClass(); 562 562 563 563 foreach ( $um_values as $key => $value ) { 564 564 565 565 $meta_values->{$key} = isset( $value[0] ) ? $value[0] : null; 566 566 } 567 567 } 568 568 569 569 return $meta_values; 570 571 } 572 570 571 } 572 573 573 /** 574 574 * Enclose the data we're adding to the export file … … 581 581 return "\"" . str_replace( "\"", "\\\"", $text ) . "\""; 582 582 } 583 583 584 584 /** 585 585 * @param array $csv_record … … 589 589 */ 590 590 public function load_export_value( $csv_record, $member ) { 591 591 592 592 $utils = Utilities::get_instance(); 593 593 594 594 $datetime_format = $this->set_datetime_format(); 595 595 $level = $utils->get_variable( 'membership_id', '' ); 596 596 597 597 // $utils->log( "For member: " . print_r( $member, true ) ); 598 598 599 599 // Process the membership data (by column) 600 600 foreach ( $this->default_columns as $field_def ) { 601 601 602 602 $column_type = $field_def[0]; 603 603 $column_name = $this->map_keys( $field_def[1], 'db_key', $column_type ); … … 607 607 $column_type 608 608 ); 609 609 610 610 // Process Join/Start dates for membership 611 611 if ( in_array( $column_name, array( 'user_registered', 'startdate' ) ) ) { 612 612 613 613 $column_value = date( 614 614 $datetime_format, … … 618 618 ); 619 619 } 620 620 621 621 // Process End of membership column 622 622 if ( 'enddate' == $column_name ) { 623 623 624 624 $enddate_value = $column_value; 625 625 626 626 if ( ! is_null( $member->membership_id ) && ! empty( $column_value ) ) { 627 627 628 628 // Membership is terminated or about to be terminated 629 629 if ( in_array( $level, array( "oldmembers", "expired", 'cancelled' ) ) && 630 630 ( ! empty( $column_value ) && '0000-00-00 00:00:00' !== $column_value ) ) { 631 631 632 632 $enddate_value = apply_filters( "pmpro_memberslist_expires_column", date( $datetime_format, strtotime( $column_value, current_time( 'timestamp' ) ) ), $member ); 633 633 634 634 } 635 635 636 636 if ( ! empty( $member->membership_id ) && ( empty( $column_value ) || '0000-00-00 00:00:00' === $column_value ) ) { 637 637 $enddate_value = apply_filters( "pmpro_memberslist_expires_column", null, $member ); 638 638 } 639 639 640 640 if ( ! empty( $member->membership_id ) && ( ! empty( $column_value ) && '0000-00-00 00:00:00' !== $column_value ) ) { 641 641 $enddate_value = date( $datetime_format, strtotime( $column_value, current_time( 'timestamp' ) ) ); 642 642 } 643 643 644 644 // Save record info for the column 645 645 $column_value = apply_filters( 'e20r-members-list-expires-col-value', $enddate_value, $member );; 646 646 } 647 647 } 648 648 649 649 /** 650 650 * Fetch the discount code data for this user/member 651 651 */ 652 652 if ( ! empty( $member->code_id ) && empty( $this->member_discount_info ) ) { 653 653 654 654 $utils->log( "Grab the discount code data for {$member->ID}/{$member->code_id}" ); 655 655 656 656 $this->member_discount_info = self::get_pmpro_discount_code( 657 657 $member->code_id, … … 660 660 ); 661 661 } 662 662 663 663 /** 664 664 * Fetch the discount code value for the user/member 665 665 */ 666 666 if ( $column_type === 'pmpro_discount_code' && ! empty( $this->member_discount_info ) ) { 667 667 668 668 $param = "pmpro_discount_{$column_name}"; 669 669 $column_value = $this->member_discount_info->{$param}; 670 670 } 671 672 671 672 673 673 if ( empty( $column_value ) ) { 674 674 $column_value = null; 675 675 } 676 676 677 677 // $utils->log( "Saving {$column_name} (looked for {$field_def[1]}): " . print_r( $column_value, true ) ); 678 678 679 679 // Save the entry info 680 680 $csv_record[ $column_name ] = $this->enclose( $column_value ); 681 681 } 682 682 683 683 // Clear the Discount Info (for the member) 684 684 $this->member_discount_info = null; 685 685 686 686 return $csv_record; 687 687 } 688 688 689 689 /** 690 690 * Configure the format to use for the export datetime value … … 693 693 */ 694 694 private function set_datetime_format() { 695 695 696 696 /** 697 697 * Filter to the format for the date (default is the WordPress General Setting value for Date) … … 705 705 $date_format = apply_filters( 'pmpro_memberslist_csv_dateformat', get_option( 'date_format' ) ); 706 706 $date_format = apply_filters( 'e20r-members-list-csv-fateformat', $date_format ); 707 707 708 708 /** 709 709 * Filter to the format for the time (default is the WordPress General Setting value for Time) … … 716 716 */ 717 717 $time_format = apply_filters( 'e20r-members-list-csv-timeformat', get_option( 'time_format' ) ); 718 718 719 719 // Assume that we want a valid MySQL DateTime format if date is Y-m-d 720 720 if ( 'Y-m-d' == $date_format && 'H:i' == $time_format ) { … … 723 723 $expected_format = "{$date_format} {$time_format}"; 724 724 } 725 725 726 726 /** 727 727 * Filter to the format for the time (default is the WordPress General Setting value for Time) … … 736 736 */ 737 737 $datetime_format = apply_filters( 'e20r-members-list-csv-datetime-format', $expected_format, $date_format, $time_format ); 738 738 739 739 return $datetime_format; 740 740 } 741 741 742 742 /** 743 743 * Return a value for the specified column (name) … … 750 750 */ 751 751 private function get_column_value( $member, $column_name, $column_type ) { 752 752 753 753 $utils = Utilities::get_instance(); 754 754 $dc_col_name = "pmpro_discount_code_"; 755 755 756 756 switch ( $column_type ) { 757 757 758 758 case 'wp_user': 759 759 case 'member_level': … … 762 762 $column_value = isset( $member->{$column_name} ) ? $member->{$column_name} : null; 763 763 break; 764 764 765 765 case 'pmpro_discount_code': 766 766 $dc_col_name = "{$dc_col_name}{$column_name}"; 767 767 $column_value = isset( $member->{$column_name} ) ? $member->{$column_name} : null; 768 768 break; 769 769 770 770 case 'meta_values': 771 771 $column_value = isset( $member->meta_values->{$column_name} ) ? $member->meta_values->{$column_name} : null; 772 772 break; 773 773 774 774 default: 775 775 $utils->log( "Using default type (type = {$column_type}) for {$column_name}" ); 776 776 $column_value = isset( $member->{$column_type}->{$column_name} ) ? $member->{$column_type}->{$column_name} : null; 777 777 778 778 /** 779 779 * Let 3rd party define the value to use for a 3rd party defined default column … … 790 790 $column_value = apply_filters( 'e20r-members-list-set-default-column-value', $column_value, $column_name, $column_type, $member ); 791 791 } 792 792 793 793 return $column_value; 794 794 } 795 795 796 796 /** 797 797 * Fetch discount code (ID and code) for the code ID/User ID/Level ID combination … … 804 804 */ 805 805 public static function get_pmpro_discount_code( $code_id, $user_id, $level_id ) { 806 806 807 807 global $wpdb; 808 808 809 809 $disSql = $wpdb->prepare( " 810 810 SELECT … … 819 819 $user_id 820 820 ); 821 821 822 822 $pmpro_discount_code = $wpdb->get_row( $disSql ); 823 823 824 824 // Make sure there's data for the discount code info 825 825 if ( empty( $pmpro_discount_code ) ) { … … 829 829 $pmpro_discount_code = $empty_dc; 830 830 } 831 831 832 832 return $pmpro_discount_code; 833 833 } 834 834 835 835 /** 836 836 * Save the data rows to the temporary Export file 837 837 */ 838 838 public function save_data_for_export() { 839 839 840 840 $utils = Utilities::get_instance(); 841 841 $utils->log( "Saving " . count( $this->csv_rows ) . " records to {$this->file_name}. " ); 842 842 843 843 $fh = fopen( $this->file_name, 'a' ); 844 844 845 845 /** 846 846 * @var $row_data -> $csv_entry[ $col_heading ] = $this->enclose( $val ); 847 847 */ 848 848 foreach ( $this->csv_rows as $row_id => $row_data ) { 849 849 850 850 $data = array(); 851 851 852 852 foreach ( $this->csv_headers as $col_key ) { 853 853 854 854 /* 855 855 if ( 'ID' == $col_key ) { … … 857 857 } 858 858 */ 859 859 860 860 $col_name = $this->map_keys( $col_key, 'db_key' ); 861 861 862 862 $value = isset( $row_data[ $col_name ] ) ? $row_data[ $col_name ] : $this->enclose( null ); 863 863 $data[] = $value; 864 864 } 865 865 866 866 $file_line = implode( ',', $data ) . "\r\n"; 867 867 fprintf( $fh, '%s', $file_line ); 868 868 } 869 869 870 870 fclose( $fh ); 871 871 $utils->log( "Saved data to {$this->file_name}" ); 872 872 } 873 873 874 874 /** 875 875 * Send the .CSV to the requesting browser 876 876 */ 877 877 public function return_content() { 878 878 879 879 $utils = Utilities::get_instance(); 880 880 $utils->log( "Headers have been sent already..?!? " . ( headers_sent() ? 'Yes' : 'No' ) ); … … 882 882 // Send the data to the recipient browser 883 883 if ( ! empty( $this->headers ) && false === headers_sent() && file_exists( $this->file_name ) ) { 884 884 885 885 $sent_content = ob_get_clean(); 886 886 $utils->log("Browser received: " . print_r( $sent_content, true) ); 887 887 888 888 if ( version_compare( phpversion(), '5.3.0', '>' ) ) { 889 889 890 890 //Clear the file cache for the export file 891 891 clearstatcache( true, $this->file_name ); … … 894 894 clearstatcache(); 895 895 } 896 896 897 897 //Set the download size for the file 898 898 $this->headers[] = "Content-Length: " . filesize( $this->file_name ); 899 899 900 900 //Set transmission (PHP) headers 901 901 foreach ( $this->headers as $header ) { 902 902 header( $header . "\r\n" ); 903 903 } 904 904 905 905 // Disable compression for the duration of file download 906 906 if ( ini_get( 'zlib.output_compression' ) ) { 907 907 ini_set( 'zlib.output_compression', 'Off' ); 908 908 } 909 909 910 910 // Bug fix for Flywheel Hosted like hosts where fpassthru() is disabled 911 911 if ( function_exists( 'fpassthru' ) ) { … … 917 917 readfile( $this->file_name ); 918 918 } 919 919 920 920 // Remove the temp file 921 921 unlink( $this->file_name ); 922 922 exit(); 923 923 924 924 } else { 925 925 $msg = __( "Unable to send the .CSV file to your browser!", 'e20r-members-list' ); … … 928 928 } 929 929 } 930 930 931 931 /** 932 932 * Returns the column name to use from the specified $header_name … … 937 937 */ 938 938 private function map_header_to_column( $header_name ) { 939 939 940 940 return isset( $this->header_map[ $header_name ]['header_key'] ) ? $this->header_map[ $header_name ]['header_key'] : null; 941 941 } -
e20r-members-list/trunk/class/members-list/class.members-list-page.php
r2228121 r2458244 1 1 <?php 2 2 /** 3 * Copyright (c) 2018-20 19- Eighty / 20 Results by Wicked Strong Chicks.3 * Copyright (c) 2018-2021 - Eighty / 20 Results by Wicked Strong Chicks. 4 4 * ALL RIGHTS RESERVED 5 5 * … … 24 24 25 25 class Members_List_Page { 26 26 27 27 /** 28 28 * Instance of the Members_List_Page class (Singleton) … … 31 31 */ 32 32 private static $instance = null; 33 33 34 34 /** 35 35 * Holds the list of members (Members_List class) … … 37 37 */ 38 38 public $member_list; 39 39 40 40 /** 41 41 * Holds the standard Utilities class … … 44 44 */ 45 45 private $utils; 46 46 47 47 /** 48 48 * Members_List_Page constructor. Loads required menu(s) & screen options … … 50 50 private function __construct() { 51 51 } 52 52 53 53 /** 54 54 * Creates or returns an instance of the Members_List_Page class. … … 57 57 */ 58 58 public static function get_instance() { 59 59 60 60 if ( null === self::$instance ) { 61 61 self::$instance = new self; 62 62 } 63 63 64 64 return self::$instance; 65 65 } 66 66 67 67 /** 68 68 * Removes the old Member List page & appends a new one to the "Memberships" admin bar node 69 69 */ 70 70 public static function admin_bar_menu() { 71 71 72 72 $is_pmpro_v2 = version_compare( PMPRO_VERSION, '2.0', 'ge' ); 73 73 74 74 if ( true === $is_pmpro_v2 ) { 75 75 return; 76 76 } 77 77 78 78 global $wp_admin_bar; 79 80 79 80 81 81 if ( ! is_admin_bar_showing() || ( ! is_super_admin() && ( ! current_user_can( 'manage_options' ) ) && ! current_user_can( 'pmpro_memberslist' ) && ! current_user_can( 'e20r_memberslist' ) ) ) { 82 82 if ( ! is_null( self::$instance ) ) { 83 83 self::$instance->utils->log( "Unable to change admin bar (wrong capabilities for user)" ); 84 84 } 85 85 86 86 return; 87 87 } 88 88 89 89 $wp_admin_bar->remove_menu( 'pmpro-members-list' ); 90 90 $wp_admin_bar->remove_node( 'pmpro-members-list' ); 91 91 92 92 //Add the (new) Members List page to the admin_bar menu 93 93 $wp_admin_bar->add_menu( array( … … 102 102 ) ); 103 103 } 104 104 105 105 /** 106 106 * Screen Option option(s) for the Members List page. … … 112 112 */ 113 113 public static function set_screen( $status, $option, $value ) { 114 114 115 115 self::$instance->utils->log( "Saving screen option (page: {$option})? {$value} vs {$status}" ); 116 116 117 117 if ( 'per_page' == $option ) { 118 118 return $value; 119 119 } 120 120 121 121 return $status; 122 122 } 123 123 124 124 /** 125 125 * Load Action and Filter hooks for the Members List page 126 126 */ 127 127 public function load_hooks() { 128 128 129 129 $this->utils = Utilities::get_instance(); 130 130 131 131 // Filters 132 132 add_filter( 'set-screen-option', array( $this, 'set_screen' ), 10, 3 ); 133 133 add_filter( 'set_url_scheme', array( $this, 'add_to_pagination' ), 10, 3 ); 134 134 135 135 // Actions 136 136 add_action( 'admin_menu', array( $this, 'plugin_menu' ), 9999 ); … … 138 138 // add_action( 'admin_bar_menu', array( $this, 'admin_bar_menu' ), 9999 ); 139 139 add_action( 'admin_enqueue_scripts', array( $this, 'load_scripts_styles' ) ); 140 141 } 142 140 141 } 142 143 143 /** 144 144 * Load the custom CSS and JavaScript for the Members List 145 145 */ 146 146 public function load_scripts_styles( $hook_suffix ) { 147 147 148 148 if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || false == DOING_AJAX ) && 1 === preg_match( '/(pmpro|e20r)-memberslist/', $hook_suffix ) ) { 149 149 150 150 wp_enqueue_style( 'jquery-ui', '//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css' ); 151 151 wp_enqueue_script( 'jquery-ui-datepicker' ); 152 152 153 153 wp_enqueue_style( 'e20r-memberslist-page', plugins_url( "/css/e20r-memberslist-page.css", __FILE__ ), array( 'pmpro_admin' ), E20R_MEMBERSLIST_VER ); 154 154 155 155 wp_register_script( 'e20r-memberslist-page', plugins_url( "/js/e20r-memberslist-page.js", __FILE__ ), array( 'jquery' ), E20R_MEMBERSLIST_VER, true ); 156 156 157 157 // $is_pmpro_v2 = version_compare( PMPRO_VERSION, '2.0', 'ge' ); 158 158 // $url = ( false === $is_pmpro_v2 ) ? 'e20r-memberslist' : 'pmpro-memberslist'; 159 159 160 160 wp_localize_script( 'e20r-memberslist-page', 'e20rml', 161 161 array( … … 168 168 ) 169 169 ); 170 170 171 171 wp_enqueue_script( 'e20r-memberslist-page' ); 172 172 } 173 173 } 174 174 175 175 /** 176 176 * Point Members List menu handler(s) to this plugin 177 177 */ 178 178 public function plugin_menu() { 179 179 180 180 $this->utils->log( "Headers sent? " . ( headers_sent() ? 'Yes' : 'No' ) ); 181 182 181 182 183 183 $pmpro_menu_slug = 'pmpro-membershiplevels'; 184 184 $is_pmpro_v2 = version_compare( PMPRO_VERSION, '2.0', 'ge' ); 185 185 186 186 if ( defined( 'PMPRO_VERSION' ) && $is_pmpro_v2 ) { 187 187 $pmpro_menu_slug = 'pmpro-dashboard'; 188 188 } 189 189 190 190 $this->utils->log( "Remove the default members list page.. (under: {$pmpro_menu_slug})" ); 191 191 192 192 // Just replace the action that loads the PMPro Members List 193 193 194 194 $hookname = get_plugin_page_hookname( 'pmpro-memberslist', $pmpro_menu_slug ); 195 195 $this->utils->log("Found hook name: {$hookname}. Sent yet? " . (headers_sent() ? 'Yes' : 'No') ); … … 197 197 add_action( $hookname, array( $this, 'memberslist_settings_page' ), 11 ); 198 198 add_action( "load-memberships_page_pmpro-memberslist", array( $this, 'screen_option' ), 9999 ); 199 199 200 200 /* 201 201 // Unhook old members list functionality (pre v2.0 of PMPro) 202 202 if ( false == ( $page = remove_submenu_page( $pmpro_menu_slug, 'pmpro-memberslist' ) ) ) { 203 203 204 204 $this->utils->log( "Unable to remove the default membership levels page!" ); 205 205 pmpro_setMessage( __( 'Error while attempting to reassign member list menu', E20R_Members_List::plugin_slug ), 'error' ); 206 206 207 207 return; 208 208 } 209 209 210 210 // Load the (new) WP_Table_List based Members List 211 211 $hook = add_submenu_page( … … 217 217 array( $this, 'memberslist_settings_page' ) 218 218 ); 219 219 220 220 // Show error if we're unable to update the members list 221 221 if ( false === $hook ) { 222 222 223 223 $this->utils->log( "Unable to load the replacement Members List page!" ); 224 224 pmpro_setMessage( __( "Unable to load Members List menu entry", "e20r-members-list" ), "error" ); 225 225 226 226 return; 227 227 } 228 228 229 229 // Process the screen option 230 230 add_action( "load-memberships_page_pmpro-memberslist", array( $this, 'screen_option' ), 9999 ); 231 231 */ 232 232 } 233 233 234 234 /** 235 235 * Add parameters to limit/include records to any members list page URI … … 242 242 */ 243 243 public function add_to_pagination( $url, $scheme, $original_scheme ) { 244 244 245 245 $page = $this->utils->get_variable( 'page', '' ); 246 246 247 247 if ( 1 === preg_match( "/{$_SERVER['HTTP_HOST']}\/wp-admin\/admin.php\?page=pmpro-memberslist/i", $url ) ) { 248 248 249 249 $arg_list = array(); 250 250 251 251 $level = $this->utils->get_variable( 'level', '' ); 252 252 $find = $this->utils->get_variable( 'find', '' ); 253 253 254 254 if ( ! empty( $level ) ) { 255 255 $arg_list['level'] = $level; 256 256 } 257 257 258 258 if ( ! empty( $find ) ) { 259 259 $arg_list['find'] = $find; 260 260 } 261 261 262 262 /** 263 263 * @filter e20r_memberslist_pagination_args - Add filtering to the URI (to preserve it for pagination ,etc) … … 266 266 */ 267 267 $arg_list = apply_filters( 'e20r_memberslist_pagination_args', $arg_list ); 268 268 269 269 // Encode the new URI variables 270 270 foreach ( $arg_list as $a_key => $value ) { 271 271 $arg_list[ $a_key ] = urlencode_deep( urldecode_deep( $value ) ); 272 272 } 273 273 274 274 $url = add_query_arg( $arg_list, $url ); 275 275 } 276 276 277 277 return $url; 278 278 } 279 279 280 280 /** 281 281 * Configure options to use for "Screen Options" on Members_List_Page page. 282 282 */ 283 283 public function screen_option() { 284 284 285 285 $options = 'per_page'; 286 286 287 287 $args = array( 288 288 'label' => _x( "Members per page", "members per page (screen options)", "e20r-members-list" ), … … 290 290 'option' => $options, 291 291 ); 292 292 293 293 add_screen_option( $options, $args ); 294 294 295 295 $this->member_list = new Members_List(); 296 296 } 297 297 298 298 /** 299 299 * Load the e20rMembersList page content 300 300 */ 301 301 public function memberslist_settings_page() { 302 302 303 303 $this->utils->log("Have we sent content? " . (headers_sent() ? 'yes' : 'no')); 304 305 if ( ! function_exists( 'pmpro_loadTemplate' ) ) { 306 $this->utils->log("Fatal: Paid Memberships Pro is not loaded on site!"); 307 return ""; 308 } 309 304 310 global $pmpro_msg; 305 311 global $pmpro_msgt; 306 312 307 313 // TODO: Fix this to match required request variables. 308 314 $search = $this->utils->get_variable( 'find', '' ); 309 315 $level = $this->utils->get_variable( 'level', '' ); 310 316 311 317 echo pmpro_loadTemplate( 'admin_header', 'local', 'adminpages' ); 312 318 313 319 $search_array = apply_filters( 'e20r_memberslist_exportcsv_search_args', array( 314 320 'action' => 'memberslist_csv', … … 318 324 ) 319 325 ); 320 326 321 327 $csv_url = add_query_arg( 322 328 $search_array, 323 329 get_admin_url( get_current_blog_id(), 'admin-ajax.php' ) 324 330 ); 325 331 326 332 $e20r_error_msgs = $this->utils->get_message( 'error' ); 327 333 $e20r_warning_msgs = $this->utils->get_message( 'warning' ); 328 334 $e20r_info_msgs = $this->utils->get_message( 'info' ); 329 335 330 336 $top_list = array( 331 337 'active' => __( 'Active Members', E20R_Members_List::plugin_slug ), 332 338 'all' => __( 'All Members', E20R_Members_List::plugin_slug ), 333 339 ); 334 340 335 341 $bottom_list = array( 336 342 'cancelled' => __( 'Cancelled Members', 'paid-membership-pro' ), … … 338 344 'oldmembers' => __( 'Old Members', 'paid-membership-pro' ), 339 345 ); 340 346 341 347 $level_list = array(); 342 343 $list = function_exists( 'pmpro_getAllLevels' ) ? pmpro_getAllLevels( true, true ) : array(); 344 348 349 $list = function_exists( 'pmpro_getAllLevels' ) ? 350 pmpro_getAllLevels( true, true ) : 351 array(); 352 345 353 foreach ( $list as $item ) { 346 354 $level_list[ $item->id ] = $item->name; 347 355 } 348 356 349 357 $option_list = $top_list + $level_list + $bottom_list; 350 358 351 359 if ( ! empty( $pmpro_msg ) ) { ?> 352 360 … … 383 391 $label = __( 'Update List', E20R_Members_List::plugin_slug ); 384 392 $button_def = 'button'; 385 393 386 394 if ( isset( $_REQUEST['find'] ) && ! empty( $_REQUEST['find'] ) ) { 387 395 388 396 $label = __( 'Clear Search', E20R_Members_List::plugin_slug ); 389 397 $button_def .= " button-primary"; … … 421 429 </form> 422 430 </div> 423 431 424 432 <?php 425 433 echo pmpro_loadTemplate( 'admin_footer', 'local', 'adminpages' ); 426 434 } 427 435 428 436 /** 429 437 * Deactivated __clone() method for the Members_List_Page class … … 431 439 private function __clone() { 432 440 } 433 441 434 442 } -
e20r-members-list/trunk/class/members-list/class.members-list.php
r2306383 r2458244 3 3 * License: 4 4 5 Copyright 2016-20 19- Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected])5 Copyright 2016-2021 - Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected]) 6 6 7 7 This program is free software; you can redistribute it and/or modify … … 73 73 */ 74 74 private $total_members_found = null; 75 76 /** 77 * The total number of records in the PMPro Membership DB 78 * 79 * @var int $total_member_records 80 */ 81 private $total_member_records = 0; 82 83 /** 84 * The status of the membership when searching for totals 85 * 86 * @var string $membership_status 87 */ 88 private $membership_status = 'active'; 89 75 90 /** 76 91 * The completed SQL query used to generate the membership list … … 176 191 $this->action = $this->utils->get_variable( 'action', '' ); 177 192 193 if ( ! empty( $level ) ) { 194 switch ($level) { 195 case 'cancelled': 196 $this->membership_status = array( 'cancelled' ); 197 break; 198 case 'expired': 199 $this->membership_status = array( 'expired' ); 200 break; 201 case 'old': 202 $this->membership_status = array( 'cancelled', 'expired' ); 203 break; 204 default: 205 $this->membership_status = array('active'); 206 } 207 } 178 208 /** 179 209 * The default Members List columns to display (with labels) … … 204 234 } 205 235 236 $this->total_member_records = $this->get_member_record_count(); 237 206 238 /** 207 239 * Prepare the Export bulk action … … 211 243 add_action( 'e20r_memberslist_process_action', array( $this, 'export_members' ), 10, 3 ); 212 244 } 245 } 246 247 /** 248 * Private function to capture the count of records in the membership database 249 * 250 * @return int|null 251 */ 252 private function get_member_record_count() { 253 254 $status = $this->utils->get_variable( 'level', 'active' ); 255 256 // Get SQL for all records in the paginated data 257 $this->generate_member_sql( $status ); 258 $records = $this->get_members( -1, -1, $status ); 259 260 return is_countable( $records ) ? count( $records ) : 0; 213 261 } 214 262 … … 294 342 295 343 // Do we need to limit? 296 $level = $this->utils->get_variable( 'level', '' ); 297 298 if ( empty( $level ) ) { 299 $level = 'active'; 300 } 344 $level = $this->utils->get_variable( 'level', 'active' ); 301 345 302 346 // Get the current page number … … 309 353 // BUG FIX: Handle situation(s) where there are no records found 310 354 if ( null !== $this->items ) { 311 $total_items = count($this->items );312 355 $this->utils->log( 313 356 sprintf( 314 357 "Configure pagination for %d total records and %d counted (returned) records", 315 $t otal_items,358 $this->total_member_records, 316 359 (is_countable( $this->items ) ? count( $this->items ) : 0) 317 360 ) … … 323 366 $this->set_pagination_args( 324 367 array( 325 'total_items' => $t otal_items,368 'total_items' => $this->total_member_records, 326 369 'per_page' => $per_page, 327 'total_pages' => ceil( $t otal_items / $per_page ),370 'total_pages' => ceil( $this->total_member_records / $per_page ), 328 371 ) 329 372 ); … … 593 636 global $wpdb; 594 637 595 $this->generate_member_sql( $per_page, $page_number, $status ); 638 // Get Pagination SQL 639 $this->sqlQuery = $this->generate_member_sql( $status, $per_page, $page_number, ); 596 640 597 641 // Fetch the data 598 $result = $wpdb->get_results( $this->sqlQuery, ARRAY_A ); 642 $result = $wpdb->get_results( $this->sqlQuery, ARRAY_A ); 643 599 644 if ( ! empty( $result ) ) { 600 645 $this->utils->log("Found records in DB..."); 601 646 $this->total_members_found = $wpdb->num_rows; 602 647 } 603 /*604 if ( ! empty( $result ) ) {605 $this->total_members_found = $result->num_rows;606 // $this->total_members_found = $wpdb->get_var( "SELECT FOUND_ROWS() AS found_rows" );607 }608 */609 648 610 649 // Return the result set unless an error occurred. … … 640 679 * @param int $page_number 641 680 * @param string $status 642 */ 643 private function generate_member_sql( $per_page, $page_number, $status = 'active' ) { 681 * 682 * @return string - Returns the SQL statement 683 */ 684 private function generate_member_sql( $status = 'active', $per_page = -1, $page_number = -1 ) { 644 685 645 686 $this->utils->log( "Called by: " . $this->utils->_who_called_me() ); … … 839 880 "; 840 881 841 // Define the SQL statement 842 $this->sqlQuery = $sql . self::$sql_from; 843 844 $this->utils->log( "SQL for fetching membership records:\n {$this->sqlQuery}" ); 882 // Created the SQL statement 883 $sqlQuery = $sql . self::$sql_from; 884 885 $this->utils->log( "SQL for fetching membership records:\n {$sqlQuery}" ); 886 return $sqlQuery; 845 887 } 846 888 -
e20r-members-list/trunk/class/members-list/class.sort-by-meta.php
r2025537 r2458244 1 1 <?php 2 2 /** 3 * Copyright (c) 2018-20 19- Eighty / 20 Results by Wicked Strong Chicks.3 * Copyright (c) 2018-2021 - Eighty / 20 Results by Wicked Strong Chicks. 4 4 * ALL RIGHTS RESERVED 5 5 * … … 21 21 22 22 if ( ! class_exists( 'E20R\Members_List\Support\Sort_By_Meta' ) ) { 23 23 24 24 class Sort_By_Meta { 25 25 26 26 private $meta_key; 27 27 28 28 private $order = 'DESC'; 29 29 30 30 /** 31 31 * Sort_Meta constructor. … … 38 38 $this->order = strtoupper( $order ); 39 39 } 40 40 41 41 /** 42 42 * @param array $a … … 48 48 */ 49 49 public function sort_records( $a, $b ) { 50 50 51 51 // $utils = Utilities::get_instance(); 52 52 // $utils->log( "A: " . print_r( $a, true)); 53 53 // $utils->log("B: " . print_r( $b, true)); 54 54 55 55 $a_user_id = is_array( $a ) ? $a['user_id'] : ( is_a( $a, '\WP_User' ) ? $a->ID : null ); 56 56 $b_user_id = is_array( $b ) ? $b['user_id'] : ( is_a( $b, '\WP_User' ) ? $b->ID : null ); 57 57 58 58 if ( is_null($a_user_id ) || is_null( $b_user_id ) ) { 59 59 return false; 60 60 } 61 61 62 62 // Check if the field specified exists in the data 63 63 if ( ! isset( $a[ $this->meta_key ] ) ) { 64 64 $a_value = get_user_meta( $a_user_id, $this->meta_key, true ); 65 65 66 66 } else { 67 67 $a_value = $b[ $this->meta_key ]; 68 68 } 69 69 70 70 if ( ! isset( $b[ $this->meta_key ] ) ) { 71 71 $b_value = get_user_meta( $b_user_id, $this->meta_key, true ); … … 73 73 $b_value = $b[ $this->meta_key ]; 74 74 } 75 75 76 76 if ( $a_value == $b_value ) { 77 77 return 0; 78 78 } 79 79 80 80 if ( 'DESC' == $this->order ) { 81 81 return ( $a_value > $b_value ? 1 : - 1 ); 82 82 } 83 83 84 84 if ( 'ASC' == $this->order ) { 85 85 return ( $a_value < $b_value ? 1 : - 1 ); 86 86 } 87 88 87 88 89 89 } 90 90 91 91 } 92 92 } -
e20r-members-list/trunk/class/members-list/css/e20r-memberslist-page.css
r2025537 r2458244 1 1 /* 2 * Copyright (c) 2018-20 19. - Eighty / 20 Results by Wicked Strong Chicks.2 * Copyright (c) 2018-2021. - Eighty / 20 Results by Wicked Strong Chicks. 3 3 * ALL RIGHTS RESERVED 4 4 * -
e20r-members-list/trunk/class/members-list/js/e20r-memberslist-page.js
r2228121 r2458244 2 2 * License: 3 3 4 Copyright 2016-20 19- Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected])4 Copyright 2016-2021 - Eighty / 20 Results by Wicked Strong Chicks, LLC ([email protected]) 5 5 6 6 This program is free software; you can redistribute it and/or modify
Note: See TracChangeset
for help on using the changeset viewer.