PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.13
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.13
1.1.13 1.1.12 1.1.11 1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 All 37 releases
desktop-mode / includes / living-tree / helpers.php

helpers.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.13, at includes/living-tree/helpers.php

312 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — Living Tree: metric helpers.
4 *
5 * The scalar signals the snapshot builder folds into the site's DNA.
6 * Each helper composes existing WordPress aggregates (`wp_count_posts`,
7 * `wp_count_comments`, `wp_count_terms`), the site-views traffic signal,
8 * and framework presence — never a per-row payload. The golden rule
9 * (WordPress emits hormones, never geometry) starts here: everything
10 * returned is a scalar or a tiny capped list.
11 *
12 * @package OpenStation
13 */
14
15 defined( 'ABSPATH' ) || exit;
16
17 /**
18 * The site's inception moment as a unix timestamp — the stable half of
19 * the determinism seed (`siteUrl|installEpoch`), so it must never drift
20 * between requests. Composed (core has no first-party "install time"):
21 * the earlier of the oldest user registration and the oldest published
22 * post date.
23 *
24 * @return int Unix timestamp, or 0 when the site has neither.
25 */
26 function openstation_living_tree_install_epoch() {
27 global $wpdb;
28
29 $oldest_user = $wpdb->get_var(
30 "SELECT MIN( user_registered ) FROM {$wpdb->users}"
31 );
32 $oldest_post = $wpdb->get_var(
33 "SELECT MIN( post_date_gmt ) FROM {$wpdb->posts}
34 WHERE post_status = 'publish' AND post_date_gmt > '1970-01-01 00:00:01'"
35 );
36
37 $candidates = array();
38 foreach ( array( $oldest_user, $oldest_post ) as $mysql_date ) {
39 if ( $mysql_date ) {
40 $ts = strtotime( $mysql_date . ' UTC' );
41 if ( $ts > 0 ) {
42 $candidates[] = $ts;
43 }
44 }
45 }
46
47 return empty( $candidates ) ? 0 : min( $candidates );
48 }
49
50 /**
51 * Age of the site in whole days, from the install epoch. Clamped to be
52 * non-negative — the master clock never runs backwards.
53 *
54 * @return int Whole days since the site's inception. >= 0.
55 */
56 function openstation_living_tree_site_age_days() {
57 $epoch = openstation_living_tree_install_epoch();
58 if ( $epoch <= 0 ) {
59 return 0;
60 }
61 return max( 0, (int) floor( ( time() - $epoch ) / DAY_IN_SECONDS ) );
62 }
63
64 /**
65 * Recent traffic signal, resolved with the same source ladder the
66 * site-views widget uses: Jetpack Stats first, then the
67 * `_post_views_YYYY-MM-DD` post-meta convention — both summed over the
68 * last 14 days. Sites with neither simply report 0 (a windless day).
69 *
70 * The final value passes through the `openstation_living_tree_traffic`
71 * filter so analytics plugins with their own counters can feed the
72 * real number in.
73 *
74 * @param bool $with_jetpack Whether Jetpack Stats may answer. False
75 * resolves the ladder from the post-views
76 * meta down, for a caller Jetpack withholds
77 * its stats from.
78 * @return int Recent view sum. >= 0.
79 */
80 function openstation_living_tree_traffic( $with_jetpack = true ) {
81 $views = $with_jetpack ? openstation_living_tree_jetpack_visits() : null;
82 if ( null === $views ) {
83 $views = openstation_living_tree_meta_views();
84 }
85
86 /**
87 * Filter the Living Tree traffic hormone source. Return a
88 * non-negative view count for the last ~14 days — it drives the
89 * wind (canopy sway amplitude / frequency).
90 *
91 * @param int $views Views in the window. Default: Jetpack Stats
92 * when available, else the `_post_views_*` meta
93 * sum, else 0.
94 */
95 $views = (int) apply_filters( 'openstation_living_tree_traffic', $views );
96 return max( 0, $views );
97 }
98
99 /**
100 * Last-14-days visits from Jetpack Stats, or `null` when unavailable.
101 *
102 * Sums the daily rows the site-views widget's `site-views-jetpack`
103 * route serves, read through {@see openstation_site_views_jetpack_days()},
104 * so the two cannot disagree about when Jetpack is the source: Jetpack
105 * absent, the Stats module off, a WP_Error or an unexpected payload
106 * all return `null` and the caller falls back to the post-views meta.
107 * A successful `0` is trusted (a quiet site is a valid answer).
108 *
109 * Not gated on the caller, so the snapshot's transient holds the same
110 * value no matter which user primes it. The gate is applied where the
111 * snapshot is served, see {@see openstation_living_tree_snapshot_for_caller()}.
112 *
113 * @return int|null Views over the last 14 days, or null when Jetpack
114 * Stats can't answer.
115 */
116 function openstation_living_tree_jetpack_visits() {
117 $days = openstation_site_views_jetpack_days();
118 return null === $days ? null : (int) array_sum( array_column( $days, 'views' ) );
119 }
120
121 /**
122 * Last-14-days view sum from the `_post_views_YYYY-MM-DD` post-meta
123 * convention — the plain-WP fallback shared with the site-views
124 * widget. Sites without a view-counter plugin report 0.
125 *
126 * @return int Recent view sum. >= 0.
127 */
128 function openstation_living_tree_meta_views() {
129 global $wpdb;
130
131 $total = 0;
132 $today = current_time( 'Y-m-d' );
133 for ( $i = 0; $i < 14; $i++ ) {
134 $date = gmdate( 'Y-m-d', strtotime( $today . ' -' . $i . ' days' ) );
135 $meta_key = '_post_views_' . $date;
136 $total += (int) $wpdb->get_var(
137 $wpdb->prepare(
138 "SELECT COALESCE( SUM( CAST( meta_value AS UNSIGNED ) ), 0 )
139 FROM {$wpdb->postmeta}
140 WHERE meta_key = %s",
141 $meta_key
142 )
143 );
144 }
145
146 return max( 0, $total );
147 }
148
149 /**
150 * Number of users currently online, from framework presence.
151 *
152 * @return int Count of users with `online` presence status. >= 0.
153 */
154 function openstation_living_tree_active_users() {
155 if ( ! function_exists( 'openstation_presence_snapshot' ) ) {
156 return 0;
157 }
158 $count = 0;
159 foreach ( openstation_presence_snapshot() as $record ) {
160 if ( isset( $record['status'] ) && 'online' === $record['status'] ) {
161 ++$count;
162 }
163 }
164 return $count;
165 }
166
167 /**
168 * SEO / site-health score, normalised 0..1.
169 *
170 * KNOWN GAP: unlike `traffic` (Jetpack Stats → post-views meta) and
171 * `performance` (core Site Health tallies), this hormone still has no
172 * first-party source — WordPress ships nothing SEO-shaped to read, so
173 * the default is a healthy 0.7 and the filter is the only integration
174 * point. Candidate future source: aggregate the per-post scores that
175 * SEO plugins store in post-meta into a site-wide average. Until then,
176 * an SEO or monitoring plugin that *does* know the site's health can
177 * feed the real value in via the filter.
178 *
179 * @return float Health score in [0, 1].
180 */
181 function openstation_living_tree_seo_health() {
182 /**
183 * Filter the Living Tree health hormone source. Return 0..1 — it
184 * drives the canopy's colour temperature (green → yellow → red →
185 * grey).
186 *
187 * @param float $health Default 0.7.
188 */
189 $health = (float) apply_filters( 'openstation_living_tree_seo_health', 0.7 );
190 return min( 1.0, max( 0.0, $health ) );
191 }
192
193 /**
194 * Performance headroom, normalised 0..1 (1 = plenty, 0 = under load).
195 *
196 * Sourced from core's own Site Health tallies when available (see
197 * {@see openstation_living_tree_site_health_performance()}), falling
198 * back to a comfortable 0.8 until the weekly Site Health cron has run
199 * at least once. The filter remains the integration point for plugins
200 * with real runtime telemetry.
201 *
202 * @return float Performance score in [0, 1].
203 */
204 function openstation_living_tree_performance() {
205 $performance = openstation_living_tree_site_health_performance();
206 if ( null === $performance ) {
207 $performance = 0.8;
208 }
209
210 /**
211 * Filter the Living Tree performance hormone source. Return 0..1 —
212 * it throttles growth vigour.
213 *
214 * @param float $performance Default: a composite of core's Site
215 * Health issue counts when the
216 * `health-check-site-status-result`
217 * transient exists, else 0.8.
218 */
219 $performance = (float) apply_filters( 'openstation_living_tree_performance', $performance );
220 return min( 1.0, max( 0.0, $performance ) );
221 }
222
223 /**
224 * Performance composite from core's Site Health tallies, or `null`
225 * when unavailable.
226 *
227 * WordPress runs every Site Health test on a weekly cron
228 * (`wp_site_health_scheduled_check`) and persists the tallies in the
229 * `health-check-site-status-result` transient as JSON counts
230 * (`good` / `recommended` / `critical`) — the same source the
231 * dashboard's Site Health widget reads. Mapping: start at 1.0,
232 * subtract 0.15 per critical issue and 0.04 per recommendation, clamp
233 * to [0.2, 1] — a clean install grows vigorously, a neglected one
234 * visibly slows down but never fully stalls.
235 *
236 * Site Health measures broad install health (PHP version, HTTPS,
237 * updates, object caching…), not pure runtime speed — the right
238 * flavour for a "growth vigour" hormone. The transient is absent on a
239 * brand-new site until the weekly cron first fires or someone opens
240 * the Site Health screen; callers fall back to the 0.8 default then.
241 *
242 * @return float|null Composite in [0.2, 1], or null when the Site
243 * Health tallies aren't available (yet).
244 */
245 function openstation_living_tree_site_health_performance() {
246 $raw = get_transient( 'health-check-site-status-result' );
247 if ( is_string( $raw ) && '' !== $raw ) {
248 $counts = json_decode( $raw, true );
249 } elseif ( is_array( $raw ) ) {
250 // Defensive: some object-cache drop-ins hand back the decoded
251 // array. Core itself always stores a JSON string.
252 $counts = $raw;
253 } else {
254 return null;
255 }
256
257 if ( ! is_array( $counts )
258 || ( ! isset( $counts['critical'] ) && ! isset( $counts['recommended'] ) && ! isset( $counts['good'] ) )
259 ) {
260 return null;
261 }
262
263 $critical = max( 0, (int) ( $counts['critical'] ?? 0 ) );
264 $recommended = max( 0, (int) ( $counts['recommended'] ?? 0 ) );
265
266 $score = 1.0 - ( 0.15 * $critical ) - ( 0.04 * $recommended );
267 return min( 1.0, max( 0.2, $score ) );
268 }
269
270 /**
271 * Compact per-region structural hints (the `branches` array): published
272 * posts grouped by year, each year mapped to a depth/girth/length hint
273 * normalised against the busiest year. This is DNA, not geometry — the
274 * simulator may bias growth density with it, never position anything.
275 *
276 * @return array[] Compact branch DNA hints (max 12 entries).
277 */
278 function openstation_living_tree_branch_dna() {
279 global $wpdb;
280
281 $rows = $wpdb->get_results(
282 "SELECT YEAR( post_date_gmt ) AS y, COUNT(*) AS n
283 FROM {$wpdb->posts}
284 WHERE post_status = 'publish' AND post_type = 'post'
285 AND post_date_gmt > '1970-01-01 00:00:01'
286 GROUP BY y
287 ORDER BY y ASC
288 LIMIT 12",
289 ARRAY_A
290 );
291 if ( empty( $rows ) ) {
292 return array();
293 }
294
295 $max = 1;
296 foreach ( $rows as $row ) {
297 $max = max( $max, (int) $row['n'] );
298 }
299
300 $out = array();
301 $depth = 0;
302 foreach ( $rows as $row ) {
303 $out[] = array(
304 'depth' => $depth,
305 'girth' => round( (int) $row['n'] / $max, 3 ),
306 'length' => round( min( 1.0, (int) $row['n'] / $max + 0.2 ), 3 ),
307 );
308 ++$depth;
309 }
310 return $out;
311 }
312