LLMS_Access_Plan
LLMS_Access_Plan Model.
Description Description
Source Source
File: includes/models/model.llms.access.plan.php
class LLMS_Access_Plan extends LLMS_Post_Model { /** * Map of meta properties => type. * * @var array */ protected $properties = array( 'access_expiration' => 'string', 'access_expires' => 'string', 'access_length' => 'absint', 'access_period' => 'string', 'availability' => 'string', 'availability_restrictions' => 'array', 'content' => 'html', 'checkout_redirect_forced' => 'yesno', 'checkout_redirect_type' => 'string', 'checkout_redirect_page' => 'absint', 'checkout_redirect_url' => 'string', 'enroll_text' => 'string', 'frequency' => 'absint', 'is_free' => 'yesno', 'length' => 'absint', 'menu_order' => 'absint', 'on_sale' => 'yesno', 'period' => 'string', 'price' => 'float', 'product_id' => 'absint', 'sale_end' => 'string', 'sale_start' => 'string', 'sale_price' => 'float', 'sku' => 'string', 'title' => 'string', 'trial_length' => 'absint', 'trial_offer' => 'yesno', 'trial_period' => 'string', 'trial_price' => 'float', ); /** * Post Type name * * @var string */ protected $db_post_type = 'llms_access_plan'; /** * Name of the model. * * @var string */ protected $model_post_type = 'access_plan'; /** * Determine if the access plan has expiration settings * * @since 3.0.0 * @version 3.0.0 * @return boolean true if it can expire, false if it's for lifetime access */ public function can_expire() { return ( 'lifetime' !== $this->get( 'access_expiration' ) ); } /** * Calculate redirection url from settings * * @param string The redirection type: self, page or url. * @return string * @since 3.30.0 * @version 3.30.0 */ private function calculate_redirection_url( $redirect_type ) { $available = $this->is_available_to_user( get_current_user_id() ); if ( ! $available && 'no' === $this->get( 'checkout_redirect_forced' ) ) { $redirect_type = 'membership'; } // by default, no special redirection is needed. $redirection = ''; switch ( $redirect_type ) { // redirect to itself. case 'self': /** * Only set up when it is a member's only access plan with forced redirection to course. * This will ensure that on a regular access plan, no special parameter is added to querystring. * At the same time, if it is a members' only access plan, * after membership checkout we'd like to force redirect to course */ if ( ! $available && llms_parse_bool( $this->get( 'checkout_redirect_forced' ) ) ) { $redirection = get_permalink( $this->get( 'product_id' ) ); } break; case 'page': $redirection = get_permalink( $this->get( 'checkout_redirect_page' ) ); break; case 'url': $redirection = $this->get( 'checkout_redirect_url' ); break; } return $redirection; } /** * Get the translated and pluralized name of the plan's access period * * @since 3.4.6 * @version 3.23.0 * * @param string $period (optional) untranslated access period, if not supplied uses stored value for the plan. * @param int $length (optional) access length (for pluralization), if not supplied uses stored value for the plan. * @return string */ public function get_access_period_name( $period = null, $length = null ) { $period = $period ? $period : $this->get( 'access_period' ); $length = $length ? $length : $this->get( 'access_length' ); switch ( $period ) { case 'year': $period = _nx( 'year', 'years', $length, 'Access plan period', 'lifterlms' ); break; case 'month': $period = _nx( 'month', 'months', $length, 'Access plan period', 'lifterlms' ); break; case 'week': $period = _nx( 'week', 'weeks', $length, 'Access plan period', 'lifterlms' ); break; case 'day': $period = _nx( 'day', 'days', $length, 'Access plan period', 'lifterlms' ); break; } /** * Filter the translated name of an access plan's billing period. * * @since 3.4.6 * @version 3.4.6 * * @param string $period Translated period name. * @param int $length Access length, used for pluralization. * @param LLMS_Access_Plan $this Access plan instance. */ return apply_filters( 'llms_plan_get_access_period_name', $period, $length, $this ); } /** * Default arguments for creating a new post * * @since 3.0.0 * @version 3.0.0 * * @param string $title Title to create the post with * @return array */ protected function get_creation_args( $title = '' ) { return array_merge( parent::get_creation_args( $title ), array( 'post_status' => 'publish', ) ); } /** * Retrieve the full URL to redirect to after successful checkout * * @since 3.30.0 * @version 3.30.0 * * @return string */ public function get_redirection_url() { // what type of redirection is set up by user? $redirect_type = $this->get( 'checkout_redirect_type' ); $query_redirection = llms_filter_input( INPUT_GET, 'redirect', FILTER_VALIDATE_URL ); // force redirect querystring parameter over all else. $redirection = ! empty( $query_redirection ) ? $query_redirection : $this->calculate_redirection_url( $redirect_type ); /** * Filter the checkout redirection parameter * * @since 3.30.0 * @version 3.30.0 * * @param string $redirection The calculated url to redirect to. * @param string $redirection_type Available redirection types 'self', 'membership', 'page', 'url' or a custom type. * @param LLMS_Acccess_Plan $this Current Access Plan object. */ return urlencode( apply_filters( 'llms_plan_get_checkout_redirection', $redirection, $redirect_type, $this ) ); } /** * Retrieve the full URL to the checkout screen for the plan * * @since 3.0.0 * @since 3.30.0 Added access plan redirection settings. * @since 3.31.0 The `$check_availability` parameter was added to the filter `llms_plan_get_checkout_url` * * @param bool $check_availability determine if availability checks should be made (allows retrieving plans on admin panel). * @return string */ public function get_checkout_url( $check_availability = true ) { $ret = '#llms-plan-locked'; $available = $this->is_available_to_user( get_current_user_id() ); $redirection = $this->get_redirection_url(); // if bypassing availability checks OR plan is available to user. if ( ! $check_availability || $available ) { $ret_params = array( 'plan' => $this->get( 'id' ), ); if ( ! empty( $redirection ) ) { $ret_params['redirect'] = $redirection; } $ret = llms_get_page_url( 'checkout', $ret_params ); // not available to user -- this is a member's only plan. } elseif ( ! $available ) { $memberships = $this->get_array( 'availability_restrictions' ); // if there's only 1 plan associated with the membership return that url. if ( 1 === count( $memberships ) ) { $ret = get_permalink( $memberships[0] ); if ( ! empty( $redirection ) ) { $ret = add_query_arg( array( 'redirect' => $redirection, ), $ret ); } } } /** * Filter the checkout URL for an access plan. * * @since Unknown * @since 3.31.0 The `$check_availability` parameter was added. * * @param string $ret The checkout URL. * @param LLMS_Access_Plan $this Access plan object. * @param bool $check_availability Determine if availability checks should be made. * (allows retrieving plans on admin panel) */ return apply_filters( 'llms_plan_get_checkout_url', $ret, $this, $check_availability ); } /** * Get the initial price due on checkout. * * Automatically accounts for Trials, sales, and coupon discounts. * * @since 3.30.1 * @version 3.30.1 * * @param array $price_args Arguments passed to the price getter function to generate the price. * @param int|null $coupond_id LLMS_Coupon ID or `null` if no coupon is being used. * @param string $format Format the price to be returned. Options: html, raw, float (default). * @return mixed */ public function get_initial_price( $price_args = array(), $coupon_id = null, $format = 'float' ) { // If it's free it's a bit simpler. if ( $this->is_free() ) { $ret = $this->get_free_pricing_text( $format ); } else { // pricing function to use based on presence of coupon $func = $coupon_id ? 'get_price_with_coupon' : 'get_price'; // build args to pass to the function $args = array( $price_args, $format ); // coupons have an extra arg (coupon id) if ( $coupon_id ) { array_unshift( $args, $coupon_id ); } // setup the price key name based on the presence of a trial or sale if ( $this->has_trial() ) { array_unshift( $args, 'trial_price' ); } elseif ( $this->is_on_sale() ) { array_unshift( $args, 'sale_price' ); } else { array_unshift( $args, 'price' ); } // retrieve the price. $ret = call_user_func_array( array( $this, $func ), $args ); } /** * Filter an access plan's initial price due on checkout. * * @since 3.30.1 * * @param mixed $ret Price due on checkout. * @param array $price_args Arguments passed to the price getter function to generate the price. * @param int|null $coupond_id LLMS_Coupon ID or `null` if no coupon is being used. * @param string $format Format the price to be returned. Options: html, raw, float (default). * @param LLMS_Access_Plan $this Access Plan object. */ return apply_filters( 'llms_access_plan_get_initial_price', $ret, $price_args, $coupon_id, $format, $this ); } /** * Get a string to use for 0 dollar amount prices rather than 0 * @param string $format format to display the price in * @return string * @since 3.0.0 * @version 3.0.0 */ public function get_free_pricing_text( $format = 'html' ) { $text = __( 'FREE', 'lifterlms' ); if ( 'html' === $format ) { $text = '<span class="lifterlms-price">' . $text . '</span>'; } elseif ( 'float' === $format ) { $text = 0.00; } /** * Filter the text displayed when a plan has no price. * * @since 3.0.0 * @version 3.0.0 * * @param string $text Displayed text. * @param LLMS_Access_Plan $this The access plan instance. */ return apply_filters( 'llms_get_free_' . $this->model_post_type . '_pricing_text', $text, $this ); } /** * Getter for price strings with optional formatting options * @param string $key property key * @param array $price_args optional array of arguments that can be passed to llms_price() * @param string $format optional format conversion method [html|raw|float] * @return mixed * @since 3.0.0 * @version 3.23.0 */ public function get_price( $key, $price_args = array(), $format = 'html' ) { $price = $this->get( $key ); if ( $price <= 0 ) { $ret = $this->get_free_pricing_text( $format ); } else { $ret = parent::get_price( $key, $price_args, $format ); } return apply_filters( 'llms_plan_get_price', $ret, $key, $price_args, $format, $this ); } /** * Apply a coupon to a price * @param string $key price to retrieve, "price", "sale_price", or "trial_price" * @param int $coupon_id LifterLMS Coupon Post ID * @param array $price_args optional arguments to be passed to llms_price() * @param string $format optional return format as passed to llms_price() * @return mixed * @since 3.0.0 * @version 3.7.0 */ public function get_price_with_coupon( $key, $coupon_id, $price_args = array(), $format = 'html' ) { // allow id or instance to be passed for $coupon_id if ( $coupon_id instanceof LLMS_Coupon ) { $coupon = $coupon_id; } else { $coupon = new LLMS_Coupon( $coupon_id ); } $price = $this->get( $key ); // ensure the coupon *can* be applied to this plan if ( ! $coupon->is_valid( $this ) ) { return $price; } $discount_type = $coupon->get( 'discount_type' ); // price and sale price are calculated of coupon amount if ( 'price' === $key || 'sale_price' === $key ) { $coupon_amount = $coupon->get( 'coupon_amount' ); } elseif ( 'trial_price' === $key && $coupon->has_trial_discount() && $this->has_trial() ) { $coupon_amount = $coupon->get( 'trial_amount' ); } else { $coupon_amount = 0; } if ( $coupon_amount ) { // simple subtraction if ( 'dollar' === $discount_type ) { $price = $price - $coupon_amount; } // End if(). elseif ( 'percent' === $discount_type ) { $price = $price - ( $price * ( $coupon_amount / 100 ) ); } } // if price is less than 0 return the pricing text if ( $price <= 0 ) { $price = $this->get_free_pricing_text( $format ); } else { if ( 'html' == $format || 'raw' === $format ) { $price = llms_price( $price, $price_args ); if ( 'raw' === $format ) { $price = strip_tags( $price ); } } elseif ( 'float' === $format ) { $price = floatval( number_format( $price, get_lifterlms_decimals(), '.', '' ) ); } else { $price = apply_filters( 'llms_get_' . $this->model_post_type . '_' . $key . '_' . $format . '_with_coupon', $price, $key, $price_args, $format, $this ); } } return apply_filters( 'llms_get_' . $this->model_post_type . '_' . $key . '_price_with_coupon', $price, $key, $price_args, $format, $this ); } /** * Retrieve an instance of the associated LLMS_Product * @return obj * @since 3.0.0 * @version 3.0.0 */ public function get_product() { return new LLMS_Product( $this->get( 'product_id' ) ); } /** * Retrieve the product type (course or membership) for the associated product * @return string * @since 3.0.0 * @version 3.0.0 */ public function get_product_type() { $product = $this->get_product(); return str_replace( 'llms_', '', $product->get( 'type' ) ); } /** * Retrieve the text displayed on "Buy" buttons * Uses optional user submitted text and falls back to LifterLMS defaults if none is supplied * @return string * @since 3.0.0 * @version 3.23.0 */ public function get_enroll_text() { // user custom text option $text = $this->get( 'enroll_text' ); if ( ! $text ) { switch ( $this->get_product_type() ) { case 'course': $text = apply_filters( 'llms_course_enroll_button_text', __( 'Enroll', 'lifterlms' ), $this ); break; case 'membership': $text = apply_filters( 'llms_membership_enroll_button_text', __( 'Join', 'lifterlms' ), $this ); break; } } return apply_filters( 'llms_plan_get_enroll_text', $text, $this ); } /** * Get a sentence explaining plan expiration details * @return string * @since 3.0.0 * @version 3.28.2 */ public function get_expiration_details() { $ret = ''; $expiration = $this->get( 'access_expiration' ); if ( 'limited-date' === $expiration ) { $ret = sprintf( _x( 'access until %s', 'Access expiration date', 'lifterlms' ), $this->get_date( 'access_expires' ) ); } elseif ( 'limited-period' === $expiration ) { $ret = sprintf( _x( '%1$d %2$s of access', 'Access period description', 'lifterlms' ), $this->get( 'access_length' ), $this->get_access_period_name() ); } return apply_filters( 'llms_get_product_expiration_details', $ret, $this ); } /** * Get a sentence explaining the plan's payment schedule * @return string * @since 3.0.0 * @version 3.23.0 */ public function get_schedule_details() { $ret = ''; $period = $this->get( 'period' ); $frequency = $this->get( 'frequency' ); $length = $this->get( 'length' ); // one-time payments don't display anything here unless filtered if ( $frequency > 0 ) { if ( 1 === $frequency ) { $ret = sprintf( _x( 'per %s', 'subscription schedule', 'lifterlms' ), $this->get_access_period_name( $period, $frequency ) ); } else { $ret = sprintf( _x( 'every %1$d %2$s', 'subscription schedule', 'lifterlms' ), $frequency, $this->get_access_period_name( $period, $frequency ) ); } // add length sentence if applicable if ( $length > 0 ) { $ret .= ' ' . sprintf( _x( 'for %1$d total payments', 'subscription # of payments', 'lifterlms' ), $length ); } } return apply_filters( 'llms_get_product_schedule_details', sprintf( $ret, $this->get( 'period' ), $frequency, $length ), $this ); } /** * Get a sentence explaining the plan's trial offer * @return string * @since 3.0.0 * @version 3.4.8 */ public function get_trial_details() { $details = ''; if ( $this->has_trial() ) { $length = $this->get( 'trial_length' ); $period = $this->get( 'trial_period' ); $details = sprintf( _x( 'for %1$d %2$s', 'trial offer description', 'lifterlms' ), $length, $this->get_access_period_name( $period, $length ) ); } return apply_filters( 'llms_get_product_trial_details', $details, $this ); } /** * Get the access plans visibility setting * @return string * @since 3.8.0 * @version 3.8.0 */ public function get_visibility() { $term = $this->get_terms( 'llms_access_plan_visibility', true ); $ret = ( $term && $term->name ) ? $term->name : 'visible'; return apply_filters( 'llms_get_access_plan_visibility', $ret, $this ); } /** * Determine if the plan has availability restrictions * Related product must be a COURSE * Availability must be set to "members" and at least one membership must be selected * @return boolean * @since 3.0.0 * @version 3.0.0 */ public function has_availability_restrictions() { return ( 'course' === $this->get_product_type() && 'members' === $this->get( 'availability' ) && $this->get_array( 'availability_restrictions' ) ); } /** * Determine if the free checkout process & interface should be used for this access plan * @return boolean * @since 3.4.0 * @version 3.4.0 */ public function has_free_checkout() { return ( $this->is_free() && apply_filters( 'llms_has_free_checkout', true, $this ) ); } /** * Determine if the plan has a trial offer * One-time payments can't have a trial, so the plan must have a frequency greater than 0 * @return boolean * @since 3.0.0 * @version 3.23.0 */ public function has_trial() { $ret = false; if ( $this->get( 'frequency' ) > 0 ) { $ret = llms_parse_bool( $this->get( 'trial_offer' ) ); } return apply_filters( 'llms_plan_has_trial', $ret, $this ); } /** * Determine if the plan is available to a user based on configured availability restrictions * @param int $user_id (optional) WP User ID, if not supplied get_current_user_id() will be used * @return boolean * @since 3.4.4 * @version 3.23.0 */ public function is_available_to_user( $user_id = null ) { $user_id = empty( $user_id ) ? get_current_user_id() : $user_id; $access = true; // if there are membership restrictions, check the user is in at least one membership if ( $this->has_availability_restrictions() ) { $access = false; foreach ( $this->get_array( 'availability_restrictions' ) as $mid ) { // once we find a membership, exit if ( llms_is_user_enrolled( $user_id, $mid ) ) { $access = true; break; } } } return apply_filters( 'llms_plan_is_available_to_user', $access, $user_id, $this ); } /** * Determine if the plan is marked as "featured" * @return boolean * @since 3.0.0 * @version 3.8.0 */ public function is_featured() { return ( 'featured' === $this->get_visibility() ); } /** * Determines if a plan is marked ar free * This only returns the value of the setting and should not * be used to check if payment is required (when using a coupon for example) * @return boolean * @since 3.0.0 * @version 3.23.0 */ public function is_free() { return llms_parse_bool( $this->get( 'is_free' ) ); } /** * Determine if a plan is *currently* on sale * @return boolean * @since 3.0.0 * @version 3.24.3 */ public function is_on_sale() { $ret = false; if ( llms_parse_bool( $this->get( 'on_sale' ) ) ) { $now = llms_current_time( 'timestamp' ); $start = $this->get( 'sale_start' ); $end = $this->get( 'sale_end' ); // add times if the values exist (start of day & end of day) $start = ( $start ) ? strtotime( $start . ' 00:00:00' ) : $start; $end = ( $end ) ? strtotime( '+1 day', strtotime( $end . ' 00:00:00' ) ) : $end; // no dates, the product is indefinitely on sale if ( ! $start && ! $end ) { $ret = true; // start and end } elseif ( $start && $end ) { $ret = ( $now < $end && $now > $start ); // only start } elseif ( $start && ! $end ) { $ret = ( $now > $start ); // only end } elseif ( ! $start && $end ) { $ret = ( $now < $end ); } } return apply_filters( 'llms_plan_is_on_sale', $ret, $this ); } /** * Determine if the plan is visible * Both featured and visible access plans are considered visible * @return boolean * @since 3.8.0 * @version 3.8.0 */ public function is_visible() { return ( 'hidden' !== $this->get_visibility() ); } /** * Determine if the Access Plan has recurring payments * @return boolean true if it is recurring, false otherwise * @since 3.0.0 * @version 3.0.0 */ public function is_recurring() { return ( 0 !== $this->get( 'frequency' ) ); } /** * Determine if the access plan requires payment. * * Automatically accounts for coupons, sales, trials, and whether the plan is marked as free. * * @since 3.0.0 * @since 3.30.1 Uses self::get_initial_price(). * * @param int $coupon_id LLMS_Coupon ID. * @return bool true if payment required, false otherwise */ public function requires_payment( $coupon_id = null ) { $ret = ( $this->get_initial_price( array(), $coupon_id, 'float' ) > 0 ); return apply_filters( 'llms_plan_requires_payment', $ret, $coupon_id, $this ); } /** * Update the visibility term for the access plan * @param string $visibility access plan name * @since 3.8.0 * @version 3.8.0 */ public function set_visibility( $visibility ) { return $this->set_terms( array( $visibility ), 'llms_access_plan_visibility', false ); } /** * Cleanup data to remove unnecessary defaults * @param array $arr array of data to be serialized * @return array * @since 3.16.11 * @version 3.16.11 */ protected function toArrayAfter( $arr ) { unset( $arr['author'] ); return $arr; } /** * Don't add custom fields during toArray() * @param array $arr post model array * @return array * @since 3.16.11 * @version 3.16.11 */ protected function toArrayCustom( $arr ) { return $arr; } }
Expand full source code Collapse full source code View on GitHub
Changelog Changelog
Version | Description |
---|---|
3.31.0 | The $check_availability parameter was added to the llms_plan_get_checkout_url filter. |
3.30.1 | Added method to get the initial price due on checkout. |
3.30.0 | Added checkout redirect properties and methods |
3.0.0 | Introduced. |
Methods Methods
- calculate_redirection_url — Calculate redirection url from settings
- can_expire — Determine if the access plan has expiration settings
- get_access_period_name — Get the translated and pluralized name of the plan's access period
- get_checkout_url — Retrieve the full URL to the checkout screen for the plan
- get_creation_args — Default arguments for creating a new post
- get_enroll_text — Retrieve the text displayed on "Buy" buttons Uses optional user submitted text and falls back to LifterLMS defaults if none is supplied
- get_expiration_details — Get a sentence explaining plan expiration details
- get_free_pricing_text — Get a string to use for 0 dollar amount prices rather than 0
- get_initial_price — Get the initial price due on checkout.
- get_price — Getter for price strings with optional formatting options
- get_price_with_coupon — Apply a coupon to a price
- get_product — Retrieve an instance of the associated LLMS_Product
- get_product_type — Retrieve the product type (course or membership) for the associated product
- get_redirection_url — Retrieve the full URL to redirect to after successful checkout
- get_schedule_details — Get a sentence explaining the plan's payment schedule
- get_trial_details — Get a sentence explaining the plan's trial offer
- get_visibility — Get the access plans visibility setting
- has_availability_restrictions — Determine if the plan has availability restrictions Related product must be a COURSE Availability must be set to "members" and at least one membership must be selected
- has_free_checkout — Determine if the free checkout process & interface should be used for this access plan
- has_trial — Determine if the plan has a trial offer One-time payments can't have a trial, so the plan must have a frequency greater than 0
- is_available_to_user — Determine if the plan is available to a user based on configured availability restrictions
- is_featured — Determine if the plan is marked as "featured"
- is_free — Determines if a plan is marked ar free This only returns the value of the setting and should not be used to check if payment is required (when using a coupon for example)
- is_on_sale — Determine if a plan is *currently* on sale
- is_recurring — Determine if the Access Plan has recurring payments
- is_visible — Determine if the plan is visible Both featured and visible access plans are considered visible
- requires_payment — Determine if the access plan requires payment.
- set_visibility — Update the visibility term for the access plan
- toArrayAfter — Cleanup data to remove unnecessary defaults
- toArrayCustom — Don't add custom fields during toArray()
User Contributed Notes User Contributed Notes
Permalink: