PluginProbe
wpDataTables – WordPress Data Table, Dynamic Tables & Table Charts Plugin / trunk
wpDataTables – WordPress Data Table, Dynamic Tables & Table Charts Plugin vtrunk
6.5.1.7 6.5.1.6 6.5.1.5 6.5.1.4 6.5.1.3 6.5.1.2 6.5.1.1 6.5.0.9 6.5.0.8 6.5.0.7 6.5.0.6 trunk 3.4.2.40 3.4.2.41 3.4.2.42 3.4.2.43 3.4.2.44 3.4.2.45 3.4.2.46 3.4.2.47 3.4.2.48 3.4.2.49 3.4.2.50 6.3.2 6.3.3.1 All 47 releases
wpdatatables / Infrastructure / WP / MCP / Abilities / get-system-info.php

get-system-info.php in wpDataTables – WordPress Data Table, Dynamic Tables & Table Charts Plugin trunk, at Infrastructure/WP/MCP/Abilities/get-system-info.php

328 lines 15.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Ability: wpdatatables/get-system-info
4 *
5 * Returns the wpDataTables installation environment: plugin version, active
6 * edition/tier, which feature integrations are loaded (HighCharts, ApexCharts,
7 * separate DB connections, SQL constructor, editing, etc.), available chart
8 * engines, global settings, and the list of registered database connections.
9 *
10 * This is the AI's first step: understand what the installation can do before
11 * attempting operations that may require a higher-tier licence.
12 *
13 * @package wpDataTables_MCP_Server
14 * @since 0.2.0
15 */
16
17 defined( 'ABSPATH' ) or die('Access denied.');
18
19 add_action( 'wp_abilities_api_init', 'wdtmcp_register_get_system_info_ability' );
20
21 function wdtmcp_register_get_system_info_ability() {
22 wp_register_ability(
23 'wpdatatables/get-system-info',
24 array(
25 'label' => __( 'Get wpDataTables System Info', 'wpdatatables' ),
26 'description' => __( 'Returns the wpDataTables version, detected licence tier, enabled integrations, global settings, and the default database connection. Call this first to understand what capabilities are available before creating tables or charts. This is wpDataTables Lite, so paid integrations report as unavailable and only the google and chartjs chart engines are present. WHEN TO CALL: as the very first MCP call in a new session, or before any create/update operation — it tells you which features and licence limits apply on this site. No parameters are required — pass an empty object {}. RETURNS: version, detected_tier (free for Lite), integrations (feature flags), chart_engines[], connections[], settings, addons. USE THIS TO DECIDE: which chart engine to pass to create-chart. AFTER LICENCE ERRORS: call wpdatatables/get-mcp-license-matrix for a structured ability × tier overview and the pricing URL. TYPICAL WORKFLOW: get-system-info → then list-tables, get-mcp-license-matrix (when needed), or the appropriate create-* ability.', 'wpdatatables' ),
27 'category' => 'wpdatatables-data',
28
29 'input_schema' => array(
30 'type' => 'object',
31 'properties' => array(),
32 'required' => array(),
33 ),
34 'output_schema' => array(
35 'type' => 'object',
36 'properties' => array(
37 'version' => array( 'type' => 'string', 'description' => 'wpDataTables plugin version.' ),
38 'detected_tier' => array( 'type' => 'string', 'description' => 'Best-guess tier based on integration files present and an active main licence (wdtActivated). Returns free when integration files exist but the licence is not activated.' ),
39 'license_active' => array( 'type' => 'boolean', 'description' => 'Whether the main wpDataTables licence is activated on this site (Melograno Store or Envato).' ),
40 'integrations' => array( 'type' => 'object', 'description' => 'Map of feature name => boolean indicating whether the integration is loaded.' ),
41 'chart_engines' => array( 'type' => 'array', 'description' => 'Chart rendering engines available on this installation.', 'items' => array( 'type' => 'string' ) ),
42 'connections' => array( 'type' => 'array', 'description' => 'Registered database connections (always includes the default WP connection).', 'items' => array( 'type' => 'object' ) ),
43 'settings' => array( 'type' => 'object', 'description' => 'Relevant global settings (date format, number format, skin, etc.).' ),
44 'addons' => array( 'type' => 'object', 'description' => 'Activation status of add-ons (Powerful Filters, Report Builder, Gravity, Formidable, Master-Detail).' ),
45 ),
46 ),
47
48 'execute_callback' => 'wdtmcp_execute_get_system_info',
49
50 'permission_callback' => function () {
51 return current_user_can( 'manage_options' );
52 },
53
54 'meta' => array(
55 'annotations' => array(
56 'instructions' => __( 'Call first in a new session with an empty object {}. Check detected_tier, integrations, chart_engines, and connections before any create or update ability.', 'wpdatatables' ),
57 'readonly' => true,
58 'destructive' => false,
59 'idempotent' => true,
60 ),
61 ),
62 )
63 );
64 }
65
66 /**
67 * Execute callback for wpdatatables/get-system-info.
68 *
69 * @return array|WP_Error
70 */
71 function wdtmcp_execute_get_system_info() {
72
73 // ── Version ──────────────────────────────────────────────────
74 $version = defined( 'WDT_CURRENT_VERSION' ) ? WDT_CURRENT_VERSION : 'unknown';
75
76 // ── Integration detection ────────────────────────────────────
77 // Some integration constants are only defined inside is_admin()
78 // (e.g. Google Sheet API, SQL Constructor, Folders), so they are
79 // unavailable during REST/MCP requests. We use is_file() against the
80 // entry-point files — the same approach wpDataTables' own loader uses.
81 $integrations = wdtmcp_detect_integrations();
82
83 // ── Tier heuristic ───────────────────────────────────────────
84 $detected_tier = wdtmcp_detect_tier( $integrations );
85
86 // ── Chart engines ────────────────────────────────────────────
87 $chart_engines = array( 'google', 'chartjs' );
88 if ( $integrations['highcharts'] ) { $chart_engines[] = 'highcharts'; }
89 if ( $integrations['apexcharts'] ) { $chart_engines[] = 'apexcharts'; }
90 if ( $integrations['highstock'] ) { $chart_engines[] = 'highstock'; }
91
92 // ── Database connections ─────────────────────────────────────
93 $connections = wdtmcp_get_connections( $integrations['separate_db'] );
94
95 // ── Global settings (safe subset) ────────────────────────────
96 $settings = wdtmcp_get_safe_settings();
97
98 // ── Add-on activation ────────────────────────────────────────
99 $addons = array(
100 'powerful_filters' => (bool) get_option( 'wdtActivatedPowerful' ),
101 'report_builder' => (bool) get_option( 'wdtActivatedReport' ),
102 'gravity_forms' => (bool) get_option( 'wdtActivatedGravity' ),
103 'formidable_forms' => (bool) get_option( 'wdtActivatedFormidable' ),
104 'master_detail' => (bool) get_option( 'wdtActivatedMasterDetail' ),
105 );
106
107 return array(
108 'version' => $version,
109 'detected_tier' => $detected_tier,
110 'license_active' => wdtmcp_is_main_license_active(),
111 'integrations' => $integrations,
112 'chart_engines' => $chart_engines,
113 'connections' => $connections,
114 'settings' => $settings,
115 'addons' => $addons,
116 );
117 }
118
119 /**
120 * Detect available integrations by checking whether the integration
121 * entry-point files exist on disk.
122 *
123 * We cannot rely on defined() for WDT_*_INTEGRATION constants because
124 * several are loaded only inside is_admin() — which is false during
125 * REST/MCP requests.
126 *
127 * @return array Feature name => boolean map.
128 */
129 function wdtmcp_detect_integrations() {
130 $starter = defined( 'WDT_STARTER_INTEGRATIONS_PATH' ) ? WDT_STARTER_INTEGRATIONS_PATH : '';
131 $standard = defined( 'WDT_STANDARD_INTEGRATIONS_PATH' ) ? WDT_STANDARD_INTEGRATIONS_PATH : '';
132 $pro = defined( 'WDT_PRO_INTEGRATIONS_PATH' ) ? WDT_PRO_INTEGRATIONS_PATH : '';
133
134 // feature => relative path from the tier integrations directory.
135 $file_map = array(
136 'google_sheet_api' => array( $starter, 'google-sheet-api/wdt-google-sheet-api-integration.php' ),
137 'separate_db' => array( $standard, 'separate-db-connection/wdt-separate-connection-integration.php' ),
138 'sql_constructor' => array( $standard, 'sql-constructor/wdt-sql-constructor-integration.php' ),
139 'sql_query' => array( $standard, 'sql-query/wdt-sql-query-integration.php' ),
140 'highcharts' => array( $standard, 'highcharts/wdt-highcharts-integration.php' ),
141 'apexcharts' => array( $standard, 'apexcharts/wdt-apexcharts-integration.php' ),
142 'editing' => array( $standard, 'editing/wdt-editing-integration.php' ),
143 'placeholders' => array( $standard, 'placeholders/wdt-placeholders-integration.php' ),
144 'foreign_key' => array( $standard, 'foreign-key/wdt-foreign-key-integration.php' ),
145 'formula_column' => array( $standard, 'formula-column/wdt-formula-column-integration.php' ),
146 'hidden_column' => array( $standard, 'hidden-column/wdt-hidden-column-integration.php' ),
147 'fixed_col_header' => array( $standard, 'fixed-columns-and-headers/wdt-fixed-ch-integration.php' ),
148 'index_column' => array( $standard, 'index-column/wdt-index-column-integration.php' ),
149 'update_manual_file' => array( $standard, 'update-manual-from-file/wdt-update-manual-from-file-integration.php' ),
150 'highstock' => array( $pro, 'highstock/wdt-highstock-integration.php' ),
151 'folders' => array( $pro, 'folders/source/class.wpdatafolders.php' ),
152 'wp_posts_builder' => array( $pro, 'query-builder/source/wdt-query-builder.php' ),
153 'woocommerce' => array( $pro, 'woo-commerce/source/wdt-woo-commerce.php' ),
154 );
155
156 $integrations = array();
157 foreach ( $file_map as $feature => $parts ) {
158 $base = $parts[0];
159 $file = $parts[1];
160 $integrations[ $feature ] = ( '' !== $base && is_file( $base . $file ) );
161 }
162
163 return $integrations;
164 }
165
166 /**
167 * Whether the main wpDataTables licence is activated on this site.
168 *
169 * Matches the plugin's own activation flag (Melograno Store purchase code or
170 * Envato OAuth). Integration files on disk alone do not count as a valid licence.
171 *
172 * @return bool
173 */
174 function wdtmcp_is_main_license_active() {
175 return (bool) get_option( 'wdtActivated' );
176 }
177
178 /**
179 * Detect the wpDataTables Lite tier for usage telemetry.
180 *
181 * Lite is a separate product — always report the lite SKU. The $integrations
182 * argument is accepted for API compatibility with the shared usage-tracker
183 * collector and is ignored.
184 *
185 * @param array $integrations Feature => boolean map (unused on Lite).
186 * @return string Always 'lite'.
187 */
188 function wdtmcp_detect_tier_from_features( array $integrations ) {
189 return 'lite';
190 }
191
192 /**
193 * Detect the wpDataTables tier from loaded integration constants.
194 *
195 * The tiers are cumulative — Developer ⊃ Pro ⊃ Standard ⊃ Starter ⊃ Free.
196 * Developer has identical features to Pro but with unlimited domain licensing;
197 * it is identified by the presence of developer_license.txt in the developer
198 * integrations directory.
199 *
200 * Paid tiers require an activated main licence; copied integration files without
201 * activation are reported as free. For file-only / telemetry SKU detection on
202 * Lite, use {@see wdtmcp_detect_tier_from_features()}.
203 *
204 * @param array $integrations Feature => boolean map.
205 * @return string One of: developer, pro, standard, starter, free.
206 */
207 function wdtmcp_detect_tier( array $integrations ) {
208 if ( ! wdtmcp_is_main_license_active() ) {
209 return 'free';
210 }
211
212 $has_pro_feature = false;
213 $pro_features = array( 'highstock', 'folders', 'wp_posts_builder', 'woocommerce' );
214 foreach ( $pro_features as $f ) {
215 if ( ! empty( $integrations[ $f ] ) ) {
216 $has_pro_feature = true;
217 break;
218 }
219 }
220
221 if ( $has_pro_feature ) {
222 if ( defined( 'WDT_DEVELOPER_INTEGRATIONS_PATH' )
223 && is_file( WDT_DEVELOPER_INTEGRATIONS_PATH . 'developer_license.txt' ) ) {
224 return 'developer';
225 }
226 return 'pro';
227 }
228
229 $standard_features = array( 'separate_db', 'sql_constructor', 'highcharts', 'apexcharts', 'editing', 'placeholders', 'foreign_key' );
230 foreach ( $standard_features as $f ) {
231 if ( ! empty( $integrations[ $f ] ) ) {
232 return 'standard';
233 }
234 }
235
236 if ( ! empty( $integrations['google_sheet_api'] ) ) {
237 return 'starter';
238 }
239
240 return 'free';
241 }
242
243 /**
244 * Build the list of database connections.
245 *
246 * Always includes the default WordPress connection. If separate DB
247 * connections are enabled, appends each registered connection
248 * (without exposing credentials).
249 *
250 * @param bool $separate_db_enabled Whether the separate DB integration is loaded.
251 * @return array
252 */
253 function wdtmcp_get_connections( $separate_db_enabled ) {
254 global $wpdb;
255
256 $connections = array(
257 array(
258 'id' => '',
259 'name' => 'WordPress (default)',
260 'vendor' => 'mysql',
261 'database' => $wpdb->dbname,
262 ),
263 );
264
265 if ( $separate_db_enabled && class_exists( 'Connection' ) ) {
266 $all = \Connection::getAll();
267 if ( is_array( $all ) ) {
268 foreach ( $all as $conn ) {
269 $connections[] = array(
270 'id' => isset( $conn['id'] ) ? (string) $conn['id'] : '',
271 'name' => isset( $conn['name'] ) ? (string) $conn['name'] : '',
272 'vendor' => isset( $conn['vendor'] ) ? (string) $conn['vendor'] : '',
273 'database' => isset( $conn['database'] ) ? (string) $conn['database'] : '',
274 );
275 }
276 }
277 }
278
279 return $connections;
280 }
281
282 /**
283 * Return a safe subset of global plugin settings.
284 *
285 * Excludes sensitive keys (purchase codes, tokens, passwords, custom JS).
286 *
287 * @return array
288 */
289 function wdtmcp_get_safe_settings() {
290 $safe_keys = array(
291 'wdtDateFormat',
292 'wdtTimeFormat',
293 'wdtBaseSkin',
294 'wdtNumberFormat',
295 'wdtDecimalPlaces',
296 'wdtCSVDelimiter',
297 'wdtTablesPerPage',
298 'wdtSortingOrderBrowseTables',
299 'wdtTabletWidth',
300 'wdtMobileWidth',
301 'wdtIncludeBootstrap',
302 'wdtPreventDeletingTables',
303 'wdtParseShortcodes',
304 'wdtNumbersAlign',
305 'wdtBorderRemoval',
306 'wdtBorderRemovalHeader',
307 'wdtRenderFilter',
308 'wdtInterfaceLanguage',
309 'wdtSumFunctionsLabel',
310 'wdtAvgFunctionsLabel',
311 'wdtMinFunctionsLabel',
312 'wdtMaxFunctionsLabel',
313 );
314
315 $settings = array();
316 foreach ( $safe_keys as $key ) {
317 $val = get_option( $key );
318 $settings[ $key ] = ( false === $val ) ? null : $val;
319 }
320
321 $font_color = get_option( 'wdtFontColorSettings' );
322 if ( $font_color && is_array( $font_color ) ) {
323 $settings['wdtFontColorSettings'] = $font_color;
324 }
325
326 return $settings;
327 }
328