Analytics
2 weeks ago
Database
2 months ago
DynamicFieldResolver.php
1 month ago
Elementor_Enhancer.php
6 months ago
EmbedPress_Core_Installer.php
6 years ago
EmbedPress_Notice.php
3 months ago
EmbedPress_Plugin_Usage_Tracker.php
1 month ago
Extend_CustomPlayer_Controls.php
2 months ago
Extend_Elementor_Controls.php
1 year ago
FeatureNoticeManager.php
4 days ago
FeatureNotices.php
4 days ago
FeaturePreviewModal.php
4 days ago
Feature_Enhancer.php
1 month ago
GoogleReviewsAdminPage.php
4 days ago
GoogleReviewsApify.php
4 days ago
GoogleReviewsManaged.php
4 days ago
GoogleReviewsRenderer.php
4 days ago
GoogleReviewsRestController.php
4 days ago
GoogleReviewsStore.php
4 days ago
Helper.php
4 days ago
Pdf_Thumbnail_Handler.php
2 months ago
PermalinkHelper.php
10 months ago
View_Count_Display.php
2 weeks ago
FeatureNotices.php
359 lines
| 1 | <?php |
| 2 | |
| 3 | namespace EmbedPress\Includes\Classes; |
| 4 | |
| 5 | /** |
| 6 | * Feature Notices Registration |
| 7 | * |
| 8 | * This file contains all feature notice registrations for EmbedPress. |
| 9 | * Add your feature notices here to keep them organized in one place. |
| 10 | * |
| 11 | * @package EmbedPress |
| 12 | * @since 4.1.0 |
| 13 | */ |
| 14 | |
| 15 | (defined('ABSPATH') && defined('EMBEDPRESS_IS_LOADED')) or die("No direct script access allowed."); |
| 16 | |
| 17 | class FeatureNotices |
| 18 | { |
| 19 | |
| 20 | /** |
| 21 | * Singleton instance |
| 22 | * @var FeatureNotices |
| 23 | */ |
| 24 | private static $instance = null; |
| 25 | |
| 26 | /** |
| 27 | * Get singleton instance |
| 28 | * |
| 29 | * @return FeatureNotices |
| 30 | */ |
| 31 | public static function get_instance() |
| 32 | { |
| 33 | if (self::$instance === null) { |
| 34 | self::$instance = new self(); |
| 35 | } |
| 36 | return self::$instance; |
| 37 | } |
| 38 | |
| 39 | /** |
| 40 | * Constructor |
| 41 | */ |
| 42 | private function __construct() |
| 43 | { |
| 44 | // Register all notices |
| 45 | add_action('init', [$this, 'register_all_notices']); |
| 46 | } |
| 47 | |
| 48 | /** |
| 49 | * Register all feature notices |
| 50 | * |
| 51 | * Add your feature notices here. Each notice should have a unique ID. |
| 52 | * |
| 53 | * @return void |
| 54 | */ |
| 55 | public function register_all_notices() |
| 56 | { |
| 57 | $notice_manager = FeatureNoticeManager::get_instance(); |
| 58 | |
| 59 | // ======================================== |
| 60 | // ACTIVE NOTICES |
| 61 | // ======================================== |
| 62 | // Note: Notices only show on Dashboard page (/wp-admin/index.php) |
| 63 | // Any action (Skip, Close, or Click Button) = Permanently dismiss |
| 64 | |
| 65 | // Analytics Dashboard Feature Notice |
| 66 | $notice_manager->register_notice('analytics_dashboard_2024', [ |
| 67 | 'title' => 'New Features', |
| 68 | 'icon' => '<svg width="11" height="13" viewBox="0 0 11 13" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 69 | <path d="M0 1.61956C1.26645e-05 1.53091 0.0177073 1.44315 0.0520487 1.36142C0.0863901 1.27968 0.136689 1.20562 0.2 1.14356C0.926563 0.431321 1.89723 0.0227172 2.91444 0.000919081C3.93164 -0.0208791 4.91893 0.345767 5.67533 1.02623L5.90933 1.2449C6.39562 1.67096 7.02013 1.90585 7.66667 1.90585C8.3132 1.90585 8.93771 1.67096 9.424 1.2449L9.59 1.09356C9.99667 0.771563 10.608 1.0289 10.6633 1.54423L10.6667 1.61956V7.61956C10.6667 7.70822 10.649 7.79598 10.6146 7.87771C10.5803 7.95944 10.53 8.03351 10.4667 8.09556C9.7401 8.8078 8.76943 9.21641 7.75223 9.23821C6.73502 9.26 5.74774 8.89336 4.99133 8.2129L4.75733 7.99423C4.28636 7.58157 3.68518 7.3478 3.05915 7.33391C2.43311 7.32001 1.82216 7.52687 1.33333 7.91823V12.2862C1.33314 12.4561 1.26808 12.6196 1.15143 12.7431C1.03479 12.8667 0.875365 12.9411 0.705737 12.951C0.536109 12.961 0.36908 12.9058 0.238778 12.7967C0.108476 12.6877 0.0247357 12.533 0.00466665 12.3642L0 12.2862V1.61956Z" fill="#25396F"/> |
| 70 | </svg>', |
| 71 | 'message' => '🥳 New In EmbedPress: Introducing, Analytics dashboard to track every embed performance; see total counts, views, clicks, geo insights, etc.', |
| 72 | 'button_text' => 'View Analytics', |
| 73 | 'button_url' => admin_url('admin.php?page=embedpress-analytics'), |
| 74 | 'skip_text' => 'Skip', |
| 75 | 'screens' => [], // Empty = show on all admin pages |
| 76 | 'capability' => 'manage_options', |
| 77 | 'start_date' => '2024-01-01', |
| 78 | 'end_date' => '2025-10-31', |
| 79 | 'priority' => 10, |
| 80 | // 'dismissible' => false, |
| 81 | 'type' => 'info', // info, success, warning, error |
| 82 | ]); |
| 83 | |
| 84 | // ======================================== |
| 85 | // EXAMPLE NOTICES (Commented Out) |
| 86 | // ======================================== |
| 87 | |
| 88 | /* |
| 89 | // Example: New Feature Announcement |
| 90 | $notice_manager->register_notice('new_feature_example', [ |
| 91 | 'title' => 'New Features', |
| 92 | 'icon' => '🚀', |
| 93 | 'message' => '<strong>Exciting Update!</strong> We\'ve added amazing new features to EmbedPress.', |
| 94 | 'button_text' => 'Learn More', |
| 95 | 'button_url' => 'https://embedpress.com/features', |
| 96 | 'button_target' => '_blank', |
| 97 | 'skip_text' => 'Maybe Later', |
| 98 | 'screens' => ['toplevel_page_embedpress'], // Show only on EmbedPress pages |
| 99 | 'capability' => 'manage_options', |
| 100 | 'priority' => 5, |
| 101 | 'type' => 'success', |
| 102 | ]); |
| 103 | */ |
| 104 | |
| 105 | /* |
| 106 | // Example: Limited Time Offer |
| 107 | $notice_manager->register_notice('black_friday_2024', [ |
| 108 | 'title' => 'Limited Time Offer', |
| 109 | 'icon' => '🎁', |
| 110 | 'message' => '<strong>Black Friday Sale!</strong> Get 50% off on EmbedPress Pro. Offer ends soon!', |
| 111 | 'button_text' => 'Claim Discount', |
| 112 | 'button_url' => 'https://embedpress.com/pricing', |
| 113 | 'button_target' => '_blank', |
| 114 | 'skip_text' => 'Not Interested', |
| 115 | 'start_date' => '2024-11-25', |
| 116 | 'end_date' => '2024-12-02', |
| 117 | 'priority' => 1, // Higher priority (lower number = shown first) |
| 118 | 'type' => 'warning', |
| 119 | ]); |
| 120 | */ |
| 121 | |
| 122 | /* |
| 123 | // Example: Important Update Notice |
| 124 | $notice_manager->register_notice('important_update_2024', [ |
| 125 | 'title' => 'Important Update', |
| 126 | 'icon' => '⚠️', |
| 127 | 'message' => '<strong>Action Required:</strong> Please update your settings to ensure compatibility with the latest version.', |
| 128 | 'button_text' => 'Update Settings', |
| 129 | 'button_url' => admin_url('admin.php?page=embedpress#/settings'), |
| 130 | 'skip_text' => 'Remind Me Later', |
| 131 | 'priority' => 1, |
| 132 | 'type' => 'warning', |
| 133 | ]); |
| 134 | */ |
| 135 | |
| 136 | /* |
| 137 | // Example: Success/Milestone Notice |
| 138 | $notice_manager->register_notice('milestone_100k', [ |
| 139 | 'title' => 'Thank You!', |
| 140 | 'icon' => '🎊', |
| 141 | 'message' => '<strong>We did it!</strong> EmbedPress has reached 100,000+ active installations. Thank you for being part of our journey!', |
| 142 | 'button_text' => 'Share Your Feedback', |
| 143 | 'button_url' => 'https://wordpress.org/support/plugin/embedpress/reviews/', |
| 144 | 'button_target' => '_blank', |
| 145 | 'skip_text' => 'Close', |
| 146 | 'start_date' => '2024-01-01', |
| 147 | 'end_date' => '2024-12-31', |
| 148 | 'priority' => 15, |
| 149 | 'type' => 'success', |
| 150 | ]); |
| 151 | */ |
| 152 | |
| 153 | // ======================================== |
| 154 | // "WHAT'S NEW" FEATURE PREVIEW MODAL |
| 155 | // ======================================== |
| 156 | // The big post-update split modal (preview + copy). One feature set |
| 157 | // per release; edit copy/CTA/media here without touching the renderer. |
| 158 | $this->register_feature_previews(); |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Register the post-update "What's New" feature preview modal sets. |
| 163 | * |
| 164 | * One entry per release. The modal fires once on the dashboard when the |
| 165 | * set's `version` is newer than the user's seen-version, then stamps the |
| 166 | * version so it never re-shows. Single feature => no nav; 2+ => carousel. |
| 167 | * |
| 168 | * Media `type` accepts: 'image', 'gif', 'video' (relative paths resolve |
| 169 | * against assets/images/), or 'html' for an inline CSS demo. |
| 170 | * |
| 171 | * @return void |
| 172 | */ |
| 173 | public function register_feature_previews() |
| 174 | { |
| 175 | if (!class_exists('\\EmbedPress\\Includes\\Classes\\FeaturePreviewModal')) { |
| 176 | return; |
| 177 | } |
| 178 | |
| 179 | $version = defined('EMBEDPRESS_VERSION') ? EMBEDPRESS_VERSION : '0.0.0'; |
| 180 | $modal = FeaturePreviewModal::get_instance(); |
| 181 | |
| 182 | // Feature slides for this release. Two slides → the modal auto-switches |
| 183 | // to a carousel (dots + Back/Next). Demos are inline HTML (no assets). |
| 184 | $features = [ |
| 185 | [ |
| 186 | 'eyebrow' => sprintf(__('New in %s', 'embedpress'), $version), |
| 187 | 'title' => __('Embed Google Business reviews', 'embedpress'), |
| 188 | 'desc' => __('Showcase your Google reviews anywhere — Gutenberg block, Elementor widget, or shortcode. Pick a place, choose a layout, and you’re live. No Google API keys required.', 'embedpress'), |
| 189 | 'media' => [ |
| 190 | 'type' => 'html', |
| 191 | 'html' => $this->preview_demo_google_reviews(), |
| 192 | ], |
| 193 | 'cta' => [ |
| 194 | 'label' => __('Set up Google Reviews →', 'embedpress'), |
| 195 | 'url' => admin_url('admin.php?page=embedpress-google-reviews'), |
| 196 | ], |
| 197 | ], |
| 198 | [ |
| 199 | 'eyebrow' => sprintf(__('New in %s', 'embedpress'), $version), |
| 200 | 'title' => __('Highlight links in 3D PDF Flipbooks', 'embedpress'), |
| 201 | 'desc' => __('Make hyperlinks inside your PDF 3D Flipbooks stand out, so readers never miss a clickable link.', 'embedpress'), |
| 202 | 'media' => [ |
| 203 | // Real product capture of link-highlighting in a 3D |
| 204 | // Flipbook, hosted on the CDN. Full URL passes through |
| 205 | // resolve_media_src() unchanged; preloaded in <head> and |
| 206 | // shown behind a shimmer while it downloads. |
| 207 | 'type' => 'gif', |
| 208 | 'src' => 'https://cloud.zoobbe.com/backgrounds/pdf-3d-flipbook-d.gif', |
| 209 | ], |
| 210 | 'cta' => [ |
| 211 | 'label' => __('View documentation →', 'embedpress'), |
| 212 | 'url' => 'https://embedpress.com/docs/turn-embedded-pdf-into-a-3d-flip-book/', |
| 213 | 'external' => true, |
| 214 | ], |
| 215 | ], |
| 216 | ]; |
| 217 | |
| 218 | // 4.6.0 release set. Versioned id so a fresh release is a fresh |
| 219 | // announcement. Demos are inline, on-brand HTML (no asset files). |
| 220 | $modal->register('whatsnew_' . str_replace('.', '_', $version), [ |
| 221 | 'version' => $version, |
| 222 | // Master switch for THIS release's modal. Set to false to ship the |
| 223 | // release without the modal (e.g. a refactor-only version), or pass |
| 224 | // a callable for conditional gating, e.g.: |
| 225 | // 'show_if' => function () { return ! function_exists('embedpress_pro_load'); }, // non-Pro only |
| 226 | 'show_if' => true, |
| 227 | 'changelog_url' => 'https://embedpress.com/changelog/', |
| 228 | // When usage tracking is NOT yet enabled, the bottom link becomes |
| 229 | // "What we collect" → the privacy policy (replacing the changelog |
| 230 | // link). Users who already opted in keep the normal changelog link. |
| 231 | 'whatwe_collect_url' => FeaturePreviewModal::is_tracking_enabled() |
| 232 | ? '' |
| 233 | : 'https://wpdeveloper.com/privacy-policy', |
| 234 | 'features' => $features, |
| 235 | ]); |
| 236 | } |
| 237 | |
| 238 | /** |
| 239 | * Inline placeholder demo for the sample feature — a clean, on-brand mock |
| 240 | * of the "Display Rules" control (role / login / schedule toggles). |
| 241 | * |
| 242 | * Uses EmbedPress brand colors only: brand purple #5945b0 (+ #7c3aed |
| 243 | * gradient), success green #00cc76 for the "on" schedule toggle, and the |
| 244 | * dark preview-panel tones. Real releases should swap this for a screenshot |
| 245 | * or GIF via 'media' => ['type' => 'image', 'src' => '…']. |
| 246 | * |
| 247 | * @return string |
| 248 | */ |
| 249 | private function preview_demo_display_rules() |
| 250 | { |
| 251 | $row = function ($on, $label, $color, $pill = '') { |
| 252 | $knob = $on |
| 253 | ? 'right:2px' |
| 254 | : 'left:2px'; |
| 255 | $bg = $on ? $color : '#3a3550'; |
| 256 | $pill_html = $pill |
| 257 | ? '<span style="margin-left:auto;font-size:11px;font-weight:600;color:#9a8be0;background:rgba(89,69,176,.22);padding:3px 9px;border-radius:999px">' . esc_html($pill) . '</span>' |
| 258 | : ''; |
| 259 | return '<div style="display:flex;align-items:center;gap:10px;background:#1b1928;border:1px solid #2a2740;border-radius:10px;padding:11px 13px;margin-bottom:9px">' |
| 260 | . '<span style="width:30px;height:17px;border-radius:9px;background:' . $bg . ';position:relative;flex-shrink:0"><span style="position:absolute;top:2px;' . $knob . ';width:13px;height:13px;border-radius:50%;background:#fff"></span></span>' |
| 261 | . '<span style="font-size:12px;color:#dfe3f0">' . esc_html($label) . '</span>' |
| 262 | . $pill_html |
| 263 | . '</div>'; |
| 264 | }; |
| 265 | |
| 266 | return '<div style="width:262px;font-family:inherit">' |
| 267 | . '<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;color:#c7cce0;font-size:12px;font-weight:600">' |
| 268 | . '<span style="width:18px;height:18px;border-radius:5px;background:#5945b0;display:inline-flex;align-items:center;justify-content:center;color:#fff;font-size:10px">✦</span>' |
| 269 | . esc_html__('Display Rules', 'embedpress') . '</div>' |
| 270 | . $row(true, __('User role', 'embedpress'), '#5945b0', __('Members', 'embedpress')) |
| 271 | . $row(false, __('Logged-in only', 'embedpress'), '#5945b0') |
| 272 | . $row(true, __('Schedule by date', 'embedpress'), '#00cc76') |
| 273 | . '</div>'; |
| 274 | } |
| 275 | |
| 276 | /** |
| 277 | * Animated, LIGHT demo for the Google Reviews feature — a 3-step loop that |
| 278 | * tells the feature story: (1) search for a place, (2) click the result, |
| 279 | * (3) the reviews animate in. The keyframe animations live in |
| 280 | * static/css/feature-preview-modal.css (.ep-grdemo-* classes) because the |
| 281 | * modal runs this markup through wp_kses_post, which strips <style>; class |
| 282 | * attributes survive, so the CSS does the motion. |
| 283 | * |
| 284 | * @return string |
| 285 | */ |
| 286 | private function preview_demo_google_reviews() |
| 287 | { |
| 288 | $stars = '<span class="ep-grdemo-stars">' |
| 289 | . str_repeat('<span>★</span>', 5) . '</span>'; |
| 290 | |
| 291 | // The same icons our place picker uses (GoogleReviews.jsx). Inline SVG |
| 292 | // survives because the modal allows a small SVG tag set for plugin-built |
| 293 | // demo markup (FeaturePreviewModal::demo_allowed_html()). |
| 294 | $search_icon = '<svg class="ep-grdemo-search-ico" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>'; |
| 295 | $pin_icon = '<svg class="ep-grdemo-pin" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>'; |
| 296 | |
| 297 | // Per-card stagger is done via CSS (adjacent-sibling chains) — NOT an |
| 298 | // inline animation-delay, which kses strips. |
| 299 | $review = function ($initial, $name, $text) use ($stars) { |
| 300 | return '<div class="ep-grdemo-review">' |
| 301 | . '<div class="ep-grdemo-review-head">' |
| 302 | . '<span class="ep-grdemo-avatar">' . esc_html($initial) . '</span>' |
| 303 | . '<span class="ep-grdemo-name">' . esc_html($name) . '</span>' |
| 304 | . $stars |
| 305 | . '</div>' |
| 306 | . '<div class="ep-grdemo-text">' . esc_html($text) . '</div>' |
| 307 | . '</div>'; |
| 308 | }; |
| 309 | |
| 310 | return '<div class="ep-grdemo">' |
| 311 | // ── Phase A: our place-search picker — input (text types in) + |
| 312 | // the suggestion dropdown that gets clicked. Mirrors the real |
| 313 | // .ep-gr-search / .ep-gr-suggestion markup. ── |
| 314 | . '<div class="ep-grdemo-phase ep-grdemo-phase--search">' |
| 315 | . '<div class="ep-grdemo-label">' . esc_html__('Search for a place', 'embedpress') . '</div>' |
| 316 | . '<div class="ep-grdemo-search">' |
| 317 | . $search_icon |
| 318 | . '<span class="ep-grdemo-typed">' . esc_html__('Sydney Opera House', 'embedpress') . '</span>' |
| 319 | . '<span class="ep-grdemo-caret"></span>' |
| 320 | . '</div>' |
| 321 | . '<div class="ep-grdemo-suggest">' |
| 322 | . '<div class="ep-grdemo-result">' |
| 323 | . $pin_icon |
| 324 | . '<span class="ep-grdemo-result-text"><strong>' . esc_html__('Sydney Opera House', 'embedpress') . '</strong>' |
| 325 | . '<small>' . esc_html__('Bennelong Point, Sydney NSW, Australia', 'embedpress') . '</small></span>' |
| 326 | . '<span class="ep-grdemo-cursor"></span>' |
| 327 | . '</div>' |
| 328 | . '</div>' |
| 329 | . '</div>' |
| 330 | // ── Phase B: the reviews our block renders — summary + cards. ── |
| 331 | . '<div class="ep-grdemo-phase ep-grdemo-phase--reviews">' |
| 332 | . '<div class="ep-grdemo-summary"><b>4.8</b>' . $stars |
| 333 | . '<span class="ep-grdemo-count">2,341 ' . esc_html__('reviews', 'embedpress') . '</span></div>' |
| 334 | . $review('A', __('Aisha K.', 'embedpress'), __('Breathtaking venue and a flawless experience — the guided tour was the trip highlight.', 'embedpress')) |
| 335 | . $review('M', __('Marco T.', 'embedpress'), __('A must-see. The architecture is stunning from every angle, day or night.', 'embedpress')) |
| 336 | . $review('L', __('Liam P.', 'embedpress'), __('Incredible acoustics and a stunning harbour view — booked tickets for the evening show.', 'embedpress')) |
| 337 | . '</div>' |
| 338 | . '</div>'; |
| 339 | } |
| 340 | |
| 341 | /** |
| 342 | * Helper method to get admin URL for EmbedPress pages |
| 343 | * |
| 344 | * @param string $page_type The page type (e.g., 'analytics', 'settings') |
| 345 | * @return string |
| 346 | */ |
| 347 | private function get_embedpress_url($page_type = '') |
| 348 | { |
| 349 | $url = admin_url('admin.php?page=embedpress'); |
| 350 | if (!empty($page_type)) { |
| 351 | $url .= '&page_type=' . $page_type; |
| 352 | } |
| 353 | return $url; |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | // Initialize the class |
| 358 | FeatureNotices::get_instance(); |
| 359 |