*/ private static function bundled( string $name ): array { static $cache = array(); if ( ! isset( $cache[ $name ] ) ) { $file = DASHBREAD_PATH . 'assets/data/' . $name . '.json'; $data = is_readable( $file ) ? json_decode( (string) file_get_contents( $file ), true ) : null; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- local file bundled with the plugin. $cache[ $name ] = is_array( $data ) ? $data : array(); } return $cache[ $name ]; } /** Catalog keyed by product slug: a live list from an add-on, else the bundled one. @return array> */ public static function catalog(): array { /** * Supply the product catalog (Dashbread Pro fetches it from dashbread.com). Return null to use the * catalog bundled with Core. * * @param array>|null $products */ $live = apply_filters( 'dashbread_store_catalog', null ); $from = is_array( $live ) && $live ? $live : (array) ( self::bundled( 'catalog' )['products'] ?? array() ); $out = array(); foreach ( $from as $p ) { if ( is_array( $p ) && ! empty( $p['slug'] ) ) { if ( ! is_array( $live ) || ! $live ) { $p['imageUrl'] = null; // Bundled data never points at remote images. $p['bundled'] = true; } $out[ (string) $p['slug'] ] = $p; } } return $out; } /** * Everything the Pro & Add-ons screen needs: plans, PMAs, and where this site stands. * * @return array */ public static function payload(): array { $catalog = self::catalog(); $ent = array_map( 'strval', (array) apply_filters( 'dashbread_entitlements', array() ) ); $state = (array) apply_filters( 'dashbread_license_state', array() ); $pro = Features::pro_active(); $plans = array(); $addons = array(); foreach ( $catalog as $slug => $p ) { $row = array( 'slug' => $slug, 'name' => (string) ( $p['name'] ?? $slug ), 'tagline' => (string) ( $p['tagline'] ?? '' ), 'description' => wp_kses_post( (string) ( $p['descriptionHtml'] ?? '' ) ), 'includes' => array_values( (array) ( $p['includes'] ?? array() ) ), 'price' => isset( $p['priceCents'] ) ? (int) $p['priceCents'] : null, 'term' => (string) ( $p['term'] ?? '' ), 'tier' => (string) ( $p['tier'] ?? '' ), 'requires' => (string) ( $p['requires'] ?? '' ), 'status' => (string) ( $p['status'] ?? 'active' ), 'image' => esc_url_raw( (string) ( $p['imageUrl'] ?? '' ) ), 'url' => self::site_url() . (string) ( $p['url'] ?? '/products/' . $slug ), ); if ( 'addon' === $row['tier'] && 'dashbread-pmas-bundle' !== $slug ) { $cap = 'addon:' . preg_replace( '/^dashbread-/', '', $slug ); $row['cap'] = $cap; $row['entitled'] = in_array( '*', $ent, true ) || in_array( 'addon:*', $ent, true ) || in_array( $cap, $ent, true ); $row['guide'] = preg_replace( '/^dashbread-/', '', $slug ); $addons[] = $row; } else { $plans[] = $row; } } // Where this site stands, from Pro's license state (empty on a Core-only install). $current = 'free'; if ( in_array( '*', $ent, true ) ) { $current = '__all'; } elseif ( in_array( 'pro', $ent, true ) ) { if ( 'lifetime' === ( $state['term_mode'] ?? '' ) || ( in_array( 'addon:*', $ent, true ) && 'one_time' === ( $state['term_mode'] ?? '' ) ) ) { $current = 'dashbread-lifetime'; } elseif ( in_array( 'addon:*', $ent, true ) ) { $current = 'dashbread-pmas-bundle'; } else { $sl = $state['site_limit'] ?? 1; // null or 'unlimited' = unlimited sites. $current = ( null === $sl || 'unlimited' === (string) $sl ) ? 'dashbread-pro-unlimited' : ( 5 === (int) $sl ? 'dashbread-pro-5-site' : 'dashbread-pro' ); } } $rank = array( 'free' => 0, 'dashbread-pro' => 1, 'dashbread-pro-5-site' => 2, 'dashbread-pro-unlimited' => 3, 'dashbread-pmas-bundle' => 4, 'dashbread-lifetime' => 5, '__all' => 99 ); $names = array(); foreach ( $plans as $p ) { $names[ $p['slug'] ] = $p['name']; } $first = reset( $catalog ); return array( 'plans' => $plans, 'addons' => $addons, 'entitlements' => $ent, 'pro' => $pro, 'dev' => defined( 'DASHBREAD_DEV_ENTITLEMENTS' ), 'current' => array( 'key' => $current, 'rank' => $rank[ $current ] ?? 0, 'label' => '__all' === $current ? __( 'All features (dev)', 'dashbread' ) : ( 'free' === $current ? __( 'Free (Core)', 'dashbread' ) : ( $names[ $current ] ?? $current ) ) ), 'rank' => $rank, 'site_url' => self::site_url(), 'checkout_url' => (string) apply_filters( 'dashbread_checkout_url', self::site_url() . '/#pricing' ), 'account_url' => self::site_url() . '/account', 'license_url' => $pro ? admin_url( 'admin.php?page=dashbread-license' ) : '', 'bundled' => is_array( $first ) && ! empty( $first['bundled'] ), 'bundled_date' => (string) ( self::bundled( 'catalog' )['generated'] ?? '' ), ); } // ---- Help guides ----------------------------------------------------------------------- /** Categories + articles (no bodies): a live list from an add-on, else the bundled guides. @return array{categories:array>,articles:array>,bundled:bool} */ public static function kb(): array { /** * Supply the guide list (Dashbread Pro fetches it from dashbread.com). Return null to use the * guides bundled with Core. * * @param array{categories:array,articles:array}|null $kb */ $live = apply_filters( 'dashbread_help_kb', null ); $bundled = ! ( is_array( $live ) && ! empty( $live['articles'] ) ); $src = $bundled ? self::bundled( 'kb' ) : $live; $out = array( 'categories' => array(), 'articles' => array(), 'bundled' => $bundled ); foreach ( (array) ( $src['categories'] ?? array() ) as $c ) { if ( is_array( $c ) ) { $out['categories'][] = array( 'id' => (string) ( $c['id'] ?? '' ), 'slug' => (string) ( $c['slug'] ?? '' ), 'title' => (string) ( $c['title'] ?? '' ), 'description' => (string) ( $c['description'] ?? '' ) ); } } foreach ( (array) ( $src['articles'] ?? array() ) as $a ) { if ( is_array( $a ) && ! empty( $a['slug'] ) ) { $out['articles'][] = array( 'slug' => (string) $a['slug'], 'title' => (string) ( $a['title'] ?? '' ), 'excerpt' => (string) ( $a['excerpt'] ?? '' ), 'category' => (string) ( $a['category'] ?? '' ), 'category_id' => (string) ( $a['category_id'] ?? '' ) ); } } return $out; } /** One guide with its body: from an add-on, else the bundled copy. */ public static function kb_article( string $slug ): ?array { $slug = sanitize_title( $slug ); /** * Supply one guide (Dashbread Pro fetches it from dashbread.com). Return null to use the bundled copy. * * @param array{title:string,excerpt:string,body:string}|null $article * @param string $slug */ $a = apply_filters( 'dashbread_help_kb_article', null, $slug ); if ( ! is_array( $a ) || empty( $a['body'] ) ) { $a = null; foreach ( (array) ( self::bundled( 'kb' )['articles'] ?? array() ) as $row ) { if ( is_array( $row ) && ( $row['slug'] ?? '' ) === $slug ) { $a = $row; break; } } } if ( ! $a || empty( $a['body'] ) ) { return null; } return array( 'slug' => $slug, 'title' => (string) ( $a['title'] ?? '' ), 'excerpt' => (string) ( $a['excerpt'] ?? '' ), 'body' => wp_kses_post( (string) $a['body'] ) ); } }