hide_errors(); $wpdb->pmpro_membership_levels = $wpdb->prefix . 'pmpro_membership_levels'; $wpdb->pmpro_memberships_users = $wpdb->prefix . 'pmpro_memberships_users'; $wpdb->pmpro_memberships_categories = $wpdb->prefix . 'pmpro_memberships_categories'; $wpdb->pmpro_memberships_pages = $wpdb->prefix . 'pmpro_memberships_pages'; $wpdb->pmpro_membership_orders = $wpdb->prefix . 'pmpro_membership_orders'; $wpdb->pmpro_discount_codes = $wpdb->prefix . 'pmpro_discount_codes'; $wpdb->pmpro_discount_codes_levels = $wpdb->prefix . 'pmpro_discount_codes_levels'; $wpdb->pmpro_discount_codes_uses = $wpdb->prefix . 'pmpro_discount_codes_uses'; $wpdb->pmpro_membership_levelmeta = $wpdb->prefix . 'pmpro_membership_levelmeta'; $wpdb->pmpro_subscriptions = $wpdb->prefix . 'pmpro_subscriptions'; $wpdb->pmpro_membership_ordermeta = $wpdb->prefix . 'pmpro_membership_ordermeta'; $wpdb->pmpro_subscriptionmeta = $wpdb->prefix . 'pmpro_subscriptionmeta'; $wpdb->pmpro_groups = $wpdb->prefix . 'pmpro_groups'; $wpdb->pmpro_membership_levels_groups = $wpdb->prefix . 'pmpro_membership_levels_groups'; } pmpro_setDBTables(); // thanks: http://wordpress.org/support/topic/is_plugin_active function pmpro_is_plugin_active( $plugin ) { return in_array( $plugin, (array) get_option( 'active_plugins', array() ) ); } /** * @param int $n Override if you have more than 1 group of matches and don't want the first group. */ function pmpro_getMatches( $p, $s, $firstvalue = false, $n = 1 ) { $ok = preg_match_all( $p, $s, $matches ); if ( ! $ok ) { return false; } else { if ( $firstvalue ) { return $matches[ $n ][0]; } else { return $matches[ $n ]; } } } function pmpro_br2nl( $text, $tags = 'br' ) { if ( ! is_array( $tags ) ) { $tags = explode( ' ', $tags ); } foreach ( $tags as $tag ) { $text = preg_replace( "/<{$tag}[^>]*>/", "\n", $text ); $text = preg_replace( "/<\/{$tag}[^>]*>/", "\n", $text ); } return( $text ); } /** * get_option() should be used directly instead. * * Will be deprecated in a future release. */ function pmpro_getOption( $s, $force = false ) { return get_option( 'pmpro_' . $s, '' ); } function pmpro_setOption( $s, $v = null, $sanitize_function = 'sanitize_text_field', $autoload = false ) { if ( $v === null && isset( $_POST[ $s ] ) ) { // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if ( is_array( $_POST[ $s ] ) ) { $v = array_map( $sanitize_function, $_POST[ $s ] ); } else { $v = call_user_func( $sanitize_function, $_POST[ $s ] ); } // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized } if ( is_array( $v ) ) { $v = implode( ',', $v ); } elseif ( is_string( $v ) ) { $v = trim( $v ); } return update_option( 'pmpro_' . $s, $v, $autoload ); } function pmpro_get_slug( $post_id ) { global $pmpro_slugs, $wpdb; // make sure post id is int for security $post_id = intval( $post_id ); if ( ! $pmpro_slugs[ $post_id ] ) { $pmpro_slugs[ $post_id ] = $wpdb->get_var( "SELECT post_name FROM $wpdb->posts WHERE ID = '" . esc_sql( $post_id ) . "' LIMIT 1" ); } return $pmpro_slugs[ $post_id ]; } function pmpro_url( $page = null, $querystring = '', $scheme = null ) { global $besecure; $besecure = apply_filters( 'besecure', $besecure ); if ( ! $scheme && $besecure ) { $scheme = 'https'; } elseif ( ! $scheme ) { $scheme = 'http'; } if ( ! $page ) { $page = 'levels'; } global $pmpro_pages; if ( ! empty( $pmpro_pages[ $page ] ) ) { // start with the permalink $url = get_permalink( $pmpro_pages[ $page ] ); // WPML/etc support if ( function_exists( 'icl_object_id' ) && defined( 'ICL_LANGUAGE_CODE' ) ) { $trans_id = icl_object_id( $pmpro_pages[ $page ], 'page', false, ICL_LANGUAGE_CODE ); if ( ! empty( $trans_id ) ) { $url = get_permalink( $trans_id ); } } } else { $url = ''; } // figure out querystring $querystring = str_replace( '?', '', $querystring ); parse_str( $querystring, $query_args ); if ( ! empty( $url ) ) { $url = esc_url_raw( add_query_arg( $query_args, $url ) ); // figure out scheme if ( is_ssl() ) { $url = str_replace( 'http:', 'https:', $url ); } } /** * Filter the URL before returning. */ $url = apply_filters( 'pmpro_url', $url, $page, $querystring, $scheme ); return $url; } function pmpro_isLevelFree( &$level ) { if ( ! empty( $level ) && $level->initial_payment <= 0 && $level->billing_amount <= 0 && $level->trial_amount <= 0 ) { $r = true; } else { $r = false; } $r = apply_filters( 'pmpro_is_level_free', $r, $level ); return $r; } /** * Given an array of levels, will return true if all of them are free. */ function pmpro_areLevelsFree( $levelarr ) { if ( ! is_array( $levelarr ) ) { return false; } foreach ( $levelarr as $curlevel ) { if ( ! empty( $curlevel ) && ( $curlevel->initial_payment > 0 || $curlevel->billing_amount > 0 || $curlevel->trial_amount > 0 ) ) { return false; } } return true; } /** * Check to see if only free levels are available. * @return boolean This will return true if only free levels are available for signup. * @internal Creates a filter 'pmpro_only_free_levels'. * @since 2.1 */ function pmpro_onlyFreeLevels() { // Get levels that are available for checkout only. $levels = pmpro_getAllLevels( false, true ); return apply_filters( 'pmpro_only_free_levels', pmpro_areLevelsFree( $levels ) ); } function pmpro_isLevelRecurring( &$level ) { if ( ! empty( $level ) && ( $level->billing_amount > 0 || $level->trial_amount > 0 ) ) { $r = true; } else { $r = false; } $r = apply_filters( 'pmpro_is_level_recurring', $r, $level ); return $r; } /** * Check if a user has any recurring levels. * Supports Multiple Memberships Per User scenarios. * @param $user_id int User to check for. Defaults to current user. * @since 2.2.6 */ function pmpro_has_recurring_level( $user_id = null ) { global $current_user; if ( empty( $user_id ) ) { $user_id = $current_user->ID; } if ( empty( $user_id ) ) { return false; } $levels = pmpro_getMembershipLevelsForUser( $user_id ); if ( empty( $levels ) ) { return false; } foreach( $levels as $level ) { if ( pmpro_isLevelRecurring( $level ) ) { return true; } } return false; } function pmpro_isLevelTrial( &$level ) { if ( ! empty( $level ) && ! empty( $level->trial_limit ) && $level->trial_limit > 0 ) { $r = true; } else { $r = false; } $r = apply_filters( 'pmpro_is_level_trial', $r, $level ); return $r; } function pmpro_isLevelExpiring( &$level ) { if ( ! empty( $level ) && ( ! empty( $level->expiration_number ) && $level->expiration_number > 0 ) || ! empty( $level->enddate ) ) { $r = true; } else { $r = false; } $r = apply_filters( 'pmpro_is_level_expiring', $r, $level ); return $r; } /** * Is this level expiring within one pay period * * @since 1.8.6.3 * * @param object $level PMPro Level Object to test */ function pmpro_isLevelExpiringSoon( &$level ) { if ( ! pmpro_isLevelExpiring( $level ) || empty( $level->enddate ) ) { $r = false; } else { // days til expiration for the standard level $standard = pmpro_getLevel( $level->id ); if ( ! empty( $standard->expiration_number ) ) { if ( $standard->expiration_period == 'Hour' ) { $days = $level->expiration_number; } else if ( $standard->expiration_period == 'Day' ) { $days = $level->expiration_number; } elseif ( $standard->expiration_period == 'Week' ) { $days = $level->expiration_number * 7; } elseif ( $standard->expiration_period == 'Month' ) { $days = $level->expiration_number * 30; } elseif ( $standard->expiration_period == 'Year' ) { $days = $level->expiration_number * 365; } } else { $days = 30; } // are we within the days til expiration? $now = current_time( 'timestamp' ); if( $standard->expiration_period == 'Hour' ){ if( $now + ( $days * 60 ) >= $level->enddate ){ $r = true; } else { $r = false; } } else if ( $now + ( $days * 3600 * 24 ) >= $level->enddate ) { $r = true; } else { $r = false; } } // filter $r = apply_filters( 'pmpro_is_level_expiring_soon', $r, $level ); return $r; } function pmpro_getLevelCost( &$level, $tags = true, $short = false ) { // initial payment if ( ! $short ) { $r = sprintf( __( 'The price for membership is %s now', 'paid-memberships-pro' ), pmpro_formatPrice( $level->initial_payment ) ); } else { if ( pmpro_isLevelFree( $level ) ) { $r = '' . __('Free', 'paid-memberships-pro' ) . ''; } else { $r = sprintf( __( '%s now', 'paid-memberships-pro' ), pmpro_formatPrice( $level->initial_payment ) ); } } // recurring part if ( (float)$level->billing_amount > 0 ) { if ( $level->billing_limit > 1 ) { if ( $level->cycle_number == '1' ) { $r .= sprintf( __( ' and then %1$s per %2$s for %3$d more %4$s.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->billing_amount ), pmpro_translate_billing_period( $level->cycle_period ), $level->billing_limit, pmpro_translate_billing_period( $level->cycle_period, $level->billing_limit ) ); } else { $r .= sprintf( __( ' and then %1$s every %2$d %3$s for %4$d more payments.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->billing_amount ), $level->cycle_number, pmpro_translate_billing_period( $level->cycle_period, $level->cycle_number ), $level->billing_limit ); } } elseif ( $level->billing_limit == 1 ) { $r .= sprintf( __( ' and then %1$s after %2$d %3$s.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->billing_amount ), $level->cycle_number, pmpro_translate_billing_period( $level->cycle_period, $level->cycle_number ) ); } else { if ( $level->billing_amount === $level->initial_payment ) { if ( $level->cycle_number == '1' ) { if ( ! $short ) { $r = sprintf( __( 'The price for membership is %1$s per %2$s.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->initial_payment ), pmpro_translate_billing_period( $level->cycle_period ) ); } else { $r = sprintf( __( '%1$s per %2$s.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->initial_payment ), pmpro_translate_billing_period( $level->cycle_period ) ); } } else { if ( ! $short ) { $r = sprintf( __( 'The price for membership is %1$s every %2$d %3$s.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->initial_payment ), $level->cycle_number, pmpro_translate_billing_period( $level->cycle_period, $level->cycle_number ) ); } else { $r = sprintf( __( '%1$s every %2$d %3$s.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->initial_payment ), $level->cycle_number, pmpro_translate_billing_period( $level->cycle_period, $level->cycle_number ) ); } } } else { if ( $level->cycle_number == '1' ) { $r .= sprintf( __( ' and then %1$s per %2$s.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->billing_amount ), pmpro_translate_billing_period( $level->cycle_period ) ); } else { $r .= sprintf( __( ' and then %1$s every %2$d %3$s.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->billing_amount ), $level->cycle_number, pmpro_translate_billing_period( $level->cycle_period, $level->cycle_number ) ); } } } } else { $r .= '.'; } // add a space $r .= ' '; // trial part if ( $level->trial_limit ) { if ( (float)$level->trial_amount == 0 ) { if ( $level->trial_limit == '1' ) { $r .= ' ' . __( 'After your initial payment, your first payment is Free.', 'paid-memberships-pro' ); } else { $r .= ' ' . sprintf( __( 'After your initial payment, your first %d payments are Free.', 'paid-memberships-pro' ), $level->trial_limit ); } } else { if ( $level->trial_limit == '1' ) { $r .= ' ' . sprintf( __( 'After your initial payment, your first payment will cost %s.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->trial_amount ) ); } else { $r .= ' ' . sprintf( __( 'After your initial payment, your first %1$d payments will cost %2$s.', 'paid-memberships-pro' ), $level->trial_limit, pmpro_formatPrice( $level->trial_amount ) ); } } } // taxes part $tax_state = get_option( 'pmpro_tax_state' ); $tax_rate = get_option( 'pmpro_tax_rate' ); if ( $tax_state && $tax_rate && ! pmpro_isLevelFree( $level ) ) { $r .= sprintf( __( 'Customers in %1$s will be charged %2$s%% tax.', 'paid-memberships-pro' ), $tax_state, round( $tax_rate * 100, 2 ) ); } if ( ! $tags ) { $r = strip_tags( $r ); } $r = apply_filters( 'pmpro_level_cost_text', $r, $level, $tags, $short ); // passing $tags and $short since v1.8 return $r; } /** * Similar to pmpro_getLevelCost, but loops through all levels in the incoming array and puts it all together. */ function pmpro_getLevelsCost( &$levels, $tags = true, $short = false ) { // let's build the array to work from to consolidate recurring info. // recurpmts[cycle_period][cycle_number][billing_limit] = total_amount $initpmt = 0; $recurpmts = array(); $trialperiods = 0; foreach ( $levels as $curlevel ) { $initpmt += $curlevel->initial_payment; if ( (float)$curlevel->billing_amount > 0 ) { if ( array_key_exists( $curlevel->cycle_period, $recurpmts ) ) { if ( array_key_exists( $curlevel->cycle_number, $recurpmts[ $curlevel->cycle_period ] ) ) { if ( array_key_exists( $curlevel->billing_limit, $recurpmts[ $curlevel->cycle_period ][ $curlevel->cycle_number ] ) ) { $recurpmts[ $curlevel->cycle_period ][ $curlevel->cycle_number ][ $curlevel->billing_limit ] += $curlevel->billing_amount; } else { $recurpmts[ $curlevel->cycle_period ][ $curlevel->cycle_number ][ $curlevel->billing_limit ] = $curlevel->billing_amount; } } else { $recurpmts[ $curlevel->cycle_period ][ $curlevel->cycle_number ] = array(); $recurpmts[ $curlevel->cycle_period ][ $curlevel->cycle_number ][ $curlevel->billing_limit ] = $curlevel->billing_amount; } } else { $recurpmts[ $curlevel->cycle_period ] = array(); $recurpmts[ $curlevel->cycle_period ][ $curlevel->cycle_number ] = array(); $recurpmts[ $curlevel->cycle_period ][ $curlevel->cycle_number ][ $curlevel->billing_limit ] = $curlevel->billing_amount; } } if ( $curlevel->trial_limit && intval( $curlevel->trial_limit ) > $trialperiods ) { $trialperiods = intval( $curlevel->trial_limit ); } } // initial payment if ( ! $short ) { $r = sprintf( __( 'The price for membership is %s now', 'paid-memberships-pro' ), pmpro_formatPrice( $initpmt ) ); } else { $r = sprintf( __( '%s now', 'paid-memberships-pro' ), pmpro_formatPrice( $initpmt ) ); } // recurring part $billtextparts = array(); if ( count( $recurpmts ) > 0 ) { foreach ( $recurpmts as $curperiod => $curpddata ) { foreach ( $curpddata as $curcyclenum => $curcycledata ) { foreach ( $curcycledata as $curbilllimit => $curtotal ) { if ( $curbilllimit > 1 ) { if ( $curcyclenum == '1' ) { $billtextparts[] = sprintf( __( '%1$s per %2$s for %3$d more %4$s', 'paid-memberships-pro' ), pmpro_formatPrice( $curtotal ), pmpro_translate_billing_period( $curperiod ), $curbilllimit, pmpro_translate_billing_period( $curperiod, $curbilllimit ) ); } else { $billtextparts[] = sprintf( __( '%1$s every %2$d %3$s for %4$d more payments', 'paid-memberships-pro' ), pmpro_formatPrice( $curtotal ), $curcyclenum, pmpro_translate_billing_period( $curperiod, $curcyclenum ), $curbilllimit ); } } elseif ( $curbilllimit == 1 ) { $billtextparts[] = sprintf( __( '%1$s after %2$d %3$s', 'paid-memberships-pro' ), pmpro_formatPrice( $curtotal ), $curcyclenum, pmpro_translate_billing_period( $curperiod, $curcyclenum ) ); } else { if ( $curcyclenum == '1' ) { $billtextparts[] = sprintf( __( '%1$s every %2$s', 'paid-memberships-pro' ), pmpro_formatPrice( $curtotal ), pmpro_translate_billing_period( $curperiod ) ); } else { $billtextparts[] = sprintf( __( '%1$s every %2$d %3$s', 'paid-memberships-pro' ), pmpro_formatPrice( $curtotal ), $curcyclenum, pmpro_translate_billing_period( $curperiod, $curcyclenum ) ); } } } } } $laststanza = array_pop( $billtextparts ); if ( count( $billtextparts ) > 0 ) { $r .= ', '; $r .= implode( ', ', $billtextparts ); } $r .= ', and ' . $laststanza . '.'; } else { $r .= '.'; } // add a space $r .= ' '; // trial part - not as detailed as the single-level counterpart. Could be improved in the future. if ( $trialperiods > 0 ) { if ( $trialperiods == 1 ) { $r .= __( 'Trial pricing has been applied to the first payment.', 'paid-memberships-pro' ); } else { $r .= sprintf( __( 'Trial pricing has been applied to the first %d payments.', 'paid-memberships-pro' ), $trialperiods ); } } // taxes part $tax_state = get_option( 'pmpro_tax_state' ); $tax_rate = get_option( 'pmpro_tax_rate' ); if ( $tax_state && $tax_rate && ! pmpro_areLevelsFree( $levels ) ) { $r .= sprintf( __( 'Customers in %1$s will be charged %2$s%% tax.', 'paid-memberships-pro' ), $tax_state, round( $tax_rate * 100, 2 ) ); } if ( ! $tags ) { $r = strip_tags( $r ); } /** * Filter the levels cost text. Note the s in levels. Similar to pmpro_levels_cost_text */ $r = apply_filters( 'pmpro_levels_cost_text', $r, $levels, $tags, $short ); return $r; } function pmpro_getLevelExpiration( &$level ) { if ( $level->expiration_number ) { $expiration_text = sprintf( __( 'Membership expires after %1$d %2$s.', 'paid-memberships-pro' ), $level->expiration_number, pmpro_translate_billing_period( $level->expiration_period, $level->expiration_number ) ); } else { $expiration_text = ''; } $expiration_text = apply_filters( 'pmpro_levels_expiration_text', $expiration_text, $level ); $expiration_text = apply_filters( 'pmpro_level_expiration_text', $expiration_text, $level ); // Backwards compatible return $expiration_text; } function pmpro_getLevelsExpiration( &$levels ) { $expirystrings = array(); $ongoinglevelnum = 0; if ( ! empty( $levels ) && ! is_array( $levels ) ) { $levels = array( $levels ); } elseif ( empty( $levels ) ) { $levels = array(); } foreach ( $levels as $curlevel ) { if ( $curlevel->expiration_number ) { $expirystrings[] = sprintf( __( '%1$s membership expires after %2$d %3$s', 'paid-memberships-pro' ), $curlevel->name, $curlevel->expiration_number, pmpro_translate_billing_period( $curlevel->expiration_period, $curlevel->expiration_number ) ); } else { $ongoinglevelnum++; } } $expiration_text = ''; if ( count( $expirystrings ) > 0 ) { $laststanza = array_pop( $expirystrings ); $expiration_text = implode( ', ', $expirystrings ); if ( count( $expirystrings ) > 0 ) { $expiration_text .= ', and '; } $expiration_text .= $laststanza; $expiration_text .= '. '; if ( $ongoinglevelnum > 0 ) { $expiration_text .= 'The remaining membership'; if ( $ongoinglevelnum > 1 ) { $expiration_text .= 's are'; } else { $expiration_text .= ' is'; } $expiration_text .= ' ongoing.'; } } /** * Filter the levels expiration text. Note the s in levels. Similar to pmpro_levels_expiration_text */ $expiration_text = apply_filters( 'pmpro_levels_expiration_text', $expiration_text, $levels ); // Backwards compatible if ( ! empty( $levels ) ) { $first_level = reset($levels); } else { $first_level = false; } $expiration_text = apply_filters( 'pmpro_level_expiration_text', $expiration_text, $first_level ); return $expiration_text; } /** * Get the text to display a membership's expiration date. * * @since 3.0 * * @param object|int $level The level object or ID to get the expiration date for. * @param WP_User|int $user The user object or ID to get the expiration date for. * * @return string The expiration date text. */ function pmpro_get_membership_expiration_text( $level, $user ) { // If a user ID was passed, get the user object. if ( is_numeric( $user ) ) { $user = get_userdata( $user ); } // Make sure that we have a user object. if ( empty( $user ) || ! is_a( $user, 'WP_User' ) ) { return ''; } // If a level ID was passed, get the level object. if ( is_numeric( $level ) ) { $level = pmpro_getSpecificMembershipLevelForUser( $user->ID, (int)$level ); } // Make sure that we have a level object. if ( empty( $level ) || ! is_object( $level ) ) { return ''; } /** * Filter to include the expiration time with expiration date. * Used in adminpages/member-edit/pmpro-class-member-edit-panel-memberships.php. * * @param bool $show_time Whether to show the expiration time with expiration date. * * @return bool Whether to show the expiration time with expiration date. */ $show_time = apply_filters( 'pmpro_show_time_on_expiration_date', false ); // Generate the expiration date text. if ( empty( $level->enddate ) ) { // If the level does not have an enddate, show a dash (—). $text = esc_html_x( '—', 'A dash is shown when there is no expiration date.', 'paid-memberships-pro' ); } elseif ( $show_time ) { // Show the enddate with the time. $text = sprintf( // translators: %1$s is the date and %2$s is the time. esc_html__( '%1$s at %2$s', 'paid-memberships-pro' ), date_i18n( get_option( 'date_format'), $level->enddate ), date_i18n( get_option( 'time_format'), $level->enddate ) ); } else { // Show the enddate without the time. $text = date_i18n( get_option( 'date_format' ), $level->enddate ); } // Apply legacy filter pmpro_memberslist_expires_column. if ( is_admin() && has_filter( 'pmpro_memberslist_expires_column' ) ) { /** * Legacy filter for showing the expiration date in the WP Dashboard. * * Note: Since level data is not passed, this filter is not MMPU-compatible. * * @deprecated 3.0 Use the pmpro_membership_expiration_text filter instead. * * @param string $text The expiration date text to show for this level. * @param WP_User $user The user that the expiration date is for. * * @return string $text The expiration date text to show for this level. */ $text = apply_filters_deprecated( 'pmpro_memberslist_expires_column', array( $text, $user ), '3.0', 'pmpro_membership_expiration_text' ); } // Apply legacy filter pmpro_account_membership_expiration_text. if ( ! is_admin() && has_filter( 'pmpro_account_membership_expiration_text' ) ) { /** * Legacy filter for showing the expiration date on the frontend. * * @deprecated 3.0 Use the pmpro_membership_expiration_text filter instead. * * @param string $text The expiration date text to show for this level. * @param object $level The level that the expiration date is for. * * @return string $text The expiration date text to show for this level. */ $text = apply_filters_deprecated( 'pmpro_account_membership_expiration_text', array( $text, $level ), '3.0', 'pmpro_membership_expiration_text' ); } /** * Filter the expiration date text to show for this level. * * @since 3.0 * * @param string $text The expiration date text to show for this level. * @param object $level The level that the expiration date is for. * @param WP_User $user The user that the expiration date is for. * @param bool $show_time Whether to show the expiration time with expiration date. * * @return string $text The expiration date text to show for this level. */ $text = apply_filters( 'pmpro_membership_expiration_text', $text, $level, $user, $show_time ); return $text; } /** * pmpro_membership_level Meta Functions * * @ssince 1.8.6.5 */ function add_pmpro_membership_level_meta( $level_id, $meta_key, $meta_value, $unique = false ) { return add_metadata( 'pmpro_membership_level', $level_id, $meta_key, $meta_value, $unique ); } function get_pmpro_membership_level_meta( $level_id, $key = '', $single = false ) { return get_metadata( 'pmpro_membership_level', $level_id, $key, $single ); } function update_pmpro_membership_level_meta( $level_id, $meta_key, $meta_value, $prev_value = '' ) { return update_metadata( 'pmpro_membership_level', $level_id, $meta_key, $meta_value, $prev_value ); } function delete_pmpro_membership_level_meta( $level_id, $meta_key, $meta_value = '' ) { return delete_metadata( 'pmpro_membership_level', $level_id, $meta_key, $meta_value ); } /** * pmpro_membership_order Meta Functions */ function add_pmpro_membership_order_meta( $order_id, $meta_key, $meta_value, $unique = false ) { return add_metadata( 'pmpro_membership_order', $order_id, $meta_key, $meta_value, $unique ); } function get_pmpro_membership_order_meta( $order_id, $key = '', $single = false ) { return get_metadata( 'pmpro_membership_order', $order_id, $key, $single ); } function update_pmpro_membership_order_meta( $order_id, $meta_key, $meta_value, $prev_value = '' ) { return update_metadata( 'pmpro_membership_order', $order_id, $meta_key, $meta_value, $prev_value ); } function delete_pmpro_membership_order_meta( $order_id, $meta_key, $meta_value = '' ) { return delete_metadata( 'pmpro_membership_order', $order_id, $meta_key, $meta_value ); } /** * pmpro_subscription Meta Functions */ function add_pmpro_subscription_meta( $subscription_id, $meta_key, $meta_value, $unique = false ) { return add_metadata( 'pmpro_subscription', $subscription_id, $meta_key, $meta_value, $unique ); } function get_pmpro_subscription_meta( $subscription_id, $key = '', $single = false ) { return get_metadata( 'pmpro_subscription', $subscription_id, $key, $single ); } function update_pmpro_subscription_meta( $subscription_id, $meta_key, $meta_value, $prev_value = '' ) { return update_metadata( 'pmpro_subscription', $subscription_id, $meta_key, $meta_value, $prev_value ); } function delete_pmpro_subscription_meta( $subscription_id, $meta_key, $meta_value = '' ) { return delete_metadata( 'pmpro_subscription', $subscription_id, $meta_key, $meta_value ); } function pmpro_hideAds() { global $pmpro_display_ads; return ! $pmpro_display_ads; } function pmpro_displayAds() { global $pmpro_display_ads; return $pmpro_display_ads; } /** * Get the next payment date for a user. * * This function will only return the next payment date for the subscription with * the most recent start date. For this reason, it is not MMPU-compatible. * * @since unknown * * @param int|null $user_id User ID. Defaults to the current user. * @param string $order_status If value passed in is not "success", will get next payment date for a cancelled subscription. * @param string $format Date format to return. * * @return string|bool Date of next payment, or false if no subscription is found. */ function pmpro_next_payment( $user_id = null, $order_status = 'success', $format = 'timestamp' ) { global $wpdb, $current_user; if ( ! $user_id ) { $user_id = $current_user->ID; } if ( $user_id ) { // Convert passed order status to a subscription status. $subscription_status = ( $order_status === 'success' || ( is_array( $order_status ) && in_array( 'success', $order_status ) ) ) ? 'active' : 'cancelled'; $subscriptions = PMPro_Subscription::get_subscriptions_for_user( $user_id, null, $subscription_status ); if ( ! empty( $subscriptions ) ) { return $subscriptions[0]->get_next_payment_date( $format ); } } return false; } if ( ! function_exists( 'last4' ) ) { function last4( $t ) { return substr( $t, strlen( $t ) - 4, 4 ); } } if ( ! function_exists( 'hideCardNumber' ) ) { function hideCardNumber( $c, $dashes = true ) { if ( $c ) { if ( $dashes ) { return 'XXXX-XXXX-XXXX-' . substr( $c, strlen( $c ) - 4, 4 ); } else { return 'XXXXXXXXXXXX' . substr( $c, strlen( $c ) - 4, 4 ); } } else { return ''; } } } // check for existing functions since we didn't use a prefix for this function if ( ! function_exists( 'cleanPhone' ) ) { /** * Function to remove special characters from a phone number. * NOTE: Could probably replace with preg_replace("[^0-9]", "", $phone) * * @since 1.0 * * @param string $phone The phone number to clean. */ function cleanPhone( $phone ) { // if a + is passed, just pass it along if ( strpos( $phone, '+' ) !== false ) { return $phone; } // clean the phone $phone = str_replace( '-', '', $phone ); $phone = str_replace( '.', '', $phone ); $phone = str_replace( '(', '', $phone ); $phone = str_replace( ')', '', $phone ); $phone = str_replace( ' ', '', $phone ); return $phone; } } // check for existing functions since we didn't use a prefix for this function if ( ! function_exists( 'formatPhone' ) ) { /** * Function to format a phone number. * * @since 1.0 * * @param string $phone The phone number to format. */ function formatPhone( $phone ) { $r = cleanPhone( $phone ); if ( strlen( $r ) == 11 ) { $r = substr( $r, 0, 1 ) . ' (' . substr( $r, 1, 3 ) . ') ' . substr( $r, 4, 3 ) . '-' . substr( $r, 7, 4 ); } elseif ( strlen( $r ) == 10 ) { $r = '(' . substr( $r, 0, 3 ) . ') ' . substr( $r, 3, 3 ) . '-' . substr( $r, 6, 4 ); } elseif ( strlen( $r ) == 7 ) { $r = substr( $r, 0, 3 ) . '-' . substr( $r, 3, 4 ); } /** * Filter to do more or less cleaning of phone numbers. * * @since 1.8.4.4 * * @param string $r The formatted phone number. * @param string $phone The original phone number. */ return apply_filters( 'pmpro_format_phone', $r, $phone ); } } function pmpro_showRequiresMembershipMessage() { global $current_user, $post_membership_levels_names; // get the correct message if ( is_feed() ) { $content = get_option( 'pmpro_rsstext' ); $content = str_replace( '!!levels!!', implode( ', ', $post_membership_levels_names ), $content ); } elseif ( $current_user->ID ) { // not a member $content = get_option( 'pmpro_nonmembertext' ); $content = str_replace( '!!levels!!', implode( ', ', $post_membership_levels_names ), $content ); } else { // not logged in! $content = get_option( 'pmpro_notloggedintext' ); $content = str_replace( '!!levels!!', implode( ', ', $post_membership_levels_names ), $content ); } } /** * Function to check if a user has specified membership levels. * * pmpro_hasMembershipLevel() checks if the passed user is a member of the passed level * $level may either be the ID or name of the desired membership_level. (or an array of such or a comma separated string) * If $user_id is omitted, the value will be retrieved from $current_user. * * Return values: * * Success returns boolean true. * * Failure returns a string containing the error message. * * @since 2.12.3 Added support to pass comma separated value to $levels * @since 1.8.5 Added 'e' option for expired members. * @since 1.0.0 * * @param mixed $levels The levels to check. * @param int $user_id The user ID to check. * * @return bool Result of membership query. */ function pmpro_hasMembershipLevel( $levels = null, $user_id = null ) { global $current_user, $wpdb; $return = false; if ( empty( $user_id ) ) { $user_id = $current_user->ID; } if ( ! empty( $user_id ) && is_numeric( $user_id ) ) { // get membership levels for given user $membership_levels = pmpro_getMembershipLevelsForUser( $user_id ); } else { $membership_levels = null; // non-users don't have levels } if ( $levels === '0' || $levels === 0 ) { $return = empty( $membership_levels ); } elseif ( empty( $levels ) ) { $return = ! empty( $membership_levels ); } else { if ( ! is_array( $levels ) ) { // Check for a comma. $levels_str = (string)$levels; if ( strpos( $levels_str, ',' ) !== false ) { // We have a string with at least 1 comma in it, turn it into an array. $level_ids = explode( ',', $levels_str ); // Trim whitespace from the levels ids or names. $levels = array_map( 'trim', $level_ids ); } else { // No comma, but we want an array of levels. $levels = array( $levels ); } } if ( empty( $membership_levels ) ) { // check for negative level $negative_level = false; foreach ( $levels as $level ) { if ( intval( $level ) < 0 ) { $negative_level = true; break; } } // are we looking for non-members or not? if ( $negative_level ) { return true; // -1/etc, negative level } elseif ( in_array( 0, $levels, true ) || in_array( '0', $levels ) ) { $return = true; // 0 level } elseif ( in_array( 'L', $levels ) || in_array( 'l', $levels ) ) { $return = ( ! empty( $user_id ) && $user_id == $current_user->ID ); // L, logged in users } elseif ( in_array( '-L', $levels ) || in_array( '-l', $levels ) ) { $return = ( empty( $user_id ) || $user_id != $current_user->ID ); // -L, not logged in users } elseif ( in_array( 'E', $levels ) || in_array( 'e', $levels ) ) { $sql = "SELECT id FROM $wpdb->pmpro_memberships_users WHERE user_id = " . (int) $user_id . " AND status ='expired' LIMIT 1"; $expired = $wpdb->get_var( $sql ); // E, expired members $return = ! empty( $expired ); } } else { foreach ( $levels as $level ) { if ( strtoupper( $level ) == 'L' ) { // checking if user is logged in if ( ! empty( $user_id ) && $user_id == $current_user->ID ) { $return = true; } } elseif ( strtoupper( $level ) == '-L' ) { // checking if user is logged out if ( empty( $user_id ) || $user_id != $current_user->ID ) { $return = true; } } elseif ( $level === '0' || $level === 0 || strtoupper( $level ) === 'E' ) { continue; // user with levels so not a "non-member" or expired } else { // checking a level id $level_obj = pmpro_getLevel( is_numeric( $level ) ? abs( intval( $level ) ) : $level ); // make sure our level is in a proper format if ( empty( $level_obj ) ) { continue;} //invalid level $found_level = false; foreach ( $membership_levels as $membership_level ) { if ( $membership_level->id == $level_obj->id || $membership_level->name == $level_obj->name) { $found_level = true; } } if ( is_numeric( $level ) && intval( $level ) < 0 && ! $found_level ) { $return = true; } elseif ( is_numeric( $level ) && intval( $level ) > 0 && $found_level ) { $return = true; } elseif ( ! is_numeric( $level ) && $found_level) { // if a level name was passed $return = true; } } } } } $return = apply_filters( 'pmpro_has_membership_level', $return, $user_id, $levels ); return $return; } /** * Remove a membership level from a user. * * @since 1.8.11 * * @param int $level_id ID of the level to remove. * @param int $user_id ID of the user to remove the level from. * @param string $status Status to set the membership to. * * @return bool True if the level was removed, false otherwise. */ function pmpro_cancelMembershipLevel( $level_id, $user_id = null, $status = 'inactive' ) { global $current_user, $wpdb, $pmpro_error; // If we weren't passed a user ID, use the current user. if ( empty( $user_id ) && ! empty( $current_user->ID ) ) { $user_id = $current_user->ID; } elseif ( empty( $user_id ) ) { $pmpro_error = __( 'User ID not found.', 'paid-memberships-pro' ); return false; } // If the user doesn't have the level, we don't need to do anything. $user_levels = pmpro_getMembershipLevelsForUser( $user_id ); $user_level_ids = array_map( 'intval', wp_list_pluck( $user_levels, 'id' ) ); if ( ! in_array( (int)$level_id, $user_level_ids ) ) { return false; } // Set old user levels to be used in the pmpro_do_action_after_all_membership_level_changes() function. pmpro_set_old_user_levels( $user_id ); // If we are not giving the user a new level, run the before change membership action. if ( ! in_array( $status, array( 'changed', 'admin_changed' ) ) ) { /** * Action to run before the membership level changes. * * @param int $level_id ID of the level changed to. * @param int $user_id ID of the user changed. * @param int $cancel_level ID of the level being cancelled if specified. */ do_action( 'pmpro_before_change_membership_level', 0, $user_id, pmpro_getMembershipLevelsForUser( $user_id ), $level_id ); } // Remove the membership level. $cols_set = array( 'status'=> $status, 'enddate' => current_time( 'mysql' ) ); $cols_where = array( 'user_id' => $user_id, 'membership_id' => $level_id, 'status' => 'active' ); $cols_format = array( '%s', '%s'); if ( $wpdb->update( $wpdb->pmpro_memberships_users, $cols_set, $cols_where, $cols_format ) === false ) { $pmpro_error = __( 'Error interacting with database', 'paid-memberships-pro' ) . ': ' . ( $wpdb->last_error ? $wpdb->last_error : 'unavailable' ); return false; } // Check if we should cancel the user's subscription. if ( apply_filters( 'pmpro_cancel_previous_subscriptions', true ) ) { $active_subscriptions = PMPro_Subscription::get_subscriptions_for_user( $user_id, $level_id ); foreach ( $active_subscriptions as $subscription ) { $subscription->cancel_at_gateway(); } } // If we are not giving the user a new level, clear the level cache for this user and run the change membership action. if ( ! in_array( $status, array( 'changed', 'admin_changed' ) ) ) { // Clear the level cache for this user. pmpro_clear_level_cache_for_user( $user_id ); /** * Action to run after the membership level changes. * * @param int $level_id ID of the level changed to. * @param int $user_id ID of the user changed. * @param int $cancel_level ID of the level being cancelled if specified. */ do_action( 'pmpro_after_change_membership_level', 0, $user_id, $level_id ); } return true; } /** * Give a membership level to a user. * * If $user_id is omitted, the value will be retrieved from $current_user. * * @param int|array $level ID of level to set as new level, use 0 to cancel membership * @param int $user_id ID of the user to change levels for * @param string $old_level_status Deprecated. The status to set for the row in the memberships users table. (e.g. inactive, cancelled, admin_cancelled, expired) Defaults to 'inactive'. * @param int $cancel_level Deprecated. If set cancel just this one level instead of all active levels (to support Multiple Memberships per User) * * @return bool|void * Return values: * Success returns boolean true. * Failure returns boolean false. * No change returns null. */ function pmpro_changeMembershipLevel( $level, $user_id = null, $old_level_status = 'inactive', $cancel_level = null ) { global $current_user, $pmpro_error, $wpdb, $pmpro_giving_level; if ( empty( $user_id ) ) { $user_id = $current_user->ID; } if ( empty( $user_id ) ) { $pmpro_error = __( 'User ID not found.', 'paid-memberships-pro' ); return false; } // make sure user id is int for security $user_id = intval( $user_id ); /** * Filter the level passed in. * @since 2.5.8 */ $level = apply_filters( 'pmpro_change_level', $level, $user_id, $old_level_status, $cancel_level ); // Check if we are trying to cancel a single level (Deprecated). if ( empty( $level ) && ! empty( $cancel_level ) ) { _doing_it_wrong( __FUNCTION__, esc_html__( 'The $cancel_level parameter is deprecated. Use pmpro_cancelMembershipLevel() instead.', 'paid-memberships-pro' ), '3.0' ); return pmpro_cancelMembershipLevel( $cancel_level, $user_id, $old_level_status ); } // Check if we are trying to cancel all levels (Deprecated). if ( empty( $level ) && empty( $cancel_level ) ) { _doing_it_wrong( __FUNCTION__, esc_html__( 'The pmpro_cancelMembershipLevel() function should be used to cancel membership levels.', 'paid-memberships-pro' ), '3.0' ); $membership_levels = pmpro_getMembershipLevelsForUser( $user_id ); $success = true; foreach ( $membership_levels as $membership_level ) { $deletion_success = pmpro_cancelMembershipLevel( $membership_level->id, $user_id, $old_level_status ); if ( ! $deletion_success ) { $success = false; } } return $success; } // Now we know that we are actually adding a level. // Check if they they already have the membership level that we are changing them to. if ( ! is_array( $level ) && pmpro_hasMembershipLevel( $level, $user_id ) ) { return; } // Get the level ID. if ( is_array( $level ) ) { // We have a custom level. Make sure that it is valid. if ( empty( $level['membership_id'] ) ) { $pmpro_error = __( 'No membership_id specified in pmpro_changeMembershipLevel().', 'paid-memberships-pro' ); return false; } $level_id = (int) $level['membership_id']; } elseif ( is_numeric( $level ) ) { // Only a level ID was passed. $level_id = (int) $level; } else { // Invalid level passed. $pmpro_error = __( 'Invalid level parameter passed to pmpro_changeMembershipLevel().', 'paid-memberships-pro' ); return false; } // Make sure that level actually exists. if ( empty( pmpro_getLevel( $level_id ) ) ) { $pmpro_error = __( 'Invalid level.', 'paid-memberships-pro' ); return false; } // Set old user levels to be used in the pmpro_do_action_after_all_membership_level_changes() function. pmpro_set_old_user_levels( $user_id ); // Get all levels for the user. $membership_levels = pmpro_getMembershipLevelsForUser( $user_id ); $membership_ids = wp_list_pluck( $membership_levels, 'id' ); /** * Action to run before the membership level changes. * * @param int $level_id ID of the level changed to. * @param int $user_id ID of the user changed. * @param array $old_levels array of prior levels the user belonged to. * @param int $cancel_level ID of the level being cancelled if specified */ do_action( 'pmpro_before_change_membership_level', $level_id, $user_id, $membership_levels, null ); // Cancel all membership levels for this user in the same level group if only one level per group is allowed. $level_group_id = pmpro_get_group_id_for_level( $level_id ); $level_group = pmpro_get_level_group( $level_group_id ); /** * Filter whether old levels should be deactivated or not. * Typically you'll want to hook into pmpro_before_change_membership_level * or pmpro_after_change_membership_level later to run your own deactivation logic. * * @since 1.8.11 * @var $pmpro_deactivate_old_levels bool True or false if levels should be deactivated. Defaults to true. */ $pmpro_deactivate_old_levels = apply_filters( 'pmpro_deactivate_old_levels', true ); // If we are deactivating old levels, typically we will want to put the old level in 'changed status. // The exception is if the level is being changed by an administrator, in which case 'admin_changed' would have been passed to this function. $change_status = 'admin_changed' === $old_level_status ? 'admin_changed' : 'changed'; if ( ! empty( $level_group) && empty( $level_group->allow_multiple_selections ) && $pmpro_deactivate_old_levels ) { // Get all levels in the group. $levels_in_group = pmpro_get_levels_for_group( $level_group->id ); $group_level_ids = wp_list_pluck( $levels_in_group, 'id' ); // Get the intersection of the two arrays. $levels_to_cancel = array_intersect( $group_level_ids, $membership_ids ); // Cancel the levels. foreach ( $levels_to_cancel as $level_to_cancel ) { pmpro_cancelMembershipLevel( $level_to_cancel, $user_id, $change_status ); } } elseif ( $pmpro_deactivate_old_levels ) { // If the user already has this membership level, we still want to cancel it. if ( in_array( $level_id, $membership_ids ) ) { pmpro_cancelMembershipLevel( $level_id, $user_id, $change_status ); } } // Insert current membership if ( ! empty( $level ) ) { // make sure the dates are in good formats if ( is_array( $level ) ) { // Better support mySQL Strict Mode by passing a proper enum value for cycle_period if ( $level['cycle_period'] == '' ) { $level['cycle_period'] = 0; } // clean up date formatting (string/not string) $level['startdate'] = preg_replace( '/\'/', '', $level['startdate'] ); $level['enddate'] = preg_replace( '/\'/', '', $level['enddate'] ); $sql = $wpdb->prepare( " INSERT INTO {$wpdb->pmpro_memberships_users} (`user_id`, `membership_id`, `code_id`, `initial_payment`, `billing_amount`, `cycle_number`, `cycle_period`, `billing_limit`, `trial_amount`, `trial_limit`, `startdate`, `enddate`) VALUES ( %d, %d, %d, %s, %s, %d, %s, %d, %s, %d, %s, %s )", $level['user_id'], // integer $level['membership_id'], // integer $level['code_id'], // integer $level['initial_payment'], // float (string) $level['billing_amount'], // float (string) $level['cycle_number'], // integer $level['cycle_period'], // string (enum) $level['billing_limit'], // integer $level['trial_amount'], // float (string) $level['trial_limit'], // integer $level['startdate'], // string (date) $level['enddate'] // string (date) ); } else { $sql = $wpdb->prepare( " INSERT INTO {$wpdb->pmpro_memberships_users} ( `user_id`, `membership_id`, `code_id`, `initial_payment`, `billing_amount`, `cycle_number`, `cycle_period`, `billing_limit`, `trial_amount`, `trial_limit`, `startdate`, `enddate`) VALUES ( %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %s, %s )", $user_id, $level_id, '0', '0', '0', '0', '0', '0', '0', '0', current_time( 'mysql' ), '0000-00-00 00:00:00' ); } if ( false === $wpdb->query( $sql ) ) { $pmpro_error = sprintf( __( 'Error interacting with database: %s', 'paid-memberships-pro' ), ( ! empty( $wpdb->last_error ) ? $wpdb->last_error : 'unavailable' ) ); return false; } /** * Allow filtering whether to remove duplicate "active" memberships by setting them to "changed". * * @since 2.6.6 * * @param bool $remove_duplicate_memberships Whether to remove duplicate "active" memberships by setting them to "changed". */ $remove_duplicate_memberships = apply_filters( 'pmpro_remove_duplicate_membership_entries', true ); if ( $remove_duplicate_memberships ) { $wpdb->query( $wpdb->prepare( " UPDATE {$wpdb->pmpro_memberships_users} SET status = %s, enddate = %s WHERE user_id = %d AND membership_id = %d AND status = %s AND id != %d ", 'changed', current_time( 'mysql' ), $user_id, $level_id, 'active', $wpdb->insert_id // Ignore the membership that we just added. ) ); } } // Clear the level cache for this user. pmpro_clear_level_cache_for_user( $user_id ); /** * Action to run after the membership level changes. * * @param int $level_id ID of the level changed to. * @param int $user_id ID of the user changed. * @param int $cancel_level ID of the level being cancelled if specified. */ do_action( 'pmpro_after_change_membership_level', $level_id, $user_id, null ); return true; } /** * Set old user levels to be used in the pmpro_do_action_after_all_membership_level_changes() function. * * @param int $user_id ID of the user to set the old levels for. */ function pmpro_set_old_user_levels( $user_id ) { global $pmpro_old_user_levels; if ( empty( $pmpro_old_user_levels ) ) { $pmpro_old_user_levels = array(); } if ( ! array_key_exists( $user_id, $pmpro_old_user_levels ) ) { $old_levels = pmpro_getMembershipLevelsForUser( $user_id ); $pmpro_old_user_levels[$user_id] = empty( $old_levels ) ? array() : $old_levels; } } /** * Clear the membership level cache for a user. * * @param int $user_id ID of the user to clear the cache for. */ function pmpro_clear_level_cache_for_user( $user_id ) { // Removed cached global. global $all_membership_levels; unset( $all_membership_levels[ $user_id ] ); // Remove levels cache for user. $cache_key = 'user_' . $user_id . '_levels'; wp_cache_delete( $cache_key, 'pmpro' ); wp_cache_delete( $cache_key . '_all', 'pmpro' ); wp_cache_delete( $cache_key . '_active', 'pmpro' ); // Update user data. pmpro_set_current_user(); } /** * Runs after all membership level changes have been performed. * * @param mixed $filter_contents to not break the wp_redirect filter. */ function pmpro_do_action_after_all_membership_level_changes( $filter_contents = null ) { global $pmpro_old_user_levels; if ( empty( $pmpro_old_user_levels ) ) { // No level changes occurred, return. return $filter_contents; } // Clear global so that we don't run twice for same level changes $pmpro_old_user_levels_copy = $pmpro_old_user_levels; $pmpro_old_user_levels = null; /** * Run code after all membership level changes have occurred. Users who have had changes * will be stored in the global $pmpro_old_user_levels array. * * @since 2.6 * @param array $pmpro_old_user_levels_copy array of user_id => array( old_level_objs ) */ do_action( 'pmpro_after_all_membership_level_changes', $pmpro_old_user_levels_copy ); return $filter_contents; } add_action( 'template_redirect', 'pmpro_do_action_after_all_membership_level_changes', 2 ); add_filter( 'wp_redirect', 'pmpro_do_action_after_all_membership_level_changes', 100 ); add_action( 'pmpro_membership_post_membership_expiry', 'pmpro_do_action_after_all_membership_level_changes' ); add_action( 'shutdown', 'pmpro_do_action_after_all_membership_level_changes' ); /** * Function to list WordPress categories in hierarchical format. * * This is a helper function for the Membership Categories section in adminpages/membershiplevels.php * * @since 1.8.11 * * @param int $parent_id * @param array $level_categories */ function pmpro_listCategories( $parent_id = 0, $level_categories = array() ) { $args = array( 'parent' => $parent_id, 'hide_empty' => false, ); $cats = get_categories( apply_filters( 'pmpro_list_categories_args', $args ) ); if ( $cats ) { foreach ( $cats as $cat ) { if ( ! empty( $level_categories ) ) { $checked = checked( in_array( $cat->term_id, $level_categories ), true, false ); } else { $checked = ''; } ?>
>
term_id, $level_categories ); ?> get_var( "SELECT id FROM {$wpdb->pmpro_membership_levels} WHERE name = '" . esc_sql( $safe ) . "' LIMIT 1" ) ) ) <= 0 ) { return __( 'Membership level not found.', 'paid-memberships-pro' ); } } if ( $value ) { $sql = "REPLACE INTO {$wpdb->pmpro_memberships_categories} (`membership_id`,`category_id`) VALUES ('" . esc_sql( $level ) . "','" . esc_sql( $category ) . "')"; $wpdb->query( $sql ); if ( $wpdb->last_error ) { return $wpdb->last_error; } } else { $sql = "DELETE FROM {$wpdb->pmpro_memberships_categories} WHERE `membership_id` = '" . esc_sql( $level ) . "' AND `category_id` = '" . esc_sql( $category ). "' LIMIT 1"; $wpdb->query( $sql ); if ( $wpdb->last_error ) { return $wpdb->last_error; } } return true; } /** * pmpro_updateMembershipCategories() ensures that all those and only those categories given * are associated with the given membership level. * * @param string|int $level is a valid membership level ID or name * @param int[] $categories is an array of post category IDs * * @return string|true * Return values: * Success returns boolean true. * Failure returns a string containing the error message. */ function pmpro_updateMembershipCategories( $level, $categories ) { global $wpdb; if ( ! is_numeric( $level ) ) { $level = $wpdb->get_var( "SELECT id FROM $wpdb->pmpro_membership_levels WHERE name = '" . esc_sql( $level ) . "' LIMIT 1" ); if ( empty( $level ) ) { return __( 'Membership level not found.', 'paid-memberships-pro' ); } } // remove all existing links... $sqlQuery = "DELETE FROM $wpdb->pmpro_memberships_categories WHERE `membership_id` = '" . esc_sql( $level ) . "'"; $wpdb->query( $sqlQuery ); if ( $wpdb->last_error ) { return $wpdb->last_error; } // add the given links [back?] in... foreach ( $categories as $cat ) { if ( is_string( $r = pmpro_toggleMembershipCategory( $level, $cat, true ) ) ) { // uh oh, error return $r; } } // all good return true; } /** * pmpro_getMembershipCategories() returns the categories for a given level * * @param int $level_id is a valid membership level ID * * @return int[] */ function pmpro_getMembershipCategories( $level_id ) { $level_id = intval( $level_id ); global $wpdb; $categories = $wpdb->get_col( "SELECT c.category_id FROM {$wpdb->pmpro_memberships_categories} AS c WHERE c.membership_id = '" . esc_sql( $level_id ) . "'" ); return $categories; } function pmpro_isAdmin( $user_id = null ) { global $current_user; if ( ! $user_id ) { $user_id = $current_user->ID; } if ( ! $user_id ) { return false; } $admincap = user_can( $user_id, 'manage_options' ); if ( $admincap ) { return true; } else { return false; } } function pmpro_replaceUserMeta( $user_id, $meta_keys, $meta_values, $prev_values = null ) { // expects all arrays for last 3 params or all strings if ( ! is_array( $meta_keys ) ) { $meta_keys = array( $meta_keys ); $meta_values = array( $meta_values ); $prev_values = array( $prev_values ); } for ( $i = 0; $i < count( $meta_values ); $i++ ) { if ( isset( $prev_values[ $i ] ) ) { update_user_meta( $user_id, $meta_keys[ $i ], $meta_values[ $i ], $prev_values[ $i ] ); } else { $old_value = get_user_meta( $user_id, $meta_keys[ $i ], true ); if ( $old_value ) { update_user_meta( $user_id, $meta_keys[ $i ], $meta_values[ $i ], $old_value ); } else { update_user_meta( $user_id, $meta_keys[ $i ], $meta_values[ $i ] ); } } } return $i; } function pmpro_getMetavalues( $query ) { global $wpdb; $results = $wpdb->get_results( $query ); $r = new stdClass(); foreach ( $results as $result ) { if ( ! empty( $r ) && ! empty( $result->key ) ) { $r->{$result->key} = $result->value; } } return $r; } // function to return the pagination string function pmpro_getPaginationString( $page = 1, $totalitems = 0, $limit = 15, $adjacents = 1, $targetpage = '/', $pagestring = '&pn=' ) { // defaults if ( ! $adjacents ) { $adjacents = 1; } if ( ! $limit ) { $limit = 15; } if ( ! $page ) { $page = 1; } if ( ! $targetpage ) { $targetpage = '/'; } // other vars $prev = $page - 1; // previous page is page - 1 $next = $page + 1; // next page is page + 1 $lastpage = ceil( $totalitems / $limit ); // lastpage is = total items / items per page, rounded up. $lpm1 = $lastpage - 1; // last page minus 1 /* Now we apply our rules and draw the pagination object. We're actually saving the code to a variable in case we want to draw it more than once. */ $pagination = ''; if ( $lastpage > 1 ) { $pagination .= ' 1 ) { $pagination .= "« prev"; } else { $pagination .= '« prev'; } // pages if ( $lastpage < 7 + ( $adjacents * 2 ) ) { for ( $counter = 1; $counter <= $lastpage; $counter++ ) { if ( $counter == $page ) { $pagination .= "$counter"; } else { $pagination .= '$counter"; } } } elseif ( $lastpage >= 7 + ( $adjacents * 2 ) ) { // close to beginning; only hide later pages if ( $page < 1 + ( $adjacents * 3 ) ) { for ( $counter = 1; $counter < 4 + ( $adjacents * 2 ); $counter++ ) { if ( $counter == $page ) { $pagination .= "$counter"; } else { $pagination .= '$counter"; } } $pagination .= '...'; $pagination .= '$lpm1"; $pagination .= '$lastpage"; } // in middle; hide some front and some back elseif ( $lastpage - ( $adjacents * 2 ) > $page && $page > ( $adjacents * 2 ) ) { $pagination .= '1'; $pagination .= '2'; $pagination .= '...'; for ( $counter = $page - $adjacents; $counter <= $page + $adjacents; $counter++ ) { if ( $counter == $page ) { $pagination .= "$counter"; } else { $pagination .= '$counter"; } } $pagination .= '...'; $pagination .= '$lpm1"; $pagination .= '$lastpage"; } // close to end; only hide early pages else { $pagination .= '1'; $pagination .= '2'; $pagination .= '...'; for ( $counter = $lastpage - ( 1 + ( $adjacents * 3 ) ); $counter <= $lastpage; $counter++ ) { if ( $counter == $page ) { $pagination .= "$counter"; } else { $pagination .= '$counter"; } } } } // next button if ( $page < $counter - 1 ) { $pagination .= 'next »'; } else { $pagination .= 'next »'; } $pagination .= "\n"; } return $pagination; } function pmpro_calculateInitialPaymentRevenue( $s = null, $l = null ) { global $wpdb; // if we're limiting users by search if ( $s || $l ) { $user_ids_query = "SELECT u.ID FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id WHERE mu.status = 'active' "; if ( $s ) { $user_ids_query .= "AND (u.user_login LIKE '%" . esc_sql( $s ) . "%' OR u.user_email LIKE '%" . esc_sql( $s ) . "%' OR um.meta_value LIKE '%$" . esc_sql( $s ) . "%') "; } if ( $l ) { $user_ids_query .= "AND mu.membership_id = '" . esc_sql( $l ) . "' "; } } // query to sum initial payments $sqlQuery = "SELECT SUM(initial_payment) FROM $wpdb->pmpro_memberships_users WHERE `status` = 'active' "; if ( ! empty( $user_ids_query ) ) { $sqlQuery .= 'AND user_id IN(' . $user_ids_query . ') '; } $total = $wpdb->get_var( $sqlQuery ); return (double) $total; } function pmpro_calculateRecurringRevenue( $s, $l ) { global $wpdb; // if we're limiting users by search if ( $s || $l ) { $user_ids_query = "AND user_id IN(SELECT u.ID FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id WHERE mu.status = 'active' "; if ( $s ) { $user_ids_query .= "AND (u.user_login LIKE '%" . esc_sql( $s ) . "%' OR u.user_email LIKE '%" . esc_sql( $s ) . "%' OR um.meta_value LIKE '%" . esc_sql( $s ) . "%') "; } if ( $l ) { $user_ids_query .= "AND mu.membership_id = '" . esc_sql( $l ) . "' "; } $user_ids_query .= ")"; } else { $user_ids_query = ''; } // 4 queries to get annual earnings for each cycle period. currently ignoring trial periods and billing limits. $sqlQuery = " SELECT SUM((12/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Month' AND cycle_number <> 12 $user_ids_query UNION SELECT SUM((365/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Day' AND cycle_number <> 365 $user_ids_query UNION SELECT SUM((24/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Hour' AND cycle_number <> 24 $user_ids_query UNION SELECT SUM((52/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Week' AND cycle_number <> 52 $user_ids_query UNION SELECT SUM(billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Year' $user_ids_query "; $annual_revenues = $wpdb->get_col( $sqlQuery ); $total = 0; foreach ( $annual_revenues as $r ) { $total += $r; } return $total; } /** * Generate a Username from the provided first name, last name or email address. * * @param string $firstname User-submitted First Name. * @param string $lastname User-submitted Last Name. * @param string $email User-submitted Email Address. * * @return string $username. */ function pmpro_generateUsername( $firstname = '', $lastname = '', $email = '' ) { // Strip all non-alpha characters from first and last name. if ( ! empty( $firstname) ) { $firstname = preg_replace( '/[^A-Za-z]/', '', $firstname ); } if ( ! empty( $lastname ) ) { $lastname = preg_replace( '/[^A-Za-z]/', '', $lastname ); } // Try to create username using first and last name. if ( ! empty( $firstname ) && ! empty( $lastname ) ) { // Create username using first initial + last name. $username = substr( $firstname, 0, 1 ) . $lastname; } elseif ( ! empty( $firstname ) ) { // Create username using only first name. $username = $firstname; } elseif ( ! empty( $lastname ) ) { // Create username using only last name. $username = $lastname; } // If no username yet or one based on name exists, // try to create username using email address. if ( ( empty( $username ) || username_exists( $username ) ) && ! empty( $email ) && is_email( $email ) ) { // Break email into two parts, before and after the @ symbol. $emailparts = explode( '@', $email ); if ( ! empty( $emailparts ) ) { // Set username to the string before the email's @ symbol. $email = preg_replace( '/[^A-Za-z0-9]/', '', $emailparts[0] ); $username = $email; } } // No Username yet. Generate a random one. if ( empty( $username ) ) { $username = wp_generate_password( 10, false ); } // Check if username is taken and continue to append an incremented number until it is unique. $taken = true; $count = 0; while ( $taken ) { // Append a number to the end of the username. if ( $count ) { $username = preg_replace( '/[0-9]/', '', $username ) . $count; } // Check if the username is taken. $taken = username_exists( $username ); // Increment the number. $count++; } // Sanitize the username. $username = sanitize_user( $username ); // We must have a good username now. return $username; } /** * Get a new random code for discount codes. */ function pmpro_getDiscountCode( $seed = null ) { global $wpdb; // We mix this with the seed to make sure we get unique codes. static $count = 0; $count++; if( defined( 'AUTH_KEY' ) && defined( 'SECURE_AUTH_KEY' ) ) { $auth_code = AUTH_KEY; $secure_auth_code = SECURE_AUTH_KEY; } else { //Generate our own random string and hash it $auth_code = md5( rand() ); $secure_auth_code = md5( rand() ); } while ( empty( $code ) ) { $scramble = md5( $auth_code . microtime() . $seed . $secure_auth_code . $count ); $code = substr( $scramble, 0, 10 ); $check = $wpdb->get_var( "SELECT code FROM $wpdb->pmpro_discount_codes WHERE code = '" . esc_sql( $code ) . "' LIMIT 1" ); if ( $check || is_numeric( $code ) ) { $code = null; } } return strtoupper( $code ); } /** * Is a discount code valid - $level_id could be a scalar or an array (or unset) */ function pmpro_checkDiscountCode( $code, $level_id = null, $return_errors = false ) { global $wpdb; $error = false; $dbcode = false; // no code, no code if ( empty( $code ) ) { $error = __( 'No code was given to check.', 'paid-memberships-pro' ); } // get code from db if ( ! $error ) { $dbcode = $wpdb->get_row( "SELECT *, UNIX_TIMESTAMP(CONVERT_TZ(starts, '+00:00', @@global.time_zone)) as starts, UNIX_TIMESTAMP(CONVERT_TZ(expires, '+00:00', @@global.time_zone)) as expires FROM $wpdb->pmpro_discount_codes WHERE code ='" . esc_sql( $code ) . "' LIMIT 1" ); // did we find it? if ( empty( $dbcode->id ) ) { $error = __( 'The discount code could not be found.', 'paid-memberships-pro' ); } } // check if the code has started if ( ! $error ) { // fix the date timestamps $dbcode->starts = strtotime( date_i18n( 'm/d/Y', $dbcode->starts ) ); $dbcode->expires = strtotime( date_i18n( 'm/d/Y', $dbcode->expires ) ); // today $today = strtotime( date_i18n( 'm/d/Y H:i:00', current_time( 'timestamp' ) ) ); // has this code started yet? if ( ! empty( $dbcode->starts ) && $dbcode->starts > $today ) { $error = sprintf( __( 'This discount code goes into effect on %s.', 'paid-memberships-pro' ), date_i18n( get_option( 'date_format' ), $dbcode->starts ) ); } } // check if the code is expired if ( ! $error ) { if ( ! empty( $dbcode->expires ) && $dbcode->expires < $today ) { $error = sprintf( __( 'This discount code expired on %s.', 'paid-memberships-pro' ), date_i18n( get_option( 'date_format' ), $dbcode->expires ) ); } } // have we run out of uses? if ( ! $error ) { if ( $dbcode->uses > 0 ) { $used = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->pmpro_discount_codes_uses WHERE code_id = '" . esc_sql( $dbcode->id ) . "'" ); if ( $used >= $dbcode->uses ) { $error = __( 'This discount code is no longer valid.', 'paid-memberships-pro' ); } } } // if a level was passed check if this code applies if ( ! $error ) { $pmpro_check_discount_code_levels = apply_filters( 'pmpro_check_discount_code_levels', true, $dbcode->id ); if ( ! empty( $level_id ) && $pmpro_check_discount_code_levels ) { // clean up level id for security before the database call if ( is_array( $level_id ) ) { $levelnums = array_map( 'intval', $level_id ); $level_id = implode( ',', $levelnums ); } else { $level_id = intval( $level_id ); } $code_level = $wpdb->get_row( "SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id WHERE cl.code_id = '" . esc_sql( $dbcode->id ) . "' AND cl.level_id IN (" . $level_id . ") LIMIT 1" ); // $level_id is already escaped above. if ( empty( $code_level ) ) { $error = __( 'This discount code does not apply to this membership level.', 'paid-memberships-pro' ); } } } /** * Filter the results of the discount code check. * * @since 1.7.13.1 * * @param bool $okay true if code check is okay or false if there was an error * @param object $dbcode Object containing code data from the database row * @param int|array $level_id ID of the level the user is checking out for. * @param string $code Discount code string. * * @return mixed $okay true if okay, false or error message string if not okay */ $okay = ! $error; $pmpro_check_discount_code = apply_filters( 'pmpro_check_discount_code', $okay, $dbcode, $level_id, $code ); if ( is_string( $pmpro_check_discount_code ) ) { $error = $pmpro_check_discount_code; // string returned, this is an error } elseif ( ! $pmpro_check_discount_code && ! $error ) { $error = true; // no error before, but filter returned error } elseif ( $pmpro_check_discount_code ) { $error = false; // filter is true, so error false } // return if ( $error ) { // there was an error if ( ! empty( $return_errors ) ) { return array( false, $error ); } else { return false; } } else { // guess we're all good if ( ! empty( $return_errors ) ) { return array( true, __( 'This discount code is okay.', 'paid-memberships-pro' ) ); } else { return true; } } } function pmpro_no_quotes( $s, $quotes = array( "'", '"' ) ) { if ( empty( $s ) ) { $s = ''; } return str_replace( $quotes, '', $s ); } /** * From: http://www.php.net/manual/en/function.implode.php#86845 */ function pmpro_implodeToEnglish( $array, $conjunction = 'and' ) { // sanity check if ( ! $array || ! count( $array ) ) { return ''; } // get last element $last = array_pop( $array ); // if it was the only element - return it if ( ! count( $array ) ) { return $last; } // possibly translate the conjunction if ( $conjunction == 'and' ) { $conjunction = __( 'and', 'paid-memberships-pro' ); } // List the elements in a comma-separated list. $start = implode( ', ', $array ); // Add the Oxford comma if needed. if ( count( $array ) > 1 ) { $start .= ','; } // Output the elements. return $start . ' ' . $conjunction . ' ' . $last; } // from yoast wordpress seo function pmpro_text_limit( $text, $limit, $finish = '…' ) { if ( strlen( $text ) > $limit ) { $text = substr( $text, 0, $limit ); $text = substr( $text, 0, - ( strlen( strrchr( $text, ' ' ) ) ) ); $text .= $finish; } return $text; } /** * Filters the separator used between action navigation links. * * @since 2.3 */ function pmpro_actions_nav_separator() { $separator = apply_filters( 'pmpro_actions_nav_separator', ' | ' ); return $separator; } /** * pmpro_get_no_access_message to return the appropriate content message for the protected content. * * @param array $level_ids The array of level IDs this post is protected for. * @param array $level_names The array of names for the levels this post is protected for. * * @return string $content The appropriate content message for the given user/visitor and required levels. * */ function pmpro_get_no_access_message( $content, $level_ids, $level_names = NULL ) { global $current_user; if ( empty( $level_ids ) ) { $level_ids = array(); } if ( empty( $level_names ) ) { $level_names = array(); foreach ( $level_ids as $key => $id ) { $level_obj = pmpro_getLevel( $id ); if ( ! empty( $level_obj ) ) { $level_names[] = $level_obj->name; } } } // Hide levels which don't allow signups by default. if( ! apply_filters( 'pmpro_membership_content_filter_disallowed_levels', false, $level_ids, $level_names ) ) { foreach ( $level_ids as $key => $id ) { // Does this level allow registrations? $level_obj = pmpro_getLevel( $id ); if ( empty( $level_obj ) || empty( $level_obj->allow_signups ) ) { unset( $level_ids[$key] ); unset( $level_names[$key] ); } } } $pmpro_content_message_pre = '
'; $pmpro_content_message_post = '
'; $sr_search = array( '!!levels!!', '!!referrer!!', '!!login_url!!', '!!login_page_url!!', '!!levels_url!!', '!!levels_page_url!!' ); $sr_replace = array( pmpro_implodeToEnglish( $level_names ), urlencode( site_url( esc_url_raw( $_SERVER['REQUEST_URI'] ) ) ), esc_url( pmpro_login_url() ), esc_url( pmpro_login_url() ), esc_url( pmpro_url( 'levels' ) ), esc_url( pmpro_url( 'levels' ) ) ); // Get the correct message to show at the bottom. if ( is_feed() ) { $newcontent = apply_filters( 'pmpro_rss_text_filter', stripslashes( get_option( 'pmpro_rsstext' ) ) ); $content .= $pmpro_content_message_pre . str_replace( $sr_search, $sr_replace, $newcontent ) . $pmpro_content_message_post; } elseif ( $current_user->ID ) { //not a member $newcontent = apply_filters( 'pmpro_non_member_text_filter', stripslashes( get_option( 'pmpro_nonmembertext' ) ) ); $content .= $pmpro_content_message_pre . str_replace( $sr_search, $sr_replace, $newcontent ) . $pmpro_content_message_post; } else { //not logged in! $newcontent = apply_filters( 'pmpro_not_logged_in_text_filter', stripslashes( get_option( 'pmpro_notloggedintext' ) ) ); $content .= $pmpro_content_message_pre . str_replace( $sr_search, $sr_replace, $newcontent ) . $pmpro_content_message_post; } return $content; } /** * pmpro_getMembershipLevelForUser() returns the first active membership level for a user * * If $user_id is omitted, the value will be retrieved from $current_user. * * @return false|object * Return values: * Success returns the level object. * Failure returns false. */ function pmpro_getMembershipLevelForUser( $user_id = null, $force = false ) { if ( empty( $user_id ) ) { global $current_user; $user_id = $current_user->ID; } if ( empty( $user_id ) ) { return false; } // make sure user id is int for security $user_id = intval( $user_id ); global $all_membership_levels; if ( isset( $all_membership_levels[ $user_id ] ) && ! $force ) { return $all_membership_levels[ $user_id ]; } else { global $wpdb; $all_membership_levels[ $user_id ] = $wpdb->get_row( "SELECT l.id AS ID, l.id as id, mu.id as subscription_id, l.name AS name, l.description, l.confirmation, l.expiration_number, l.expiration_period, l.allow_signups, mu.initial_payment, mu.billing_amount, mu.cycle_number, mu.cycle_period, mu.billing_limit, mu.trial_amount, mu.trial_limit, mu.code_id as code_id, UNIX_TIMESTAMP( CONVERT_TZ(startdate, '+00:00', @@global.time_zone) ) as startdate, UNIX_TIMESTAMP( CONVERT_TZ(enddate, '+00:00', @@global.time_zone) ) as enddate FROM {$wpdb->pmpro_membership_levels} AS l JOIN {$wpdb->pmpro_memberships_users} AS mu ON (l.id = mu.membership_id) WHERE mu.user_id = $user_id AND mu.status = 'active' LIMIT 1" ); // if null, change to false to avoid user meta conflicts if ( empty( $all_membership_levels[ $user_id ] ) ) { $all_membership_levels[ $user_id ] = false; } // Round off prices if ( ! empty( $all_membership_levels[$user_id] ) ) { if ( isset( $all_membership_levels[$user_id]->initial_payment ) ) { $all_membership_levels[$user_id]->initial_payment = pmpro_round_price( $all_membership_levels[$user_id]->initial_payment ); } if ( isset( $all_membership_levels[$user_id]->billing_amount ) ) { $all_membership_levels[$user_id]->billing_amount = pmpro_round_price( $all_membership_levels[$user_id]->billing_amount ); } if ( isset( $all_membership_levels[$user_id]->trial_amount ) ) { $all_membership_levels[$user_id]->trial_amount = pmpro_round_price( $all_membership_levels[$user_id]->trial_amount ); } } /** * pmpro_get_membership_level_for_user filter. * * Filters the returned level. * * @since 1.8.5.4 * * @param object $level Level object. */ $all_membership_levels[ $user_id ] = apply_filters( 'pmpro_get_membership_level_for_user', $all_membership_levels[ $user_id ], $user_id ); return $all_membership_levels[ $user_id ]; } } /** * pmpro_getMembershipLevelsForUser() returns the membership levels for a user * * If $user_id is omitted, the value will be retrieved from $current_user. * By default it only includes active memberships. * * @return array|false * Return values: * Success returns an array of level objects. * Failure returns false. */ function pmpro_getMembershipLevelsForUser( $user_id = null, $include_inactive = false ) { global $current_user, $pmpro_pages; if ( empty( $user_id ) ) { $user_id = $current_user->ID; } if ( empty( $user_id ) ) { return false; } // make sure user id is int for security $user_id = intval( $user_id ); // Admins have special rules for membership levels. Check them here. if ( $user_id == $current_user->ID && current_user_can( 'manage_options' ) ) { // Make sure that we are not on a page where we want to always show the user's true levels. if ( ! is_admin() && ( empty( $GLOBALS['wp_query'] ) || ! pmpro_is_checkout() ) && ( empty( $pmpro_pages['account'] ) || ! is_page( $pmpro_pages['account'] ) ) && ( empty( $pmpro_pages['billing'] ) || ! is_page( $pmpro_pages['billing'] ) ) && ( empty( $pmpro_pages['cancel'] ) || ! is_page( $pmpro_pages['cancel'] ) ) && ( empty( $pmpro_pages['checkout'] ) || ! is_page( $pmpro_pages['checkout'] ) ) && ( empty( $pmpro_pages['confirmation'] ) || ! is_page( $pmpro_pages['confirmation'] ) ) && ( empty( $pmpro_pages['invoice'] ) || ! is_page( $pmpro_pages['invoice'] ) ) && ( empty( $pmpro_pages['levels'] ) || ! is_page( $pmpro_pages['levels'] ) ) && ! apply_filters( 'pmpro_disable_admin_membership_access', false ) ) { // This user meta can be changed via the admin bar. $admin_membership_access = get_user_meta( $current_user->ID, 'pmpro_admin_membership_access', true ); if ( 'no' === $admin_membership_access ) { return array(); } elseif ( 'yes' === $admin_membership_access ) { $all_levels = pmpro_getAllLevels( true ); // Make sure that each level has all the necessary fields. foreach ( $all_levels as $key => $level ) { $level->ID = $level->id; $level->subscription_id = null; $level->code_id = null; $level->startdate = strtotime( '-1 year' ); $level->enddate = strtotime ( '+1 year' ); } return $all_levels; } // If we get here, the admin membership access is set to 'current'. // Continue checking access as normal. } } global $wpdb; /** * We are going to see if cache is set before doing the query and use that if it is. * * In a default environment with no external object cache, the value is cached in that request and * reduces future MySQL requests. If there is an external object cache like Redis then it will be * persisted until the user level changes. **/ $cache_key = 'user_' . $user_id . '_levels' . ( $include_inactive ? '_all' : '_active' ); $levels = wp_cache_get( $cache_key, 'pmpro' ); if ( $levels === false ) { $levels = $wpdb->get_results( "SELECT l.id AS ID, l.id as id, mu.id as subscription_id, l.name, l.description, l.confirmation, l.expiration_number, l.expiration_period, mu.initial_payment, mu.billing_amount, mu.cycle_number, mu.cycle_period, mu.billing_limit, mu.trial_amount, mu.trial_limit, mu.code_id as code_id, UNIX_TIMESTAMP(CONVERT_TZ(startdate, '+00:00', @@global.time_zone)) as startdate, UNIX_TIMESTAMP(CONVERT_TZ(enddate, '+00:00', @@global.time_zone)) as enddate FROM {$wpdb->pmpro_membership_levels} AS l JOIN {$wpdb->pmpro_memberships_users} AS mu ON (l.id = mu.membership_id) WHERE mu.user_id = $user_id" . ( $include_inactive ? '' : " AND mu.status = 'active' GROUP BY ID" ) ); wp_cache_set( $cache_key, $levels, 'pmpro', 3600 ); } // Round off prices if ( ! empty( $levels ) ) { foreach( $levels as $key => $level ) { $levels[$key]->initial_payment = pmpro_round_price( $level->initial_payment ); $levels[$key]->billing_amount = pmpro_round_price( $level->billing_amount ); $levels[$key]->trial_amount = pmpro_round_price( $level->trial_amount ); } } /** * pmpro_get_membership_levels_for_user filter. * * Filters the returned levels. * * @since 1.8.5.4 * * @param array $levels Array of level objects. */ $levels = apply_filters( 'pmpro_get_membership_levels_for_user', $levels, $user_id ); return $levels; } /** * Get a specific membership level for a user if they have that level. * This is better to use when MMPU is enabled on the site. * * If $user_id is null, the value will be retrieved from $current_user. * * @param int $user_id User ID to check for * @param int $level_id Level ID to check for. * * @return false|object * Return values: * Success returns the level object. * Failure returns false. */ function pmpro_getSpecificMembershipLevelForUser( $user_id, $level_id ) { if ( empty( $user_id ) ) { global $current_user; $user_id = $current_user->ID; } if ( empty( $user_id ) || empty( $level_id ) ) { return false; } $all_levels = pmpro_getMembershipLevelsForUser( $user_id ); foreach ( $all_levels as $level ) { if ( $level->id == $level_id ) { return $level; } } return false; } /** * pmpro_getLevel() returns the level object for a level * * @param int|string|object $level may be the level id or name * * @return false|object * Return values: * Success returns the level object. * Failure returns false. */ function pmpro_getLevel( $level ) { global $pmpro_levels; if ( is_object( $level ) && ! empty( $level->id ) ) { $level = $level->id; } // was a name passed? (Todo: make sure level names have at least one non-numeric character. if ( is_numeric( $level ) ) { $level_id = intval( $level ); if ( isset( $pmpro_levels[ $level_id ] ) ) { return $pmpro_levels[ $level_id ]; } else { global $wpdb; $pmpro_levels[ $level_id ] = $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql( $level_id ) . "' LIMIT 1" ); } } else { global $wpdb; $level_obj = $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_membership_levels WHERE name = '" . esc_sql( $level ) . "' LIMIT 1" ); if ( ! empty( $level_obj ) ) { $level_id = $level_obj->id; } else { return false; } $pmpro_levels[ $level_id ] = $level_obj; } // Round prices if ( ! empty( $pmpro_levels[ $level_id ] ) ) { $pmpro_levels[ $level_id ]->initial_payment = pmpro_round_price( $pmpro_levels[ $level_id ]->initial_payment ); $pmpro_levels[ $level_id ]->billing_amount = pmpro_round_price( $pmpro_levels[ $level_id ]->billing_amount ); $pmpro_levels[ $level_id ]->trial_amount = pmpro_round_price( $pmpro_levels[ $level_id ]->trial_amount ); } return $pmpro_levels[ $level_id ]; } /** * Get all PMPro membership levels. * * @since 3.0 deprecated the second `$use_cache` argument. * * @param bool $include_hidden Include levels marked as hidden/inactive. * @param bool $deprecated_argument No longer used. * @param bool $force Resets the static var caches. */ function pmpro_getAllLevels( $include_hidden = false, $deprecated_argument = false, $force = false ) { // The global $pmpro_levels variable is deprecated and should no longer be used, but we'll set it for backwards compatibility. global $pmpro_levels, $wpdb; static $pmpro_all_levels; // every single level static $pmpro_visible_levels; // every single level that's not hidden if ( $force ) { $pmpro_levels = NULL; $pmpro_all_levels = NULL; $pmpro_visible_levels = NULL; } // If use_cache is true check if we have something in a static var. if ( $include_hidden && isset( $pmpro_all_levels ) ) { $pmpro_levels = $pmpro_all_levels; return $pmpro_all_levels; } if ( ! $include_hidden && isset( $pmpro_visible_levels ) ) { $pmpro_levels = $pmpro_visible_levels; return $pmpro_visible_levels; } // build query $sqlQuery = "SELECT * FROM $wpdb->pmpro_membership_levels "; if ( ! $include_hidden ) { $sqlQuery .= ' WHERE allow_signups = 1 ORDER BY id'; } // get levels from the DB $raw_levels = $wpdb->get_results( $sqlQuery ); // lets put them into an array where the key is the id of the level $pmpro_levels = array(); foreach ( $raw_levels as $raw_level ) { $raw_level->initial_payment = pmpro_round_price( $raw_level->initial_payment ); $raw_level->billing_amount = pmpro_round_price( $raw_level->billing_amount ); $raw_level->trial_amount = pmpro_round_price( $raw_level->trial_amount ); $pmpro_levels[ $raw_level->id ] = $raw_level; } // Store an extra cache specific to the include_hidden param. if ( $include_hidden ) { $pmpro_all_levels = $pmpro_levels; } else { $pmpro_visible_levels = $pmpro_levels; } return $pmpro_levels; } /** * Check if any level(s) are available for signup. * @return bool * @since 2.3 */ function pmpro_are_any_visible_levels() { $levels = pmpro_getAllLevels( false, true ); if ( ! empty( $levels ) ) { $r = true; } else { $r = false; } return $r; } /** * Get level at checkout and place into $pmpro_level global. * If no level is passed or found in the URL parameters, global vars, * or in the post options, then this will return the first level found. * * @param int $level_id (optional) Pass a level ID to force that level. * @param string $discount_code (optional) Pass a discount code to force that code * * @return mixed|void */ function pmpro_getLevelAtCheckout( $level_id = null, $discount_code = null ) { global $pmpro_level, $wpdb, $post; static $function_cache = array(); // Check if we have a cached value to use. $cache_key = md5( serialize( array( $level_id, $discount_code ) ) ); if ( array_key_exists( $cache_key, $function_cache ) ) { // Set the global and return the cached value. $pmpro_level = $function_cache[ $cache_key ]; return $pmpro_level; } // Reset $pmpro_level global. $pmpro_level = null; // Default to level passed in via URL. if ( empty( $level_id ) && ! empty( $_REQUEST['pmpro_level'] ) ) { $level_id = intval( $_REQUEST['pmpro_level'] ); } // If we don't have a level, check the legacy 'level' request parameter. if ( empty( $level_id ) && ! empty( $_REQUEST['level'] ) ) { // TODO: We may want to show a message here that the level parameter is deprecated. $level_id = intval( $_REQUEST['level'] ); } // If we still don't have a level yet, check for a default level in the custom fields for this post. if ( empty( $level_id ) && ! empty( $post ) ) { $level_id = intval( get_post_meta( $post->ID, 'pmpro_default_level', true ) ); } // If we still don't have a level, use the default level. if ( empty( $level_id ) ) { $all_levels = pmpro_getAllLevels( false, false ); if ( ! empty( $all_levels ) ) { // Get lowest level ID. $default_level = min( array_keys( $all_levels ) ); } else { $default_level = null; } $level_id = apply_filters( 'pmpro_default_level', intval( $default_level ) ); } // If we still don't have a level, bail. if ( empty( $level_id ) || intval( $level_id ) < 1 ) { return; } // default to discount code passed in via URL. if ( empty( $discount_code ) && ! empty( $_REQUEST['pmpro_discount_code'] ) ) { $discount_code = preg_replace( '/[^A-Za-z0-9\-]/', '', sanitize_text_field( $_REQUEST['pmpro_discount_code'] ) ); } // If we still don't have a discount code, check the legacy 'discount_code' request parameter. if ( empty( $discount_code ) && ! empty( $_REQUEST['discount_code'] ) ) { $discount_code = preg_replace( '/[^A-Za-z0-9\-]/', '', sanitize_text_field( $_REQUEST['discount_code'] ) ); } // If we still don't have a discount code, add a filter to let other plugins add one. if ( empty( $discount_code ) ) { $discount_code = apply_filters( 'pmpro_default_discount_code', null, $level_id ); } // If we are using a discount code, check it and get the level. if ( ! empty( $level_id ) && ! empty( $discount_code ) ) { $discount_code_id = $wpdb->get_var( "SELECT id FROM $wpdb->pmpro_discount_codes WHERE code = '" . esc_sql( $discount_code ) . "' LIMIT 1" ); // check code $code_check = pmpro_checkDiscountCode( $discount_code, $level_id, true ); if ( $code_check[0] != false ) { $sqlQuery = "SELECT l.id, cl.*, l.name, l.description, l.allow_signups, l.confirmation FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id LEFT JOIN $wpdb->pmpro_discount_codes dc ON dc.id = cl.code_id WHERE dc.code = '" . esc_sql( $discount_code ) . "' AND cl.level_id = '" . esc_sql( $level_id ) . "' LIMIT 1"; $pmpro_level = $wpdb->get_row( $sqlQuery ); // if the discount code doesn't adjust the level, let's just get the straight level if ( empty( $pmpro_level ) ) { $pmpro_level = $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql( $level_id ) . "' LIMIT 1" ); } // filter adjustments to the level $pmpro_level->code_id = $discount_code_id; $pmpro_level->discount_code = $discount_code; $pmpro_level = apply_filters( 'pmpro_discount_code_level', $pmpro_level, $discount_code_id ); } else { // error with discount code, we want to halt checkout pmpro_setMessage( $code_check[1], 'pmpro_error' ); } } // If we don't have a level object yet, pull it from the database. if ( empty( $pmpro_level ) && ! empty( $level_id ) ) { $pmpro_level = $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql( $level_id ) . "' AND allow_signups = 1 LIMIT 1" ); } // Filter the level (for upgrades, etc). $pmpro_level = apply_filters( 'pmpro_checkout_level', $pmpro_level ); // Cache the result for future use if this is a "top level" call to this function. if ( ! doing_filter( 'pmpro_checkout_level' ) ) { $function_cache[ $cache_key ] = $pmpro_level; } return $pmpro_level; } /** * Get an ordered list of level objects or level IDs. * * @param array $pmpro_levels An array of level objects or level IDs to be reordered. * @return array $pmpro_levels An ordered array of level objects or level IDs. * */ function pmpro_sort_levels_by_order( $pmpro_levels ) { $pmpro_level_order = get_option( 'pmpro_level_order' ); // No custom sort order, just return. if ( empty( $pmpro_level_order ) ) { return $pmpro_levels; } // Convert the level order option to an array. $sort_order = explode( ',',$pmpro_level_order ); // Reorder the array. $reordered_levels = array(); foreach ( $sort_order as $level_id ) { foreach ( $pmpro_levels as $key => $level ) { if ( ! empty ( $level->id ) && $level_id == $level->id ) { $reordered_levels[$level_id] = $pmpro_levels[$key]; } elseif ( ! empty( $level ) && is_string( $level ) && $level_id == $level ) { $reordered_levels[$level_id] = $pmpro_levels[$key]; } } } $pmpro_levels = $reordered_levels; return $pmpro_levels; } function pmpro_getCheckoutButton( $level_id, $button_text = null, $classes = null ) { if ( ! empty( $level_id ) ) { // get level $level = pmpro_getLevel( $level_id ); $level_id = intval( $level_id ); if( ! empty( $level ) ) { // default button text with name field for replacement if ( empty( $button_text ) ) { $button_text = esc_html__( 'Sign Up for !!name!! Now', 'paid-memberships-pro' ); } // replace vars - this will be escaped when outputting (see below). $replacements = array( '!!id!!' => $level->id, '!!name!!' => $level->name, '!!description!!' => $level->description, '!!confirmation!!' => $level->confirmation, '!!initial_payment!!' => pmpro_filter_price_for_text_field( $level->initial_payment ), '!!billing_amount!!' => pmpro_filter_price_for_text_field( $level->billing_amount ), '!!cycle_number!!' => $level->cycle_number, '!!cycle_period!!' => $level->cycle_period, '!!billing_limit!!' => $level->billing_limit, '!!trial_amount!!' => pmpro_filter_price_for_text_field( $level->trial_amount ), '!!trial_limit!!' => $level->trial_limit, '!!expiration_number!!' => $level->expiration_number, '!!expiration_period!!' => $level->expiration_period, ); $button_text = str_replace( array_keys( $replacements ), $replacements, $button_text ); } } if ( empty( $button_text ) ) { $button_text = esc_html__( 'Sign Up Now', 'paid-memberships-pro' ); } if ( empty( $classes ) ) { $classes = 'pmpro_btn'; } if ( ! empty( $level_id ) ) { $r = '' . wp_kses_post( $button_text ) . ''; } else { $r = '' . wp_kses_post( $button_text ) . ''; } return $r; } /** * Get the "domain" from a URL. By domain, we mean the host name, minus any subdomains. So just the domain and TLD. * * @param string $url The URL to parse. (generally pass site_url() in WP) * @return string The domain. */ function pmpro_getDomainFromURL( $url = null ) { $domainparts = parse_url( $url ); $domainparts = explode( '.', $domainparts['host'] ); if ( count( $domainparts ) > 1 ) { // check for ips $isip = true; foreach ( $domainparts as $part ) { if ( ! is_numeric( $part ) ) { $isip = false; break; } } if ( $isip ) { // ip, e.g. 127.1.1.1 $domain = implode( '.', $domainparts ); } else { // www.something.com, etc. $domain = $domainparts[ count( $domainparts ) - 2 ] . '.' . $domainparts[ count( $domainparts ) - 1 ]; } } else { // localhost or another single word domain $domain = $domainparts[0]; } return $domain; } if ( ! function_exists( 'pmpro_getMemberStartdate' ) ) { /** * Get a member's start date... either in general or for a specific level_id. */ function pmpro_getMemberStartdate( $user_id = null, $level_id = 0 ) { if ( empty( $user_id ) ) { global $current_user; $user_id = $current_user->ID; } // make sure user and level id are int for security $user_id = intval( $user_id ); $level_id = intval( $level_id ); global $pmpro_startdates; // for cache if ( empty( $pmpro_startdates[ $user_id ][ $level_id ] ) ) { global $wpdb; if ( ! empty( $level_id ) ) { $sqlQuery = "SELECT UNIX_TIMESTAMP(CONVERT_TZ(startdate, '+00:00', @@global.time_zone)) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND membership_id IN(" . (int) $level_id . ") AND user_id = '" . $user_id . "' ORDER BY id LIMIT 1"; } else { $sqlQuery = "SELECT UNIX_TIMESTAMP(CONVERT_TZ(startdate, '+00:00', @@global.time_zone)) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND user_id = '" . esc_sql( $user_id ) . "' ORDER BY id LIMIT 1"; } $startdate = apply_filters( 'pmpro_member_startdate', $wpdb->get_var( $sqlQuery ), $user_id, $level_id ); $pmpro_startdates[ $user_id ][ $level_id ] = $startdate; } return $pmpro_startdates[ $user_id ][ $level_id ]; } } if ( ! function_exists( 'pmpro_getMemberDays' ) ) { /** * How long has this member been a member. */ function pmpro_getMemberDays( $user_id = null, $level_id = 0 ) { if ( empty( $user_id ) ) { global $current_user; $user_id = $current_user->ID; } global $pmpro_member_days; if ( empty( $pmpro_member_days[ $user_id ][ $level_id ] ) ) { $startdate = pmpro_getMemberStartdate( $user_id, $level_id ); // check that there was a startdate at all if ( empty( $startdate ) ) { $pmpro_member_days[ $user_id ][ $level_id ] = 0; } else { $now = current_time( 'timestamp' ); $days = ( $now - $startdate ) / 3600 / 24; $pmpro_member_days[ $user_id ][ $level_id ] = $days; } } return $pmpro_member_days[ $user_id ][ $level_id ]; } } // the start of a message handling script function pmpro_setMessage( $message, $type, $force = false ) { global $pmpro_msg, $pmpro_msgt; // for now, we only show the first message generated if ( $force || empty( $pmpro_msg ) ) { $pmpro_msg = apply_filters( 'pmpro_set_message', $message, $type ); $pmpro_msgt = $type; } } /** * Show a PMPro message set via pmpro_setMessage * * @since 1.8.5 */ function pmpro_showMessage() { global $pmpro_msg, $pmpro_msgt; $allowed_html = array ( 'a' => array ( 'href' => array(), 'target' => array(), 'title' => array(), ), 'em' => array(), 'p' => array(), 'span' => array( 'class' => array(), ), 'strong' => array(), 'ul' => array(), 'li' => array(), ); if ( ! empty( $pmpro_msg ) ) { ?> get_var( "SELECT id FROM $wpdb->pmpro_membership_levels LIMIT 1" ); // check if the gateway settings are good. first check if it's needed (is there paid membership level) $paid_membership_level = $wpdb->get_var( "SELECT id FROM $wpdb->pmpro_membership_levels WHERE allow_signups = 1 AND (initial_payment > 0 OR billing_amount > 0 OR trial_amount > 0) LIMIT 1" ); $paid_user_subscription = $wpdb->get_var( "SELECT user_id FROM $wpdb->pmpro_memberships_users WHERE initial_payment > 0 OR billing_amount > 0 OR trial_amount > 0 LIMIT 1" ); if ( empty( $paid_membership_level ) && empty( $paid_user_subscription ) ) { // no paid membership level now or attached to a user. we don't need the gateway setup $pmpro_gateway_ready = true; } else { $gateway = get_option( 'pmpro_gateway' ); $gateway_environment = get_option( 'pmpro_gateway_environment' ); if ( $gateway == 'authorizenet' ) { if ( $gateway_environment && get_option( 'pmpro_loginname' ) && get_option( 'pmpro_transactionkey' ) ) { $pmpro_gateway_ready = true; } else { $pmpro_gateway_ready = false; } } elseif ( $gateway == 'paypal' || $gateway == 'paypalexpress' ) { if ( $gateway_environment && get_option( 'pmpro_gateway_email' ) && get_option( 'pmpro_apiusername' ) && get_option( 'pmpro_apipassword' ) && get_option( 'pmpro_apisignature' ) ) { $pmpro_gateway_ready = true; } else { $pmpro_gateway_ready = false; } } elseif ( $gateway == 'paypalstandard' ) { if ( $gateway_environment && get_option( 'pmpro_gateway_email' ) ) { $pmpro_gateway_ready = true; } else { $pmpro_gateway_ready = false; } } elseif ( $gateway == 'payflowpro' ) { if ( get_option( 'pmpro_payflow_partner' ) && get_option( 'pmpro_payflow_vendor' ) && get_option( 'pmpro_payflow_user' ) && get_option( 'pmpro_payflow_pwd' ) ) { $pmpro_gateway_ready = true; } else { $pmpro_gateway_ready = false; } } elseif ( $gateway == 'stripe' ) { if ( $gateway_environment && get_option( 'pmpro_stripe_secretkey' ) && get_option( 'pmpro_stripe_publishablekey' ) ) { // Using legacy keys. $pmpro_gateway_ready = true; } elseif ( $gateway_environment && get_option( 'pmpro_' . $gateway_environment . '_stripe_connect_secretkey' ) && get_option( 'pmpro_' . $gateway_environment . '_stripe_connect_publishablekey' ) ) { // Using connect. $pmpro_gateway_ready = true; } else { $pmpro_gateway_ready = false; } } elseif ( $gateway == 'braintree' ) { if ( $gateway_environment && get_option( 'pmpro_braintree_merchantid' ) && get_option( 'pmpro_braintree_publickey' ) && get_option( 'pmpro_braintree_privatekey' ) ) { $pmpro_gateway_ready = true; } else { $pmpro_gateway_ready = false; } } elseif ( $gateway == 'twocheckout' ) { if ( $gateway_environment && get_option( 'pmpro_twocheckout_apiusername' ) && get_option( 'pmpro_twocheckout_apipassword' ) ) { $pmpro_gateway_ready = true; } else { $pmpro_gateway_ready = false; } } elseif ( $gateway == 'cybersource' ) { if ( $gateway_environment && get_option( 'pmpro_cybersource_merchantid' ) && get_option( 'pmpro_cybersource_securitykey' ) ) { $pmpro_gateway_ready = true; } else { $pmpro_gateway_ready = false; } } elseif ( $gateway == 'check' ) { $pmpro_gateway_ready = true; } else { $pmpro_gateway_ready = false; } } // check if we have all pages if ( $pmpro_pages['account'] && $pmpro_pages['billing'] && $pmpro_pages['cancel'] && $pmpro_pages['checkout'] && $pmpro_pages['confirmation'] && $pmpro_pages['invoice'] && $pmpro_pages['levels'] ) { $pmpro_pages_ready = true; } else { $pmpro_pages_ready = false; } // now check both if ( $pmpro_gateway_ready && $pmpro_pages_ready ) { $r = true; } else { $r = false; } /** * Filter to determine if PMPro setup is complete or * if notices or warnings need to be shown in the PMPro settings. * * Note: The filter should return true or false and also set * the $pmpro_level_ready, $pmpro_gateway_ready, $pmpro_pages_ready global variabls. * * @since 1.8.4.5 * * @param bool $r ready? */ $r = apply_filters( 'pmpro_is_ready', $r ); return $r; } /** * Display the Setup Wizard links. * * @since 2.10 * * @return bool $show Whether or not the Setup Wizard link should show. */ function pmpro_show_setup_wizard_link() { global $pmpro_ready; // If PMPro isn't ready AND the wizard hasn't completed yet. if ( ! $pmpro_ready && get_option( 'pmpro_wizard_step' ) !== 'done' ) { $show = true; } else { $show = false; } /** * Filter to determine if the Setup Wizard link should show. Allows you to bypass whether or not to show the link. */ return apply_filters( 'pmpro_show_setup_wizard_link', $show ); } /** * Display Invoice Price Data with Parts * * @param object $pmpro_invoice The full order object. * @param string $format Format of the return value. Accepts array, span, list, or line_breaks. * * @return array|string $price_parts The array or formatted HTML string to display price parts and total. * */ function pmpro_get_price_parts( $pmpro_invoice, $format = 'array' ) { $pmpro_price_parts = array(); if ( ! empty( $pmpro_invoice->subtotal ) && $pmpro_invoice->subtotal != $pmpro_invoice->total ) { $pmpro_price_parts['subtotal'] = array( 'label' => __( 'Subtotal', 'paid-memberships-pro' ), 'value' => pmpro_escape_price( pmpro_formatPrice( $pmpro_invoice->subtotal ) ), ); } if ( ! empty( $pmpro_invoice->tax ) ) { $pmpro_price_parts['tax'] = array( 'label' => __( 'Tax', 'paid-memberships-pro' ), 'value' => pmpro_escape_price( pmpro_formatPrice( $pmpro_invoice->tax ) ), ); } /** * Filter to modify the price parts, add parts, or modify the display. Does not include the order total. * * @param array $pmpro_price_parts The array of price parts not including the total. * @param string $format Format of the return value passed to the function. * @param object $pmpro_invoice The full order object. * * @return array $pmpro_price_parts Filtered array of price parts not including the total. * */ $pmpro_price_parts = apply_filters( 'pmpro_get_price_parts', $pmpro_price_parts, $pmpro_invoice ); $pmpro_price_parts_with_total = $pmpro_price_parts; if ( ! empty( $pmpro_invoice->total ) ) { $pmpro_price_parts_with_total['total'] = array( 'label' => __( 'Total', 'paid-memberships-pro' ), 'value' => pmpro_escape_price( pmpro_formatPrice( $pmpro_invoice->total ) ), ); } /** * Filter including the total price to modify the price parts, add parts, or modify the display. * * @param array $pmpro_price_parts The array of price parts including the total. * @param string $format Format of the return value passed to the function. * @param object $pmpro_invoice The full order object. * * @return array $pmpro_price_parts Filtered array of price parts not including the total. * */ $pmpro_price_parts_with_total = apply_filters( 'pmpro_get_price_parts_with_total', $pmpro_price_parts_with_total, $pmpro_invoice ); if ( $format == 'array' ) { return $pmpro_price_parts_with_total; } else { // Start building our formatted return string. $pmpro_price = ''; if ( $format == 'span' ) { foreach ( $pmpro_price_parts_with_total as $key => $pmpro_price_part ) { $pmpro_price .= '' . esc_html( $pmpro_price_part['label'] ) . ' ' . esc_html( $pmpro_price_part['value'] ) . ''; } } elseif ( $format == 'list' ) { $pmpro_price .= '