| 1 |
<?php |
| 2 |
/** |
| 3 |
* The two panels in the side column of the streamcast edit screen: the builder |
| 4 |
* support card, and the Pro tuner. |
| 5 |
* |
| 6 |
* This is the free build, so the Pro card always renders -- there is no premium |
| 7 |
* code path to check for. |
| 8 |
* |
| 9 |
* Both draw their own surface, so the postbox chrome around them is stripped by the |
| 10 |
* streamcast-flat-box class added in flat_postbox_class(). The streamcast post type |
| 11 |
* supports 'title' only and is not exposed to REST, so the classic editor is the only |
| 12 |
* editor this screen ever runs and no block-editor fallback is needed. |
| 13 |
* |
| 14 |
* @package StreamCast |
| 15 |
*/ |
| 16 |
|
| 17 |
namespace StreamCast; |
| 18 |
|
| 19 |
if (!defined('ABSPATH')) exit; |
| 20 |
|
| 21 |
if (!class_exists('StreamCast\STREAMCAST_SideBox')) { |
| 22 |
class STREAMCAST_SideBox { |
| 23 |
|
| 24 |
const POST_TYPE = 'streamcast'; |
| 25 |
const BUILDERS_ID = 'streamcast_where_it_plays'; |
| 26 |
const PRO_ID = 'streamcast_pro_tuner'; |
| 27 |
|
| 28 |
public function register() { |
| 29 |
if (!is_admin()) { |
| 30 |
return; |
| 31 |
} |
| 32 |
|
| 33 |
add_action('add_meta_boxes', [$this, 'add_boxes']); |
| 34 |
add_filter('get_user_option_meta-box-order_' . self::POST_TYPE, [$this, 'release_boxes']); |
| 35 |
} |
| 36 |
|
| 37 |
/** |
| 38 |
* The side column is ordered by priority: submitdiv registers at 'core', so |
| 39 |
* 'default' puts the builder card directly under Save, and 'low' closes the |
| 40 |
* column out with the Pro card. |
| 41 |
*/ |
| 42 |
public function add_boxes() { |
| 43 |
add_meta_box( |
| 44 |
self::BUILDERS_ID, |
| 45 |
esc_html__('Where It Plays', 'streamcast'), |
| 46 |
[$this, 'render_builders'], |
| 47 |
self::POST_TYPE, |
| 48 |
'side', |
| 49 |
'default' |
| 50 |
); |
| 51 |
|
| 52 |
add_filter('postbox_classes_' . self::POST_TYPE . '_' . self::BUILDERS_ID, [$this, 'flat_postbox_class']); |
| 53 |
|
| 54 |
add_meta_box( |
| 55 |
self::PRO_ID, |
| 56 |
esc_html__('StreamCast Pro', 'streamcast'), |
| 57 |
[$this, 'render_pro'], |
| 58 |
self::POST_TYPE, |
| 59 |
'side', |
| 60 |
'low' |
| 61 |
); |
| 62 |
|
| 63 |
add_filter('postbox_classes_' . self::POST_TYPE . '_' . self::PRO_ID, [$this, 'flat_postbox_class']); |
| 64 |
} |
| 65 |
|
| 66 |
/** |
| 67 |
* A box named in the user's stored meta-box order is rendered from the 'sorted' |
| 68 |
* bucket, which WordPress draws ahead of 'core' -- that is how a card like this |
| 69 |
* ends up above Save once a layout has been saved, and hiding the drag handle |
| 70 |
* means it cannot be dragged back. Dropping our IDs out of the stored string |
| 71 |
* hands them back to their priorities and leaves every other box the user |
| 72 |
* arranged exactly where they put it. Nothing is written back. |
| 73 |
*/ |
| 74 |
public function release_boxes($order) { |
| 75 |
if (!is_array($order) || empty($order['side'])) { |
| 76 |
return $order; |
| 77 |
} |
| 78 |
|
| 79 |
$ours = [self::BUILDERS_ID, self::PRO_ID]; |
| 80 |
$kept = array_diff(explode(',', $order['side']), $ours); |
| 81 |
|
| 82 |
$order['side'] = implode(',', $kept); |
| 83 |
|
| 84 |
return $order; |
| 85 |
} |
| 86 |
|
| 87 |
/** |
| 88 |
* Stripping .postbox-header also removes the toggle and the drag handle, which is |
| 89 |
* what keeps these cards open and in place. |
| 90 |
*/ |
| 91 |
public function flat_postbox_class($classes) { |
| 92 |
$classes[] = 'streamcast-flat-box'; |
| 93 |
|
| 94 |
return $classes; |
| 95 |
} |
| 96 |
|
| 97 |
/** |
| 98 |
* Short label for the two-column grid, full name for its tooltip -- "Beaver |
| 99 |
* Builder" does not fit a 10px cell in a 280px column without wrapping the row. |
| 100 |
*/ |
| 101 |
private function builders() { |
| 102 |
return [ |
| 103 |
__('Elementor', 'streamcast') => __('Elementor', 'streamcast'), |
| 104 |
__('Divi', 'streamcast') => __('Divi Builder', 'streamcast'), |
| 105 |
__('Bricks', 'streamcast') => __('Bricks Builder', 'streamcast'), |
| 106 |
__('WPBakery', 'streamcast') => __('WPBakery Page Builder', 'streamcast'), |
| 107 |
__('Beaver', 'streamcast') => __('Beaver Builder', 'streamcast'), |
| 108 |
__('Oxygen', 'streamcast') => __('Oxygen Builder', 'streamcast'), |
| 109 |
__('Breakdance', 'streamcast') => __('Breakdance', 'streamcast'), |
| 110 |
__('Widgets', 'streamcast') => __('Widget areas & sidebars', 'streamcast'), |
| 111 |
]; |
| 112 |
} |
| 113 |
|
| 114 |
/** |
| 115 |
* The settings this build gates. Kept in step with the crown-marked and locked |
| 116 |
* fields in class-streamcast-metabox.php. |
| 117 |
*/ |
| 118 |
private function locked_features() { |
| 119 |
return [ |
| 120 |
__('Auto play & initial volume', 'streamcast'), |
| 121 |
__('Artwork & background image', 'streamcast'), |
| 122 |
__('Player colors & themes', 'streamcast'), |
| 123 |
__('Fetch station name from URL', 'streamcast'), |
| 124 |
__('Sticky player position', 'streamcast'), |
| 125 |
__('Visualizer & progress colors', 'streamcast'), |
| 126 |
]; |
| 127 |
} |
| 128 |
|
| 129 |
/** |
| 130 |
* Capability statement, not detection: the jacks are lit as a matter of fact. The |
| 131 |
* card never claims a builder is installed, which is what keeps it static and |
| 132 |
* free of any runtime check. |
| 133 |
*/ |
| 134 |
public function render_builders($post) { |
| 135 |
$builders = $this->builders(); |
| 136 |
$outputs = count($builders) + 1; // the builders, plus the native block. |
| 137 |
$shortcode = "[radio_player id='" . $post->ID . "']"; |
| 138 |
?> |
| 139 |
<div class="streamcast-bay"> |
| 140 |
<div class="streamcast-bay__top"> |
| 141 |
<span class="streamcast-bay__lbl"><?php esc_html_e('Where it plays', 'streamcast'); ?></span> |
| 142 |
<span class="streamcast-bay__count"> |
| 143 |
<?php echo absint($outputs) . ' / ' . absint($outputs); ?> ✓ |
| 144 |
</span> |
| 145 |
</div> |
| 146 |
|
| 147 |
<div class="streamcast-bay__native"> |
| 148 |
<span class="streamcast-jack" aria-hidden="true"></span> |
| 149 |
<span class="streamcast-bay__names"> |
| 150 |
<span class="streamcast-bay__n1"><?php esc_html_e('Block editor', 'streamcast'); ?></span> |
| 151 |
<span class="streamcast-bay__n2"><?php esc_html_e('Native block', 'streamcast'); ?></span> |
| 152 |
</span> |
| 153 |
<span class="streamcast-bay__chip"><?php esc_html_e('No code', 'streamcast'); ?></span> |
| 154 |
</div> |
| 155 |
|
| 156 |
<button type="button" |
| 157 |
class="streamcast-bay__rail streamcast_shortcode_copy_btn" |
| 158 |
data-shortcode="<?php echo esc_attr($shortcode); ?>" |
| 159 |
title="<?php esc_attr_e('Copy shortcode', 'streamcast'); ?>"> |
| 160 |
<span class="streamcast-jack streamcast-jack--sm" aria-hidden="true"></span> |
| 161 |
<span class="streamcast-bay__code copy-text"><?php echo esc_html($shortcode); ?></span> |
| 162 |
<span class="streamcast-bay__arrow" aria-hidden="true">→</span> |
| 163 |
</button> |
| 164 |
|
| 165 |
<div class="streamcast-bay__grid"> |
| 166 |
<?php foreach ($builders as $short => $full) { ?> |
| 167 |
<span title="<?php echo esc_attr($full); ?>"> |
| 168 |
<i class="streamcast-jack streamcast-jack--sm" aria-hidden="true"></i> |
| 169 |
<?php echo esc_html($short); ?> |
| 170 |
</span> |
| 171 |
<?php } ?> |
| 172 |
</div> |
| 173 |
|
| 174 |
<p class="streamcast-bay__foot"> |
| 175 |
<?php esc_html_e('Drop the shortcode into any of these. Nothing extra to install — no add-on, no per-builder widget.', 'streamcast'); ?> |
| 176 |
</p> |
| 177 |
</div> |
| 178 |
<?php |
| 179 |
} |
| 180 |
|
| 181 |
public function render_pro() { |
| 182 |
$features = $this->locked_features(); |
| 183 |
$upgrade = \StreamCast\Functions::pro_url(); |
| 184 |
?> |
| 185 |
<div class="streamcast-tuner"> |
| 186 |
<div class="streamcast-tuner__scale" aria-hidden="true"> |
| 187 |
<div class="streamcast-tuner__ticks"> |
| 188 |
<?php for ($i = 0; $i < 24; $i++) { ?><i></i><?php } ?> |
| 189 |
</div> |
| 190 |
<span class="streamcast-tuner__needle"></span> |
| 191 |
</div> |
| 192 |
|
| 193 |
<div class="streamcast-tuner__lbl" aria-hidden="true"> |
| 194 |
<span class="on"><?php esc_html_e('Free', 'streamcast'); ?></span> |
| 195 |
<span>·</span> |
| 196 |
<span>·</span> |
| 197 |
<span><?php esc_html_e('Pro — 24 settings', 'streamcast'); ?></span> |
| 198 |
</div> |
| 199 |
</div> |
| 200 |
|
| 201 |
<div class="streamcast-tuner__body"> |
| 202 |
<h4> |
| 203 |
<?php |
| 204 |
printf( |
| 205 |
/* translators: %d: number of settings available only in the Pro version. */ |
| 206 |
esc_html__('%d switches on this screen are shut.', 'streamcast'), |
| 207 |
absint(count($features)) |
| 208 |
); |
| 209 |
?> |
| 210 |
</h4> |
| 211 |
|
| 212 |
<ul class="streamcast-tuner__locks"> |
| 213 |
<?php foreach ($features as $feature) { ?> |
| 214 |
<li> |
| 215 |
<?php |
| 216 |
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static inline icon markup. |
| 217 |
echo \StreamCast\Functions::lock_icon(); |
| 218 |
echo esc_html($feature); |
| 219 |
?> |
| 220 |
</li> |
| 221 |
<?php } ?> |
| 222 |
</ul> |
| 223 |
|
| 224 |
<a class="streamcast-tuner__cta" href="<?php echo esc_url($upgrade); ?>" target="_blank" rel="noopener"> |
| 225 |
<?php esc_html_e('See what Pro unlocks', 'streamcast'); ?> |
| 226 |
</a> |
| 227 |
|
| 228 |
<p class="streamcast-tuner__foot"><?php esc_html_e('14-day refund policy', 'streamcast'); ?></p> |
| 229 |
</div> |
| 230 |
<?php |
| 231 |
} |
| 232 |
} |
| 233 |
} |
| 234 |
|