PluginProbe ʕ •ᴥ•ʔ
Presto Player / trunk
Presto Player vtrunk
4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.4 4.2.3 4.2.2 4.2.0 4.2.1 trunk 1.10.0 1.10.1 1.10.2 1.11.0 1.12.0 1.13.0 1.14.0 1.14.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 1.6.13 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.3-beta1 2.3.0 2.3.1 2.3.2 2.3.3 3.0.0 3.0.0-beta1 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.1.0 3.1.1 3.1.2 3.1.3 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4
presto-player / inc / Services / Menu.php
presto-player / inc / Services Last commit date
API 1 week ago Abilities 1 week ago Blocks 3 weeks ago License 1 month ago OAuth 1 week ago AdminNotice.php 2 months ago AdminNotices.php 2 months ago AjaxActions.php 2 months ago Blocks.php 2 years ago Compatibility.php 11 months ago FeatureAnnounce.php 1 week ago Learn.php 1 week ago Menu.php 1 week ago Migrations.php 2 years ago NpsSurvey.php 6 months ago Onboarding.php 2 months ago Player.php 3 weeks ago PluginInstaller.php 1 week ago PreloadService.php 2 years ago ProCompatibility.php 3 weeks ago ReusableVideos.php 2 months ago RewriteRulesManager.php 2 years ago Scripts.php 2 months ago Settings.php 3 months ago Shortcodes.php 1 week ago Streamer.php 2 months ago Translation.php 3 weeks ago Usage.php 1 week ago VideoPostType.php 3 weeks ago
Menu.php
377 lines
1 <?php
2 /**
3 * Admin menu registration for the Presto Player dashboard.
4 *
5 * @package PrestoPlayer
6 * @subpackage Services
7 */
8
9 namespace PrestoPlayer\Services;
10
11 use PrestoPlayer\Plugin;
12 use PrestoPlayer\Services\License\License;
13
14 /**
15 * Registers the top-level "Presto Player" admin menu and submenu structure.
16 */
17 class Menu {
18
19 /**
20 * Whether enqueueing has run for the dashboard page.
21 *
22 * @var bool
23 */
24 protected $enqueue;
25
26 /**
27 * Register WordPress hooks for the menu service.
28 *
29 * @return void
30 */
31 public function register() {
32 add_action( 'admin_menu', array( $this, 'addMenu' ) );
33 }
34
35 /**
36 * Register the top-level menu and its submenu pages.
37 *
38 * @return void
39 */
40 public function addMenu() {
41 // Custom-slug menu following the SureDash submenu pattern. See #996.
42 $dashboard_page = add_menu_page(
43 __( 'Presto Player', 'presto-player' ),
44 __( 'Presto Player', 'presto-player' ),
45 'publish_posts',
46 'presto-dashboard',
47 function () {
48 ob_start();
49 ?>
50 <div id="presto-admin-dashboard">
51 <div id="presto-admin-dashboard-content"></div>
52 </div>
53 <?php wp_auth_check_html(); ?>
54 <?php
55 $page = ob_get_clean();
56 echo $page; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Pre-rendered admin HTML from trusted markup above.
57 },
58 'data:image/svg+xml;base64,' . base64_encode( file_get_contents( PRESTO_PLAYER_PLUGIN_DIR . 'img/menu-icon.svg' ) ),
59 58
60 );
61
62 // First submenu — same slug as parent, replaces "Presto Player" label with "Dashboard".
63 add_submenu_page(
64 'presto-dashboard',
65 __( 'Dashboard', 'presto-player' ),
66 __( 'Dashboard', 'presto-player' ),
67 'publish_posts',
68 'presto-dashboard'
69 );
70
71 // Remaining tabs — each uses admin.php?page=presto-dashboard&tab=X as slug.
72 // WordPress renders these as links to the dashboard page with the tab param.
73 add_submenu_page(
74 'presto-dashboard',
75 __( 'Media Hub', 'presto-player' ),
76 __( 'Media Hub', 'presto-player' ),
77 'publish_posts',
78 'admin.php?page=presto-dashboard&tab=MediaHub'
79 );
80
81 add_submenu_page(
82 'presto-dashboard',
83 __( 'Analytics', 'presto-player' ),
84 __( 'Analytics', 'presto-player' ),
85 'publish_posts',
86 'admin.php?page=presto-dashboard&tab=Analytics'
87 );
88
89 add_submenu_page(
90 'presto-dashboard',
91 __( 'Emails', 'presto-player' ),
92 __( 'Emails', 'presto-player' ),
93 'manage_options',
94 'admin.php?page=presto-dashboard&tab=Emails'
95 );
96
97 // AI (the MCP settings section) — surfaced as its own menu item, above
98 // Settings, with a "New" badge so the capability is easy to find. The
99 // title carries a small HTML badge; markup is a static, translated,
100 // trusted string. manage_options, like Emails above: the section reads
101 // /wp/v2/settings, so anyone below that just lands on a failed screen.
102 add_submenu_page(
103 'presto-dashboard',
104 __( 'AI Abilities', 'presto-player' ),
105 __( 'AI Abilities', 'presto-player' ) . ' <span class="presto-menu-badge">' . esc_html__( 'New', 'presto-player' ) . '</span>',
106 'manage_options',
107 'admin.php?page=presto-dashboard&tab=Settings&section=mcp'
108 );
109
110 add_submenu_page(
111 'presto-dashboard',
112 __( 'Settings', 'presto-player' ),
113 __( 'Settings', 'presto-player' ),
114 'publish_posts',
115 'admin.php?page=presto-dashboard&tab=Settings'
116 );
117
118 add_submenu_page(
119 'presto-dashboard',
120 __( 'Learn', 'presto-player' ),
121 __( 'Learn', 'presto-player' ),
122 'publish_posts',
123 'admin.php?page=presto-dashboard&tab=Learn'
124 );
125
126 // Pro-active-but-unlicensed users see the License tab CTA inside the dashboard,
127 // so the external "Upgrade to Pro" submenu entry is shown only when Pro isn't installed.
128 if ( ! class_exists( '\PrestoPlayer\Pro\Plugin' ) ) {
129 global $submenu;
130 $submenu['presto-dashboard'][] = array( // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Appending to the admin submenu global is the standard WordPress pattern.
131 __( 'Upgrade to Pro', 'presto-player' ),
132 'publish_posts',
133 'https://prestoplayer.com/pricing/',
134 );
135 add_action( 'admin_head', array( $this, 'upgradeToProLinkStyles' ) );
136 add_action( 'admin_footer', array( $this, 'upgradeToProLinkScript' ) );
137 }
138
139 add_action( 'admin_head', array( $this, 'submenuDividerStyles' ) );
140 add_action( "admin_print_scripts-{$dashboard_page}", array( $this, 'dashboardAssets' ) );
141 }
142
143 /**
144 * Add visual dividers between submenu groups.
145 *
146 * Groups:
147 * 1. Dashboard (overview)
148 * 2. Media Hub, Analytics, Emails (features)
149 * 3. Settings, Learn (config & help)
150 * 4. Upgrade to Pro (upsell, non-Pro only)
151 *
152 * nth-child mapping (1st child is the hidden parent duplicate):
153 * 2 = Dashboard, 3 = Media Hub, 4 = Analytics,
154 * 5 = Emails, 6 = Settings, 7 = Learn, 8 = Upgrade to Pro
155 */
156 public function submenuDividerStyles() {
157 $menu = '#toplevel_page_presto-dashboard';
158 $slug = 'admin.php?page=presto-dashboard';
159 ?>
160 <style>
161 <?php echo esc_attr( $menu ); ?> li {
162 clear: both;
163 }
164 <?php echo esc_attr( $menu ); ?> li:not(:last-child) a[href^="<?php echo esc_attr( $slug ); ?>"]:after {
165 border-bottom: 1px solid hsla(0, 0%, 100%, .2);
166 display: block;
167 float: left;
168 margin: 13px -15px 8px;
169 content: "";
170 width: calc(100% + 26px);
171 }
172 <?php echo esc_attr( $menu ); ?> li:not(:last-child) a[href^="<?php echo esc_attr( $slug ); ?>&tab=MediaHub"]:after,
173 <?php echo esc_attr( $menu ); ?> li:not(:last-child) a[href^="<?php echo esc_attr( $slug ); ?>&tab=Analytics"]:after,
174 <?php echo esc_attr( $menu ); ?> li:not(:last-child) a[href^="<?php echo esc_attr( $slug ); ?>&tab=Emails"]:after,
175 <?php echo esc_attr( $menu ); ?> li:not(:last-child) a[href$="&tab=Settings"]:after {
176 content: none;
177 }
178 <?php echo esc_attr( $menu ); ?> .wp-submenu .presto-menu-badge {
179 display: inline-block;
180 position: relative;
181 top: -5px;
182 margin-left: 4px;
183 padding: 0 4px;
184 font-size: 8px;
185 font-weight: 600;
186 line-height: 12px;
187 text-transform: uppercase;
188 letter-spacing: .3px;
189 color: #fff;
190 background: #4f46e5;
191 border-radius: 6px;
192 }
193 </style>
194 <?php
195 }
196
197 /**
198 * Style the "Upgrade to Pro" submenu link with an external-link icon.
199 * Hooked to admin_head only when Pro is not installed.
200 */
201 public function upgradeToProLinkStyles() {
202 ?>
203 <style>
204 #toplevel_page_presto-dashboard .wp-submenu a[href*="prestoplayer.com/pricing"]::after {
205 content: "\f504";
206 font-family: dashicons;
207 font-size: 13px;
208 margin-left: 6px;
209 vertical-align: middle;
210 opacity: 0.6;
211 }
212 </style>
213 <?php
214 }
215
216 /**
217 * Open the "Upgrade to Pro" submenu link in a new tab.
218 * Hooked to admin_footer (DOM must exist before the script runs).
219 * Follows the SureForms pattern: intercept click → window.open.
220 */
221 public function upgradeToProLinkScript() {
222 ?>
223 <script>
224 document.addEventListener( 'DOMContentLoaded', function() {
225 const link = document.querySelector( '#toplevel_page_presto-dashboard .wp-submenu a[href*="prestoplayer.com/pricing"]' );
226 if ( link ) {
227 link.addEventListener( 'click', function( e ) {
228 e.preventDefault();
229 window.open( link.href, '_blank' );
230 } );
231 }
232 } );
233 </script>
234 <?php
235 }
236
237 /**
238 * Get plugin status
239 *
240 * @param string $plugin_file Plugin file path.
241 * @return string Plugin status.
242 */
243 public function getPluginStatus( $plugin_file ) {
244 if ( ! function_exists( 'get_plugins' ) ) {
245 require_once ABSPATH . 'wp-admin/includes/plugin.php';
246 }
247
248 $installed_plugins = get_plugins();
249
250 if ( ! isset( $installed_plugins[ $plugin_file ] ) ) {
251 return 'not-installed';
252 }
253
254 return is_plugin_active( $plugin_file ) ? 'active' : 'inactive';
255 }
256
257 /**
258 * Scripts needed on dashboard page
259 */
260 public function dashboardAssets() {
261 wp_enqueue_media();
262
263 $assets = include trailingslashit( PRESTO_PLAYER_PLUGIN_DIR ) . 'dist/dashboard.asset.php';
264 wp_enqueue_script(
265 'presto/dashboard/admin',
266 trailingslashit( PRESTO_PLAYER_PLUGIN_URL ) . 'dist/dashboard.js',
267 array_merge( array( 'hls.js', 'presto-components', 'regenerator-runtime', 'updates' ), $assets['dependencies'] ),
268 $assets['version'],
269 true
270 );
271
272 // Dashboard style.
273 wp_enqueue_style( 'presto/dashboard/admin', trailingslashit( PRESTO_PLAYER_PLUGIN_URL ) . 'dist/dashboard.css', array(), $assets['version'] );
274
275 // Font styles for WhatsNew flyout.
276 wp_enqueue_style( 'presto/dashboard/fonts', trailingslashit( PRESTO_PLAYER_PLUGIN_URL ) . 'assets/css/font.css', array(), Plugin::version() );
277
278 wp_enqueue_style( 'wp-components' );
279
280 if ( function_exists( 'wp_set_script_translations' ) ) {
281 wp_set_script_translations( 'presto/dashboard/admin', 'presto-player' );
282 }
283
284 $current_user = wp_get_current_user();
285
286 // Check if SureDash is available and get onboarding image.
287 $onboarding_image = '';
288 if ( defined( 'SUREDASHBOARD_URL' ) ) {
289 $onboarding_image = SUREDASHBOARD_URL . 'assets/images/onboarding.svg';
290 }
291
292 wp_localize_script(
293 'presto/dashboard/admin',
294 'prestoPlayer',
295 array(
296 'root' => esc_url_raw( get_rest_url() ),
297 'nonce' => wp_create_nonce( 'presto_player_admin' ),
298 'ajaxurl' => admin_url( 'admin-ajax.php' ),
299 'wpVersionString' => 'wp/v2/',
300 'prestoVersionString' => 'presto-player/v1/',
301 'version' => Plugin::version(),
302 'proVersion' => Plugin::proVersion(),
303 'isPremium' => Plugin::isPro(),
304 'is_pro_available' => Plugin::isPro(),
305 'isProPluginActive' => class_exists( '\PrestoPlayer\Pro\Plugin' ),
306 'hasLicenseKey' => License::hasKey(),
307 'license_status' => Plugin::licenseStatus(),
308 'dashboardUrl' => admin_url( 'admin.php?page=presto-dashboard' ),
309 'upgradeLink' => 'https://prestoplayer.com/pricing/',
310 'upgrade_link' => 'https://prestoplayer.com/pricing/',
311 'onboarding_image' => $onboarding_image,
312 'upgrade_image' => trailingslashit( PRESTO_PLAYER_PLUGIN_URL ) . 'img/upgrade-illustration.png',
313 'createMediaUrl' => admin_url( 'post-new.php?post_type=pp_video_block' ),
314 'mediaHubUrl' => admin_url( 'admin.php?page=presto-dashboard&tab=MediaHub' ),
315 'onboardingUrl' => admin_url( 'admin.php?page=presto-dashboard&post_type=pp_video_block&tab=Onboarding' ),
316 'currentUser' => array(
317 'display_name' => $current_user->display_name,
318 'user_login' => $current_user->user_login,
319 'user_email' => $current_user->user_email,
320 'first_name' => $current_user->user_firstname ? $current_user->user_firstname : $current_user->display_name,
321 'last_name' => $current_user->user_lastname ? $current_user->user_lastname : '',
322 ),
323 'suredash_status' => $this->getPluginStatus( 'suredash/suredash.php' ),
324 'surecart_status' => $this->getPluginStatus( 'surecart/surecart.php' ),
325 'suretriggers_status' => $this->getPluginStatus( 'suretriggers/suretriggers.php' ),
326 'suremembers_status' => $this->getPluginStatus( 'suremembers/suremembers.php' ),
327 'suremails_status' => $this->getPluginStatus( 'suremails/suremails.php' ),
328 'sureforms_status' => $this->getPluginStatus( 'sureforms/sureforms.php' ),
329 'mcpAdapterStatus' => $this->getPluginStatus( 'mcp-adapter/mcp-adapter.php' ),
330 'mcpAdapterDownloadUrl' => \PrestoPlayer\Services\PluginInstaller::MCP_ADAPTER_DOWNLOAD_URL,
331 'abilitiesSupported' => function_exists( 'wp_register_ability' ),
332 'siteUrl' => site_url(),
333 'onboarding_completed' => get_option( 'presto_player_onboarding_completed', 'no' ) === 'yes',
334 'onboarding_redirect' => isset( $_GET['tab'] ) && 'Onboarding' === $_GET['tab'], // phpcs:ignore WordPress.Security.NonceVerification.Recommended
335 'onboarding' => array(
336 'addonList' => array(
337 array(
338 'slug' => 'suredash',
339 'title' => __( 'Build Your Video Community with SureDash', 'presto-player' ),
340 'logo' => 'https://ps.w.org/suredash/assets/icon-256x256.gif',
341 'description' => __( 'Create a members-only space around your video content with courses, forums, and community features.', 'presto-player' ),
342 ),
343 array(
344 'slug' => 'surecart',
345 'title' => __( 'Sell Video Content with SureCart', 'presto-player' ),
346 'logo' => 'https://ps.w.org/surecart/assets/icon-256x256.gif',
347 'description' => __( 'Accept payments for courses, memberships, and digital downloads directly from your site.', 'presto-player' ),
348 ),
349 array(
350 'slug' => 'suretriggers',
351 'title' => __( 'Automate Your Video Workflows with OttoKit', 'presto-player' ),
352 'logo' => 'https://ps.w.org/suretriggers/assets/icon-256x256.png',
353 'description' => __( 'Trigger actions when videos are watched, emails captured, or goals reached — no code needed.', 'presto-player' ),
354 ),
355 array(
356 'slug' => 'suremails',
357 'title' => __( 'Keep Emails Out of Spam with SureMail', 'presto-player' ),
358 'logo' => 'https://ps.w.org/suremails/assets/icon-256x256.gif',
359 'description' => __( 'Ensure video notifications and email captures actually reach your audience\'s inbox.', 'presto-player' ),
360 ),
361 ),
362 ),
363 'canManageEmailSubmissions' => current_user_can( 'manage_options' ),
364 'canManageOptions' => current_user_can( 'manage_options' ),
365 'i18n' => Translation::geti18n(),
366 'api' => array(
367 'analyticsViews' => '/presto-player/v1/analytics/views',
368 'analyticsWatchTime' => '/presto-player/v1/analytics/watch-time',
369 'analyticsTopVideos' => '/presto-player/v1/analytics/top-videos',
370 'analyticsTopUsers' => '/presto-player/v1/analytics/top-users',
371 'activatePlugin' => '/presto-player/v1/activate-plugin',
372 ),
373 )
374 );
375 }
376 }
377