array( 'all_bookable', 'dates', 'mode', 'duration', 'min', 'max', 'first', 'last' ), 'calendar' => array( 'display', 'background', 'accent', 'text' ), 'life' => array( 'set_start', 'keep_start', 'set_processing', 'set_end', 'keep_end', 'set_completed' ), 'tools' => array( 'remove_data' ), ); /** * Shop-wide defaults, and the shape of the settings array. * * @since 1.0.0 * @return array */ public static function defaults() { /** * The shop-wide rules and their defaults. * * @since 1.1.0 * @param array $defaults Default value of each rule, by name. */ return apply_filters( 'abfw_settings_defaults', array( 'all_bookable' => 'no', 'dates' => 'two', 'mode' => 'nights', 'duration' => 1, 'min' => 0, 'max' => 0, 'first' => 0, 'last' => 365, 'display' => 'open', 'background' => '#ffffff', 'accent' => '#1d2327', 'text' => '#3c434a', 'set_start' => 'automatic', 'keep_start' => 1, 'set_processing' => 'automatic', 'set_end' => 'automatic', 'keep_end' => 1, 'set_completed' => 'manual', 'remove_data' => 'no', ) ); } /** * Every shop-wide rule, defaults filled in. * * @since 1.0.0 * @return array */ public static function all() { $stored = get_option( self::OPTION, array() ); return wp_parse_args( is_array( $stored ) ? $stored : array(), self::defaults() ); } /** * One rule, resolved for a variation, then its product, then the shop. * * @since 1.0.0 * @param string $key Rule name, as in defaults(). * @param int $product_id Product the rule applies to, 0 for the shop. * @param int $variation_id Variation being bought, when there is one. * @return mixed */ public static function get( $key, $product_id = 0, $variation_id = 0 ) { $shop = self::all(); if ( ! isset( $shop[ $key ] ) ) { return null; } foreach ( array( $variation_id, $product_id ) as $post_id ) { if ( ! $post_id ) { continue; } $own = get_post_meta( $post_id, self::META_PREFIX . $key, true ); if ( '' !== $own && null !== $own ) { return self::cast( $key, $own ); } } return self::cast( $key, $shop[ $key ] ); } /** * Store the shop-wide rules, dropping anything unknown. * * @since 1.0.0 * @param array $values Raw values, already unslashed. */ public static function save( array $values ) { $clean = array(); foreach ( self::defaults() as $key => $default ) { $clean[ $key ] = isset( $values[ $key ] ) ? self::cast( $key, $values[ $key ] ) : $default; } update_option( self::OPTION, $clean ); } /** * Store the rules of one tab, leaving the other tabs untouched. * * @since 1.0.0 * @param string $group Tab that was submitted. * @param array $values Raw values, already unslashed. */ public static function save_group( $group, array $values ) { /** * The rules each tab of the settings screen carries. * * @since 1.1.0 * @param array $groups Rule names, by tab. */ $groups = apply_filters( 'abfw_settings_groups', self::GROUPS ); if ( ! isset( $groups[ $group ] ) ) { return; } $stored = self::all(); foreach ( $groups[ $group ] as $key ) { // An unticked box posts nothing, so a missing value means off. $raw = isset( $values[ $key ] ) ? $values[ $key ] : 'no'; $stored[ $key ] = self::cast( $key, $raw ); } update_option( self::OPTION, $stored ); } /** * Whether a product is sold by date. * * @since 1.0.0 * @param int $product_id Product to check. * @return bool */ public static function is_bookable( $product_id ) { $product = $product_id ? wc_get_product( $product_id ) : null; if ( $product && $product->get_parent_id() ) { $product_id = $product->get_parent_id(); } $own = get_post_meta( $product_id, self::META_PREFIX . 'bookable', true ); if ( '' !== $own ) { return 'yes' === $own; } return 'yes' === self::get( 'all_bookable' ); } /** * The rules a booking form needs, ready for the front end. * * @since 1.0.0 * @param int $product_id Product being shown. * @param int $variation_id Variation being shown, when there is one. * @return array */ public static function for_product( $product_id, $variation_id = 0 ) { $rules = array(); foreach ( array_keys( self::defaults() ) as $key ) { $rules[ $key ] = self::get( $key, $product_id, $variation_id ); } return $rules; } /** * Turn a stored value into the type the rest of the plugin expects. * * @since 1.0.0 * @param string $key Rule name. * @param mixed $value Raw value. * @return mixed */ private static function cast( $key, $value ) { /** * A rule read in the type its own code expects, null to leave it to the plugin. * * @since 1.1.0 * @param mixed $cast Null so far. * @param string $key Rule name. * @param mixed $value Raw value. */ $own = apply_filters( 'abfw_cast_setting', null, $key, $value ); if ( null !== $own ) { return $own; } if ( in_array( $key, array( 'duration', 'min', 'max', 'first', 'last', 'keep_start', 'keep_end' ), true ) ) { return max( 0, (int) $value ); } if ( in_array( $key, array( 'background', 'accent', 'text' ), true ) ) { $colour = sanitize_hex_color( $value ); return $colour ? $colour : self::defaults()[ $key ]; } if ( in_array( $key, array( 'set_start', 'set_processing', 'set_end', 'set_completed' ), true ) ) { return 'manual' === $value ? 'manual' : 'automatic'; } if ( 'display' === $key ) { return 'dropdown' === $value ? 'dropdown' : 'open'; } if ( 'mode' === $key ) { return 'days' === $value ? 'days' : 'nights'; } if ( 'dates' === $key ) { return 'one' === $value ? 'one' : 'two'; } return 'yes' === $value ? 'yes' : 'no'; } }