PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.1
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.1
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 0.8.9 0.8.8 0.8.7 All 34 releases
desktop-mode / includes / living-tree / snapshot.php

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

150 lines 5.2 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: REST snapshot endpoint.
4 *
5 * One route: `GET desktop-mode/v1/living-tree/snapshot`. Returns the
6 * compact site DNA (`TreeSnapshot` in the JS types) — aggregate counts
7 * and branch hints — never the full post list. The client turns this
8 * into hormones and never sees rows.
9 *
10 * `siteUrl` + `siteName` + `installEpoch` together form the determinism
11 * seed. The site NAME is deliberately part of it: two different blogs can
12 * share a URL shape (two installs on localhost, staging clones), and
13 * their trees must still be individuals.
14 *
15 * The response is cached in a transient (TTL 6h) and invalidated whenever
16 * content changes (`save_post` / `deleted_post` / `comment_post`).
17 *
18 * @package OpenStation
19 */
20
21 defined( 'ABSPATH' ) || exit;
22
23 /**
24 * Transient key the built snapshot is cached under.
25 *
26 * The VALUE keeps its pre-rebrand spelling on purpose, so live caches
27 * stay addressable. The mismatch between this constant's name and its
28 * value is deliberate — it is NOT a half-finished rename.
29 *
30 * Not to be confused with the `openstation_living_tree_snapshot` filter,
31 * which once shared this string and is now deliberately decoupled.
32 */
33 const OPENSTATION_LIVING_TREE_CACHE_KEY = 'desktop_mode_living_tree_snapshot';
34
35 /** Cache lifetime for the snapshot. */
36 const OPENSTATION_LIVING_TREE_CACHE_TTL = 6 * HOUR_IN_SECONDS;
37
38 /**
39 * Whether the current user may read the Living Tree snapshot.
40 *
41 * Defaults to `read` — anyone who can see the admin can see the wallpaper
42 * of their own site. Filterable so a site can widen or restrict it.
43 *
44 * @return bool
45 */
46 function openstation_living_tree_user_can_use() {
47 $can = current_user_can( 'read' );
48
49 /**
50 * Filter whether the current user can read the Living Tree snapshot.
51 *
52 * @param bool $can Default: the `read` capability.
53 */
54 return (bool) apply_filters( 'openstation_living_tree_user_can_use', $can );
55 }
56
57 /**
58 * Register the snapshot route.
59 */
60 function openstation_living_tree_register_routes() {
61 register_rest_route(
62 'desktop-mode/v1',
63 '/living-tree/snapshot',
64 array(
65 'methods' => WP_REST_Server::READABLE,
66 'callback' => 'openstation_living_tree_rest_snapshot',
67 'permission_callback' => 'openstation_living_tree_user_can_use',
68 )
69 );
70 }
71 add_action( 'rest_api_init', 'openstation_living_tree_register_routes' );
72
73 /**
74 * GET /living-tree/snapshot — cached snapshot response.
75 *
76 * @return WP_REST_Response
77 */
78 function openstation_living_tree_rest_snapshot() {
79 $cached = get_transient( OPENSTATION_LIVING_TREE_CACHE_KEY );
80 if ( is_array( $cached ) ) {
81 return rest_ensure_response( $cached );
82 }
83
84 $snapshot = openstation_living_tree_build_snapshot();
85 set_transient(
86 OPENSTATION_LIVING_TREE_CACHE_KEY,
87 $snapshot,
88 OPENSTATION_LIVING_TREE_CACHE_TTL
89 );
90
91 return rest_ensure_response( $snapshot );
92 }
93
94 /**
95 * Invalidate the cached snapshot. Wired to the content-mutation hooks so
96 * the tree re-DNAs on the next load after the site changes.
97 */
98 function openstation_living_tree_flush_cache() {
99 delete_transient( OPENSTATION_LIVING_TREE_CACHE_KEY );
100 }
101 add_action( 'save_post', 'openstation_living_tree_flush_cache' );
102 add_action( 'deleted_post', 'openstation_living_tree_flush_cache' );
103 add_action( 'comment_post', 'openstation_living_tree_flush_cache' );
104
105 /**
106 * Build the compact site DNA snapshot.
107 *
108 * Aggregates only. Every metric is capped / normalised so any topology
109 * yields a well-formed snapshot — the golden rule (WordPress emits
110 * hormones, never geometry) lives here: this function must never leak a
111 * per-post coordinate or identity into the payload.
112 *
113 * @return array The snapshot, matching the JS `TreeSnapshot` shape.
114 */
115 function openstation_living_tree_build_snapshot() {
116 $posts = wp_count_posts( 'post' );
117 $pages = wp_count_posts( 'page' );
118 $comments = wp_count_comments();
119
120 $categories = wp_count_terms( array( 'taxonomy' => 'category' ) );
121 $tags = wp_count_terms( array( 'taxonomy' => 'post_tag' ) );
122
123 $snapshot = array(
124 'siteUrl' => (string) home_url(),
125 'siteName' => (string) get_bloginfo( 'name' ),
126 'installEpoch' => openstation_living_tree_install_epoch(),
127 'siteAgeDays' => openstation_living_tree_site_age_days(),
128 'totalPosts' => isset( $posts->publish ) ? (int) $posts->publish : 0,
129 'totalPages' => isset( $pages->publish ) ? (int) $pages->publish : 0,
130 'totalCategories' => is_wp_error( $categories ) ? 0 : (int) $categories,
131 'totalTags' => is_wp_error( $tags ) ? 0 : (int) $tags,
132 'totalComments' => isset( $comments->approved ) ? (int) $comments->approved : 0,
133 'activeUsers' => openstation_living_tree_active_users(),
134 'traffic' => openstation_living_tree_traffic(),
135 'seoHealth' => openstation_living_tree_seo_health(),
136 'performance' => openstation_living_tree_performance(),
137 'branches' => openstation_living_tree_branch_dna(),
138 );
139
140 /**
141 * Filter the Living Tree snapshot before it is cached and served.
142 * Keep the shape intact — the JS client validates nothing; it trusts
143 * this contract. Aggregates only: never add per-post identities or
144 * coordinates (the golden rule).
145 *
146 * @param array $snapshot The compact site DNA.
147 */
148 return apply_filters( 'openstation_living_tree_snapshot', $snapshot );
149 }
150