PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.8.0
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.8.0
3.8.0 3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 All 112 releases
templately / modules / post-import-feedback / Widget.php

Widget.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! 3.8.0, at modules/post-import-feedback/Widget.php

126 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Post-import feedback widget (spec 027-post-import-feedback).
4 *
5 * Extracted verbatim from includes/Core/Admin.php — behavior byte-identical (same
6 * eligibility check, same enqueue handle/localize global-var name `templately` so
7 * the widget's `window.templately` read is unaffected, same mount-point markup).
8 * Two pieces were surgically lifted out of a larger class:
9 * - the `'index.php' === $hook` early-return branch inside Admin::scripts()
10 * (hooked here to admin_enqueue_scripts independently)
11 * - Admin::my_custom_footer_html() (hooked here to admin_footer independently)
12 * Neither block referenced `$this->` state from Admin.php — confirmed via grep
13 * before extraction, so this is a clean lift, not a rewrite. The enqueued filenames
14 * changed (js/dashboard.js -> js/post-import-feedback.js, css/dashboard-style.css ->
15 * css/post-import-feedback-style.css) to match the renamed webpack entry (spec 027's
16 * own Build Artifacts note); the WordPress script/style HANDLE and the localized JS
17 * global variable name both stay 'templately' — react-src's widget entry reads
18 * `window.templately` directly, so renaming either would break data delivery.
19 *
20 * FR-005 rolling 30-day cooldown (2026-07-16): eligibility used to be a one-shot
21 * `$is_complete !== 'done'` check (no expiry once dismissed). It is now
22 * time-boxed via `is_eligible()` — see that method's docblock for the migration
23 * handling of the old no-expiry `'done'` sentinel.
24 *
25 * @package Templately
26 */
27
28 namespace Templately\Modules\PostImportFeedback;
29
30 use Templately\Utils\Options;
31
32 class Widget {
33
34 /**
35 * User-meta key holding the Unix timestamp of the last time this user
36 * submitted or dismissed the feedback widget. Drives the FR-005 30-day
37 * rolling cooldown, replacing the old no-expiry `'done'` sentinel
38 * previously stored (and still legacy-read) in `templately_fsi_complete`.
39 */
40 const LAST_SHOWN_META = 'templately_feedback_last_shown';
41
42 /** FR-005 cooldown window: 30 days. */
43 const COOLDOWN_SECONDS = 30 * DAY_IN_SECONDS;
44
45 public function enqueue( $hook = '' ) {
46 if ( 'index.php' !== $hook ) {
47 return;
48 }
49
50 if ( ! self::is_eligible() || wp_is_mobile() ) {
51 return;
52 }
53
54 $user = Options::get_instance()->get( 'user' );
55 $email = isset( $user['email'] ) ? $user['email'] : '';
56
57 templately()->assets->enqueue( 'templately', 'css/post-import-feedback-style.css', [] );
58 templately()->assets->enqueue( 'templately', 'js/post-import-feedback.js', [], true );
59 templately()->assets->localize( 'templately', 'templately', [
60 'email' => $email,
61 'nonce' => wp_create_nonce( 'templately_nonce' ),
62 ] );
63 }
64
65 public function render_mount_point() {
66 global $current_screen;
67
68 if ( ! $current_screen ) {
69 return false;
70 }
71
72 if ( $current_screen->id !== 'dashboard' ) {
73 return false;
74 }
75
76 if ( self::is_eligible() && ! wp_is_mobile() ) :
77 ?>
78 <div id="templately-fsi-feedback"></div>
79 <?php
80 endif;
81 }
82
83 /**
84 * Whether the feedback widget should be shown to the given (or current)
85 * user, per FR-005: suppressed while the user has submitted or skipped
86 * feedback within the past 30 days; eligible again once that window
87 * elapses. Requires `templately_fsi_complete` (an FSI has completed) to
88 * be truthy at all — an entirely fresh install/user is never eligible.
89 *
90 * Migration handling for the old no-expiry `'done'` sentinel: on
91 * installs from before this cooldown existed, `templately_fsi_complete`
92 * may already be `'done'` with no `LAST_SHOWN_META` recorded. The same
93 * shape also occurs going forward for a legitimate reason outside this
94 * module's scope — `FullSiteImport\Concerns\RunsImport` writes the
95 * `'done'` sentinel directly (not via this module) whenever the backend
96 * reports feedback was already given for the pack. Both cases are
97 * indistinguishable from here, so both are handled the same,
98 * conservatively: do NOT immediately re-prompt (a bare "value isn't a
99 * timestamp" read should never suddenly show the widget to someone who
100 * already dismissed it) — treat it as "just shown", backfilling
101 * `LAST_SHOWN_META` to now so the 30-day clock starts running instead of
102 * suppressing forever.
103 */
104 public static function is_eligible( $user_id = null ): bool {
105 $user_id = $user_id ?: get_current_user_id();
106
107 $is_complete = get_user_meta( $user_id, 'templately_fsi_complete', true );
108 if ( empty( $is_complete ) ) {
109 return false;
110 }
111
112 $last_shown = get_user_meta( $user_id, self::LAST_SHOWN_META, true );
113
114 if ( 'done' === $is_complete && empty( $last_shown ) ) {
115 update_user_meta( $user_id, self::LAST_SHOWN_META, time() );
116 return false;
117 }
118
119 if ( empty( $last_shown ) ) {
120 return true;
121 }
122
123 return ( time() - (int) $last_shown ) >= self::COOLDOWN_SECONDS;
124 }
125 }
126