__( 'Pay per post', 'postfee' ), 'package' => __( 'Post package', 'postfee' ), 'subscription' => __( 'Subscription', 'postfee' ), 'upgrade' => __( 'Upgrade for an existing post', 'postfee' ), ); } /** * How long after a post goes live deleting it gives the post back, as * key => array( label, seconds ). Null seconds means without limit. */ public static function return_windows() { return array( 'never' => array( __( 'Never', 'postfee' ), 0 ), 'day' => array( __( 'For a day after it goes live', 'postfee' ), DAY_IN_SECONDS ), 'week' => array( __( 'For a week after it goes live', 'postfee' ), WEEK_IN_SECONDS ), 'month' => array( __( 'For a month after it goes live', 'postfee' ), MONTH_IN_SECONDS ), 'year' => array( __( 'For a year after it goes live', 'postfee' ), YEAR_IN_SECONDS ), 'forever' => array( __( 'Always', 'postfee' ), null ), ); } /** * Units a length of time can be entered in, as key => array( label, days ). * Lengths are stored and counted in days. */ public static function units() { return array( 'day' => array( __( 'Days', 'postfee' ), 1 ), 'week' => array( __( 'Weeks', 'postfee' ), 7 ), 'month' => array( __( 'Months', 'postfee' ), 30 ), 'year' => array( __( 'Years', 'postfee' ), 365 ), ); } /** * One of a plan's lengths of time in the unit it was entered in, e.g. "2 weeks". */ public static function duration( $plan, $key ) { $unit = $plan[ $key . '_unit' ]; $amount = (int) ( $plan[ $key ] / self::units()[ $unit ][1] ); switch ( $unit ) { case 'week': /* translators: %d: number of weeks */ return sprintf( _n( '%d week', '%d weeks', $amount, 'postfee' ), $amount ); case 'month': /* translators: %d: number of months */ return sprintf( _n( '%d month', '%d months', $amount, 'postfee' ), $amount ); case 'year': /* translators: %d: number of years */ return sprintf( _n( '%d year', '%d years', $amount, 'postfee' ), $amount ); } /* translators: %d: number of days */ return sprintf( _n( '%d day', '%d days', $amount, 'postfee' ), $amount ); } public static function register() { $cap = 'manage_options'; register_post_type( self::POST_TYPE, array( 'labels' => array( 'name' => __( 'Plans', 'postfee' ), 'singular_name' => __( 'Plan', 'postfee' ), 'add_new_item' => __( 'Add New Plan', 'postfee' ), 'edit_item' => __( 'Edit Plan', 'postfee' ), 'search_items' => __( 'Search Plans', 'postfee' ), 'not_found' => __( 'No plans found.', 'postfee' ), ), 'public' => false, 'show_ui' => true, 'show_in_menu' => 'postfee', 'supports' => array( 'title', 'excerpt', 'page-attributes' ), 'map_meta_cap' => false, 'capabilities' => array( 'edit_post' => $cap, 'read_post' => $cap, 'delete_post' => $cap, 'edit_posts' => $cap, 'edit_others_posts' => $cap, 'delete_posts' => $cap, 'publish_posts' => $cap, 'read_private_posts' => $cap, 'create_posts' => $cap, ), ) ); } /** * @return array|null Plan data, or null if the plan does not exist or is not published. */ public static function get( $id ) { $post = $id ? get_post( $id ) : null; if ( ! $post || self::POST_TYPE !== $post->post_type || 'publish' !== $post->post_status ) { return null; } $type = get_post_meta( $post->ID, '_postfee_type', true ); $window = get_post_meta( $post->ID, '_postfee_return_window', true ); $durations = array(); foreach ( array( 'credit_days', 'listing_days', 'featured_days' ) as $key ) { $days = absint( get_post_meta( $post->ID, '_postfee_' . $key, true ) ); $unit = get_post_meta( $post->ID, '_postfee_' . $key . '_unit', true ); // A length that is not a whole number of its unit is shown in days. $durations[ $key . '_unit' ] = isset( self::units()[ $unit ] ) && 0 === $days % self::units()[ $unit ][1] ? $unit : 'day'; } return $durations + array( 'unpublish_on_end' => (bool) get_post_meta( $post->ID, '_postfee_unpublish_on_end', true ), 'return_window' => isset( self::return_windows()[ $window ] ) ? $window : 'forever', 'id' => $post->ID, 'name' => $post->post_title, 'description' => $post->post_excerpt, 'type' => isset( self::types()[ $type ] ) ? $type : 'single', 'price' => (float) get_post_meta( $post->ID, '_postfee_price', true ), 'credits' => absint( get_post_meta( $post->ID, '_postfee_credits', true ) ), 'interval' => 'year' === get_post_meta( $post->ID, '_postfee_interval', true ) ? 'year' : 'month', 'credit_days' => absint( get_post_meta( $post->ID, '_postfee_credit_days', true ) ), 'listing_days' => absint( get_post_meta( $post->ID, '_postfee_listing_days', true ) ), 'featured_days' => absint( get_post_meta( $post->ID, '_postfee_featured_days', true ) ), 'post_types' => array_filter( (array) get_post_meta( $post->ID, '_postfee_post_types', true ) ), ); } /** * @param array $types Plan types to include, empty for all. * @param string $post_type Only plans usable for this post type. */ public static function all( $types = array(), $post_type = '' ) { $ids = get_posts( array( 'post_type' => self::POST_TYPE, 'post_status' => 'publish', 'posts_per_page' => 100, 'orderby' => array( 'menu_order' => 'ASC', 'title' => 'ASC', ), 'fields' => 'ids', ) ); $plans = array(); foreach ( $ids as $id ) { $plan = self::get( $id ); if ( ! $plan || ( $types && ! in_array( $plan['type'], $types, true ) ) ) { continue; } if ( $post_type && $plan['post_types'] && ! in_array( $post_type, $plan['post_types'], true ) ) { continue; } $plans[] = $plan; } return $plans; } /** * Credits keep working if their plan is later deleted or unrestricted. */ public static function allows_post_type( $plan_id, $post_type ) { $post_types = array_filter( (array) get_post_meta( $plan_id, '_postfee_post_types', true ) ); return ! $post_types || in_array( $post_type, $post_types, true ); } public static function price_label( $plan ) { if ( $plan['price'] <= 0 ) { return __( 'Free', 'postfee' ); } $price = postfee_format_price( postfee_to_minor( $plan['price'] ) ); if ( 'subscription' === $plan['type'] ) { /* translators: %s: price */ return sprintf( 'year' === $plan['interval'] ? __( '%s / year', 'postfee' ) : __( '%s / month', 'postfee' ), $price ); } return $price; } /** * Human-readable list of what a plan includes. */ public static function summary( $plan ) { $lines = array(); if ( 'single' === $plan['type'] ) { $lines[] = __( '1 post', 'postfee' ); } elseif ( 'package' === $plan['type'] ) { /* translators: %d: number of posts */ $lines[] = sprintf( _n( '%d post', '%d posts', $plan['credits'], 'postfee' ), $plan['credits'] ); if ( $plan['credit_days'] ) { /* translators: %s: length of time, e.g. "30 days" */ $lines[] = sprintf( __( 'Use within %s', 'postfee' ), self::duration( $plan, 'credit_days' ) ); } } elseif ( 'subscription' === $plan['type'] ) { if ( $plan['credits'] ) { $lines[] = 'year' === $plan['interval'] /* translators: %d: number of posts */ ? sprintf( _n( '%d post per year', '%d posts per year', $plan['credits'], 'postfee' ), $plan['credits'] ) /* translators: %d: number of posts */ : sprintf( _n( '%d post per month', '%d posts per month', $plan['credits'], 'postfee' ), $plan['credits'] ); } else { $lines[] = __( 'Unlimited posts', 'postfee' ); } } if ( $plan['listing_days'] ) { $lines[] = 'upgrade' === $plan['type'] /* translators: %s: length of time, e.g. "30 days" */ ? sprintf( __( 'Extends the listing by %s', 'postfee' ), self::duration( $plan, 'listing_days' ) ) /* translators: %s: length of time, e.g. "30 days" */ : sprintf( __( 'Listed for %s', 'postfee' ), self::duration( $plan, 'listing_days' ) ); } if ( $plan['featured_days'] ) { /* translators: %s: length of time, e.g. "30 days" */ $lines[] = sprintf( __( 'Featured for %s', 'postfee' ), self::duration( $plan, 'featured_days' ) ); } return $lines; } public static function add_meta_box() { add_meta_box( 'postfee-plan', __( 'Plan details', 'postfee' ), array( __CLASS__, 'meta_box' ), self::POST_TYPE, 'normal', 'high' ); } public static function meta_box( $post ) { $meta = function ( $key, $default = '' ) use ( $post ) { $value = get_post_meta( $post->ID, '_postfee_' . $key, true ); return '' === $value ? $default : $value; }; // A number and a unit for a length of time, which is saved in days. $duration = function ( $key ) use ( $meta ) { $days = absint( $meta( $key, 0 ) ); $unit = $meta( $key . '_unit', 'day' ); $unit = isset( self::units()[ $unit ] ) && 0 === $days % self::units()[ $unit ][1] ? $unit : 'day'; printf( ' ', esc_attr( $key ), absint( $days / self::units()[ $unit ][1] ) ); printf( ''; }; $type = $meta( 'type', 'single' ); $post_types = (array) $meta( 'post_types', array() ); wp_nonce_field( 'postfee_save_plan', '_postfee_plan_nonce' ); ?>