Analytics
1 month 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
3 weeks ago
FeatureNotices.php
4 days ago
FeaturePreviewModal.php
3 weeks ago
Feature_Enhancer.php
1 month ago
GoogleReviewsAdminPage.php
3 weeks ago
GoogleReviewsApify.php
3 weeks ago
GoogleReviewsManaged.php
3 weeks ago
GoogleReviewsRenderer.php
3 weeks ago
GoogleReviewsRestController.php
3 weeks ago
GoogleReviewsStore.php
3 weeks ago
Helper.php
1 week ago
Pdf_Thumbnail_Handler.php
3 months ago
PermalinkHelper.php
11 months ago
View_Count_Display.php
1 month ago
FeatureNotices.php
384 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 | // Which releases actually announce something. The modal is opt-IN per |
| 183 | // version: a release only shows it when its version is listed here AND |
| 184 | // the $features array below describes that release. |
| 185 | // |
| 186 | // Why an allowlist instead of a bare `'show_if' => true`: the feature |
| 187 | // slides are hand-written per release, so a patch that ships no new |
| 188 | // features (4.6.1, 4.6.2 — bug fixes and security hardening) would |
| 189 | // otherwise re-announce the PREVIOUS release's copy under a new version |
| 190 | // number. Defaulting to "off" makes the safe case automatic and the |
| 191 | // announcement deliberate. |
| 192 | // |
| 193 | // To announce a new release: rewrite $features for it, then add its |
| 194 | // version here. Patch releases with no user-facing features: add |
| 195 | // nothing — they stay silent, along with the menu "New" badge. |
| 196 | $announced_versions = [ |
| 197 | '4.6.0', // Google Reviews + PDF flipbook Highlight Links |
| 198 | ]; |
| 199 | |
| 200 | $is_announced = in_array($version, $announced_versions, true); |
| 201 | |
| 202 | // Feature slides for this release. Two slides → the modal auto-switches |
| 203 | // to a carousel (dots + Back/Next). Demos are inline HTML (no assets). |
| 204 | $features = [ |
| 205 | [ |
| 206 | 'eyebrow' => sprintf(__('New in %s', 'embedpress'), $version), |
| 207 | 'title' => __('Embed Google Business reviews', 'embedpress'), |
| 208 | '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'), |
| 209 | 'media' => [ |
| 210 | 'type' => 'html', |
| 211 | 'html' => $this->preview_demo_google_reviews(), |
| 212 | ], |
| 213 | 'cta' => [ |
| 214 | 'label' => __('Set up Google Reviews →', 'embedpress'), |
| 215 | 'url' => admin_url('admin.php?page=embedpress-google-reviews'), |
| 216 | ], |
| 217 | ], |
| 218 | [ |
| 219 | 'eyebrow' => sprintf(__('New in %s', 'embedpress'), $version), |
| 220 | 'title' => __('Highlight links in 3D PDF Flipbooks', 'embedpress'), |
| 221 | 'desc' => __('Make hyperlinks inside your PDF 3D Flipbooks stand out, so readers never miss a clickable link.', 'embedpress'), |
| 222 | 'media' => [ |
| 223 | // Real product capture of link-highlighting in a 3D |
| 224 | // Flipbook, hosted on the CDN. Full URL passes through |
| 225 | // resolve_media_src() unchanged; preloaded in <head> and |
| 226 | // shown behind a shimmer while it downloads. |
| 227 | 'type' => 'gif', |
| 228 | 'src' => 'https://cloud.zoobbe.com/backgrounds/pdf-3d-flipbook-d.gif', |
| 229 | ], |
| 230 | 'cta' => [ |
| 231 | 'label' => __('View documentation →', 'embedpress'), |
| 232 | 'url' => 'https://embedpress.com/docs/turn-embedded-pdf-into-a-3d-flip-book/', |
| 233 | 'external' => true, |
| 234 | ], |
| 235 | ], |
| 236 | ]; |
| 237 | |
| 238 | // 4.6.0 release set. Versioned id so a fresh release is a fresh |
| 239 | // announcement. Demos are inline, on-brand HTML (no asset files). |
| 240 | $modal->register('whatsnew_' . str_replace('.', '_', $version), [ |
| 241 | 'version' => $version, |
| 242 | // Master switch for THIS release's modal. Set to false to ship the |
| 243 | // release without the modal (e.g. a refactor-only version), or pass |
| 244 | // a callable for conditional gating, e.g.: |
| 245 | // 'show_if' => function () { return ! function_exists('embedpress_pro_load'); }, // non-Pro only |
| 246 | // |
| 247 | // Driven by the $announced_versions allowlist above — true only for |
| 248 | // releases that actually ship user-facing features. Bug-fix and |
| 249 | // security patches (4.6.1, 4.6.2) resolve to false, so both the |
| 250 | // modal and the menu "New" badge stay silent. |
| 251 | 'show_if' => $is_announced, |
| 252 | 'changelog_url' => 'https://embedpress.com/changelog/', |
| 253 | // When usage tracking is NOT yet enabled, the bottom link becomes |
| 254 | // "What we collect" → the privacy policy (replacing the changelog |
| 255 | // link). Users who already opted in keep the normal changelog link. |
| 256 | 'whatwe_collect_url' => FeaturePreviewModal::is_tracking_enabled() |
| 257 | ? '' |
| 258 | : 'https://wpdeveloper.com/privacy-policy', |
| 259 | 'features' => $features, |
| 260 | ]); |
| 261 | } |
| 262 | |
| 263 | /** |
| 264 | * Inline placeholder demo for the sample feature — a clean, on-brand mock |
| 265 | * of the "Display Rules" control (role / login / schedule toggles). |
| 266 | * |
| 267 | * Uses EmbedPress brand colors only: brand purple #5945b0 (+ #7c3aed |
| 268 | * gradient), success green #00cc76 for the "on" schedule toggle, and the |
| 269 | * dark preview-panel tones. Real releases should swap this for a screenshot |
| 270 | * or GIF via 'media' => ['type' => 'image', 'src' => '…']. |
| 271 | * |
| 272 | * @return string |
| 273 | */ |
| 274 | private function preview_demo_display_rules() |
| 275 | { |
| 276 | $row = function ($on, $label, $color, $pill = '') { |
| 277 | $knob = $on |
| 278 | ? 'right:2px' |
| 279 | : 'left:2px'; |
| 280 | $bg = $on ? $color : '#3a3550'; |
| 281 | $pill_html = $pill |
| 282 | ? '<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>' |
| 283 | : ''; |
| 284 | 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">' |
| 285 | . '<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>' |
| 286 | . '<span style="font-size:12px;color:#dfe3f0">' . esc_html($label) . '</span>' |
| 287 | . $pill_html |
| 288 | . '</div>'; |
| 289 | }; |
| 290 | |
| 291 | return '<div style="width:262px;font-family:inherit">' |
| 292 | . '<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;color:#c7cce0;font-size:12px;font-weight:600">' |
| 293 | . '<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>' |
| 294 | . esc_html__('Display Rules', 'embedpress') . '</div>' |
| 295 | . $row(true, __('User role', 'embedpress'), '#5945b0', __('Members', 'embedpress')) |
| 296 | . $row(false, __('Logged-in only', 'embedpress'), '#5945b0') |
| 297 | . $row(true, __('Schedule by date', 'embedpress'), '#00cc76') |
| 298 | . '</div>'; |
| 299 | } |
| 300 | |
| 301 | /** |
| 302 | * Animated, LIGHT demo for the Google Reviews feature — a 3-step loop that |
| 303 | * tells the feature story: (1) search for a place, (2) click the result, |
| 304 | * (3) the reviews animate in. The keyframe animations live in |
| 305 | * static/css/feature-preview-modal.css (.ep-grdemo-* classes) because the |
| 306 | * modal runs this markup through wp_kses_post, which strips <style>; class |
| 307 | * attributes survive, so the CSS does the motion. |
| 308 | * |
| 309 | * @return string |
| 310 | */ |
| 311 | private function preview_demo_google_reviews() |
| 312 | { |
| 313 | $stars = '<span class="ep-grdemo-stars">' |
| 314 | . str_repeat('<span>★</span>', 5) . '</span>'; |
| 315 | |
| 316 | // The same icons our place picker uses (GoogleReviews.jsx). Inline SVG |
| 317 | // survives because the modal allows a small SVG tag set for plugin-built |
| 318 | // demo markup (FeaturePreviewModal::demo_allowed_html()). |
| 319 | $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>'; |
| 320 | $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>'; |
| 321 | |
| 322 | // Per-card stagger is done via CSS (adjacent-sibling chains) — NOT an |
| 323 | // inline animation-delay, which kses strips. |
| 324 | $review = function ($initial, $name, $text) use ($stars) { |
| 325 | return '<div class="ep-grdemo-review">' |
| 326 | . '<div class="ep-grdemo-review-head">' |
| 327 | . '<span class="ep-grdemo-avatar">' . esc_html($initial) . '</span>' |
| 328 | . '<span class="ep-grdemo-name">' . esc_html($name) . '</span>' |
| 329 | . $stars |
| 330 | . '</div>' |
| 331 | . '<div class="ep-grdemo-text">' . esc_html($text) . '</div>' |
| 332 | . '</div>'; |
| 333 | }; |
| 334 | |
| 335 | return '<div class="ep-grdemo">' |
| 336 | // ── Phase A: our place-search picker — input (text types in) + |
| 337 | // the suggestion dropdown that gets clicked. Mirrors the real |
| 338 | // .ep-gr-search / .ep-gr-suggestion markup. ── |
| 339 | . '<div class="ep-grdemo-phase ep-grdemo-phase--search">' |
| 340 | . '<div class="ep-grdemo-label">' . esc_html__('Search for a place', 'embedpress') . '</div>' |
| 341 | . '<div class="ep-grdemo-search">' |
| 342 | . $search_icon |
| 343 | . '<span class="ep-grdemo-typed">' . esc_html__('Sydney Opera House', 'embedpress') . '</span>' |
| 344 | . '<span class="ep-grdemo-caret"></span>' |
| 345 | . '</div>' |
| 346 | . '<div class="ep-grdemo-suggest">' |
| 347 | . '<div class="ep-grdemo-result">' |
| 348 | . $pin_icon |
| 349 | . '<span class="ep-grdemo-result-text"><strong>' . esc_html__('Sydney Opera House', 'embedpress') . '</strong>' |
| 350 | . '<small>' . esc_html__('Bennelong Point, Sydney NSW, Australia', 'embedpress') . '</small></span>' |
| 351 | . '<span class="ep-grdemo-cursor"></span>' |
| 352 | . '</div>' |
| 353 | . '</div>' |
| 354 | . '</div>' |
| 355 | // ── Phase B: the reviews our block renders — summary + cards. ── |
| 356 | . '<div class="ep-grdemo-phase ep-grdemo-phase--reviews">' |
| 357 | . '<div class="ep-grdemo-summary"><b>4.8</b>' . $stars |
| 358 | . '<span class="ep-grdemo-count">2,341 ' . esc_html__('reviews', 'embedpress') . '</span></div>' |
| 359 | . $review('A', __('Aisha K.', 'embedpress'), __('Breathtaking venue and a flawless experience — the guided tour was the trip highlight.', 'embedpress')) |
| 360 | . $review('M', __('Marco T.', 'embedpress'), __('A must-see. The architecture is stunning from every angle, day or night.', 'embedpress')) |
| 361 | . $review('L', __('Liam P.', 'embedpress'), __('Incredible acoustics and a stunning harbour view — booked tickets for the evening show.', 'embedpress')) |
| 362 | . '</div>' |
| 363 | . '</div>'; |
| 364 | } |
| 365 | |
| 366 | /** |
| 367 | * Helper method to get admin URL for EmbedPress pages |
| 368 | * |
| 369 | * @param string $page_type The page type (e.g., 'analytics', 'settings') |
| 370 | * @return string |
| 371 | */ |
| 372 | private function get_embedpress_url($page_type = '') |
| 373 | { |
| 374 | $url = admin_url('admin.php?page=embedpress'); |
| 375 | if (!empty($page_type)) { |
| 376 | $url .= '&page_type=' . $page_type; |
| 377 | } |
| 378 | return $url; |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | // Initialize the class |
| 383 | FeatureNotices::get_instance(); |
| 384 |