PluginProbe
Extendify / 3.1.0
Extendify v3.1.0
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / app / Insights.php

Insights.php in Extendify 3.1.0, at app/Insights.php

229 lines 7.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Insights setup
5 */
6
7 namespace Extendify;
8
9 defined('ABSPATH') || die('No direct access.');
10
11 use Extendify\Shared\Services\Sanitizer;
12 use Extendify\PartnerData;
13
14 /**
15 * Controller for handling various Insights related things.
16 * WP code reviewers: This is used in another plugin and not invoked here.
17 */
18
19 class Insights
20 {
21 /**
22 * Option name storing each site's A/B test assignments, keyed by the
23 * screen/feature under test (e.g. 'AutoLaunch.WebsiteTitle').
24 *
25 * @var string
26 */
27 // phpcs:ignore PSR12.Properties.ConstantVisibility.NotFound
28 const ACTIVE_TESTS_OPTION = 'extendify_active_tests';
29
30 /**
31 * Tests the plugin knows how to run, each mapped to its available variants.
32 *
33 * @var array<string, string[]>
34 */
35 // phpcs:ignore PSR12.Properties.ConstantVisibility.NotFound
36 const ACTIVE_TESTS = [
37 'AutoLaunch.WebsiteTitle' => ['A', 'B'],
38 ];
39
40 /**
41 * Process the readme file to get version and name
42 *
43 * @return void
44 */
45 public function __construct()
46 {
47 // If there isn't a siteId, then create one.
48 if (!\get_option('extendify_site_id', false)) {
49 \update_option('extendify_site_id', \wp_generate_uuid4());
50 }
51
52 if (
53 defined('EXTENDIFY_INSIGHTS_URL')
54 && class_exists('ExtendifyInsights')
55 && !\get_option('extendify_insights_checkedin_once', 0)
56 ) {
57 \update_option('extendify_insights_checkedin_once', gmdate('Y-m-d H:i:s'));
58 // WP code reviewers: This job is defined in another plugin (i.e. it's opt-in).
59 \add_action('init', function () {
60 // Run this once but wait 10 minutes.
61 \wp_schedule_single_event((time() + 10 * MINUTE_IN_SECONDS), 'extendify_insights');
62 \spawn_cron();
63 });
64 }
65
66 $this->filterExternalInsights();
67 $this->setupAdminLoginInsights();
68 }
69
70 /**
71 * Assign A/B variants for the known tests based on the partner's active
72 * tests. Each active test is rolled once and kept on return visits;
73 * inactive tests are dropped.
74 *
75 * @param string[] $activeTests Test keys the partner has enabled.
76 * @return void
77 */
78 public static function setup(array $activeTests = [])
79 {
80 $assignments = \get_option(self::ACTIVE_TESTS_OPTION, []);
81
82 foreach (self::ACTIVE_TESTS as $key => $variants) {
83 if (!in_array($key, $activeTests, true)) {
84 unset($assignments[$key]);
85 continue;
86 }
87
88 // Roll once so a returning visitor keeps the same variant.
89 if (!isset($assignments[$key])) {
90 $assignments[$key] = $variants[random_int(0, count($variants) - 1)];
91 }
92 }
93
94 \update_option(self::ACTIVE_TESTS_OPTION, Sanitizer::sanitizeArray($assignments));
95 }
96
97 /**
98 * Add additional data to the opt-in insights
99 *
100 * @return void
101 */
102 public function filterExternalInsights()
103 {
104 add_filter('extendify_insights_data', function ($data) {
105 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
106 $readme = file_get_contents(EXTENDIFY_PATH . 'readme.txt');
107 preg_match('/Stable tag: ([0-9.:]+)/', $readme, $version);
108
109 $insights = array_merge($data, [
110 'launch' => Config::$showLaunch,
111 'launchRedirectedAt' => \get_option('extendify_attempted_redirect', null),
112 'launchLoadedAt' => \get_option('extendify_launch_loaded', null),
113 'partner' => defined('EXTENDIFY_PARTNER_ID') ? constant('EXTENDIFY_PARTNER_ID') : null,
114 'siteCreatedAt' => SiteSettings::getSiteCreatedAt(),
115 'assistRouterData' => \get_option('extendify_assist_router', null),
116 'libraryData' => \get_option('extendify_library_site_data', null),
117 'draftSettingsData' => \get_option('extendify_draft_settings', null),
118 'activity' => \get_option('extendify_shared_activity', null),
119 'domainsActivities' => \get_option('extendify_domains_recommendations_activities', null),
120 'extendifyVersion' => ($version[1] ?? null),
121 'siteProfile' => \get_option('extendify_site_profile', null),
122 'pluginSearchTerms' => \get_option('extendify_plugin_search_terms', []),
123 'blockSearchTerms' => \get_option('extendify_block_search_terms', []),
124 'phpVersion' => PHP_VERSION,
125 'themeSearchTerms' => \get_option('extendify_theme_search_terms', []),
126 'license' => PartnerData::setting('license'),
127 'pagesCount' => $this->getPostsCount('page'),
128 'postsCount' => $this->getPostsCount('post'),
129 'lastUpdatedPage' => $this->getLastUpdatedPost('page'),
130 'lastUpdatedPost' => $this->getLastUpdatedPost('post'),
131 'lastLoginAdmin' => $this->getLastAdminLogin(),
132 'hasImprint' => $this->hasImprint(),
133 ]);
134 return $insights;
135 });
136 }
137
138 /**
139 * Get the number of posts/pages
140 *
141 * @param string $type The type of post/page to get the count for (post or page)
142 * @return int The number of posts/pages
143 */
144 protected function getPostsCount($type = 'post')
145 {
146 $count = wp_count_posts($type);
147 return isset($count->publish) ? (int) $count->publish : 0;
148 }
149
150 /**
151 * Set up admin login insights to monitor when admin users log in
152 *
153 * @return void
154 */
155 protected function setupAdminLoginInsights()
156 {
157 add_action('wp_login', function ($user_login, $user) {
158 // Only get insights for admin users
159 if (user_can($user, 'manage_options')) {
160 update_user_meta($user->ID, 'extendify_last_login', gmdate('Y-m-d H:i:s'));
161 }
162 }, 10, 2);
163 }
164
165 /**
166 * Get the last time a post/page was updated
167 *
168 * @return string|null The last updated post timestamp or null if no posts found
169 */
170 protected function getLastUpdatedPost($type = 'post')
171 {
172 $posts = get_posts([
173 'post_type' => $type,
174 'post_status' => 'publish',
175 'orderby' => 'modified',
176 'order' => 'DESC',
177 'numberposts' => 1,
178 'fields' => 'ids'
179 ]);
180
181 if (!empty($posts)) {
182 $post = get_post($posts[0]);
183 return $post ? $post->post_modified : null;
184 }
185 return null;
186 }
187
188 /**
189 * Get the last time an admin user logged in
190 *
191 * @return string|null The most recent admin login timestamp or null if no data found
192 */
193 protected function getLastAdminLogin()
194 {
195 $admins = get_users([
196 'role' => 'administrator',
197 'meta_key' => 'extendify_last_login',
198 'orderby' => 'meta_value',
199 'order' => 'DESC',
200 'number' => 1
201 ]);
202
203 if (!empty($admins)) {
204 return get_user_meta($admins[0]->ID, 'extendify_last_login', true);
205 }
206 return null;
207 }
208
209 /**
210 * Check if the site has an imprint based on the site profile and language settings
211 *
212 * @return bool True if the site has an imprint, false otherwise
213 */
214 protected function hasImprint()
215 {
216 $siteProfile = \get_option('extendify_site_profile', []);
217 if (empty($siteProfile)) {
218 return false;
219 }
220
221 $imprintLanguages = array_filter(PartnerData::setting('showImprint') ?? [], function ($value) {
222 return $value === get_locale();
223 });
224
225 return !empty($imprintLanguages) && (strtolower($siteProfile['aiSiteCategory']) === 'business' ||
226 strtolower($siteProfile['category']) === 'business');
227 }
228 }
229