class-wc-rest-system-status-controller.php 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076
  1. <?php
  2. /**
  3. * REST API WC System Status controller
  4. *
  5. * Handles requests to the /system_status endpoint.
  6. *
  7. * @package WooCommerce/API
  8. * @since 3.0.0
  9. */
  10. defined( 'ABSPATH' ) || exit;
  11. /**
  12. * System status controller class.
  13. *
  14. * @package WooCommerce/API
  15. * @extends WC_REST_Controller
  16. */
  17. class WC_REST_System_Status_Controller extends WC_REST_Controller {
  18. /**
  19. * Endpoint namespace.
  20. *
  21. * @var string
  22. */
  23. protected $namespace = 'wc/v2';
  24. /**
  25. * Route base.
  26. *
  27. * @var string
  28. */
  29. protected $rest_base = 'system_status';
  30. /**
  31. * Register the route for /system_status
  32. */
  33. public function register_routes() {
  34. register_rest_route(
  35. $this->namespace, '/' . $this->rest_base,
  36. array(
  37. array(
  38. 'methods' => WP_REST_Server::READABLE,
  39. 'callback' => array( $this, 'get_items' ),
  40. 'permission_callback' => array( $this, 'get_items_permissions_check' ),
  41. 'args' => $this->get_collection_params(),
  42. ),
  43. 'schema' => array( $this, 'get_public_item_schema' ),
  44. )
  45. );
  46. }
  47. /**
  48. * Check whether a given request has permission to view system status.
  49. *
  50. * @param WP_REST_Request $request Full details about the request.
  51. * @return WP_Error|boolean
  52. */
  53. public function get_items_permissions_check( $request ) {
  54. if ( ! wc_rest_check_manager_permissions( 'system_status', 'read' ) ) {
  55. return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
  56. }
  57. return true;
  58. }
  59. /**
  60. * Get a system status info, by section.
  61. *
  62. * @param WP_REST_Request $request Full details about the request.
  63. * @return WP_Error|WP_REST_Response
  64. */
  65. public function get_items( $request ) {
  66. $schema = $this->get_item_schema();
  67. $mappings = $this->get_item_mappings();
  68. $response = array();
  69. foreach ( $mappings as $section => $values ) {
  70. foreach ( $values as $key => $value ) {
  71. if ( isset( $schema['properties'][ $section ]['properties'][ $key ]['type'] ) ) {
  72. settype( $values[ $key ], $schema['properties'][ $section ]['properties'][ $key ]['type'] );
  73. }
  74. }
  75. settype( $values, $schema['properties'][ $section ]['type'] );
  76. $response[ $section ] = $values;
  77. }
  78. $response = $this->prepare_item_for_response( $response, $request );
  79. return rest_ensure_response( $response );
  80. }
  81. /**
  82. * Get the system status schema, conforming to JSON Schema.
  83. *
  84. * @return array
  85. */
  86. public function get_item_schema() {
  87. $schema = array(
  88. '$schema' => 'http://json-schema.org/draft-04/schema#',
  89. 'title' => 'system_status',
  90. 'type' => 'object',
  91. 'properties' => array(
  92. 'environment' => array(
  93. 'description' => __( 'Environment.', 'woocommerce' ),
  94. 'type' => 'object',
  95. 'context' => array( 'view' ),
  96. 'readonly' => true,
  97. 'properties' => array(
  98. 'home_url' => array(
  99. 'description' => __( 'Home URL.', 'woocommerce' ),
  100. 'type' => 'string',
  101. 'format' => 'uri',
  102. 'context' => array( 'view' ),
  103. 'readonly' => true,
  104. ),
  105. 'site_url' => array(
  106. 'description' => __( 'Site URL.', 'woocommerce' ),
  107. 'type' => 'string',
  108. 'format' => 'uri',
  109. 'context' => array( 'view' ),
  110. 'readonly' => true,
  111. ),
  112. 'wc_version' => array(
  113. 'description' => __( 'WooCommerce version.', 'woocommerce' ),
  114. 'type' => 'string',
  115. 'context' => array( 'view' ),
  116. 'readonly' => true,
  117. ),
  118. 'log_directory' => array(
  119. 'description' => __( 'Log directory.', 'woocommerce' ),
  120. 'type' => 'string',
  121. 'context' => array( 'view' ),
  122. 'readonly' => true,
  123. ),
  124. 'log_directory_writable' => array(
  125. 'description' => __( 'Is log directory writable?', 'woocommerce' ),
  126. 'type' => 'boolean',
  127. 'context' => array( 'view' ),
  128. 'readonly' => true,
  129. ),
  130. 'wp_version' => array(
  131. 'description' => __( 'WordPress version.', 'woocommerce' ),
  132. 'type' => 'string',
  133. 'context' => array( 'view' ),
  134. 'readonly' => true,
  135. ),
  136. 'wp_multisite' => array(
  137. 'description' => __( 'Is WordPress multisite?', 'woocommerce' ),
  138. 'type' => 'boolean',
  139. 'context' => array( 'view' ),
  140. 'readonly' => true,
  141. ),
  142. 'wp_memory_limit' => array(
  143. 'description' => __( 'WordPress memory limit.', 'woocommerce' ),
  144. 'type' => 'integer',
  145. 'context' => array( 'view' ),
  146. 'readonly' => true,
  147. ),
  148. 'wp_debug_mode' => array(
  149. 'description' => __( 'Is WordPress debug mode active?', 'woocommerce' ),
  150. 'type' => 'boolean',
  151. 'context' => array( 'view' ),
  152. 'readonly' => true,
  153. ),
  154. 'wp_cron' => array(
  155. 'description' => __( 'Are WordPress cron jobs enabled?', 'woocommerce' ),
  156. 'type' => 'boolean',
  157. 'context' => array( 'view' ),
  158. 'readonly' => true,
  159. ),
  160. 'language' => array(
  161. 'description' => __( 'WordPress language.', 'woocommerce' ),
  162. 'type' => 'string',
  163. 'context' => array( 'view' ),
  164. 'readonly' => true,
  165. ),
  166. 'server_info' => array(
  167. 'description' => __( 'Server info.', 'woocommerce' ),
  168. 'type' => 'string',
  169. 'context' => array( 'view' ),
  170. 'readonly' => true,
  171. ),
  172. 'php_version' => array(
  173. 'description' => __( 'PHP version.', 'woocommerce' ),
  174. 'type' => 'string',
  175. 'context' => array( 'view' ),
  176. 'readonly' => true,
  177. ),
  178. 'php_post_max_size' => array(
  179. 'description' => __( 'PHP post max size.', 'woocommerce' ),
  180. 'type' => 'integer',
  181. 'context' => array( 'view' ),
  182. 'readonly' => true,
  183. ),
  184. 'php_max_execution_time' => array(
  185. 'description' => __( 'PHP max execution time.', 'woocommerce' ),
  186. 'type' => 'integer',
  187. 'context' => array( 'view' ),
  188. 'readonly' => true,
  189. ),
  190. 'php_max_input_vars' => array(
  191. 'description' => __( 'PHP max input vars.', 'woocommerce' ),
  192. 'type' => 'integer',
  193. 'context' => array( 'view' ),
  194. 'readonly' => true,
  195. ),
  196. 'curl_version' => array(
  197. 'description' => __( 'cURL version.', 'woocommerce' ),
  198. 'type' => 'string',
  199. 'context' => array( 'view' ),
  200. 'readonly' => true,
  201. ),
  202. 'suhosin_installed' => array(
  203. 'description' => __( 'Is SUHOSIN installed?', 'woocommerce' ),
  204. 'type' => 'boolean',
  205. 'context' => array( 'view' ),
  206. 'readonly' => true,
  207. ),
  208. 'max_upload_size' => array(
  209. 'description' => __( 'Max upload size.', 'woocommerce' ),
  210. 'type' => 'integer',
  211. 'context' => array( 'view' ),
  212. 'readonly' => true,
  213. ),
  214. 'mysql_version' => array(
  215. 'description' => __( 'MySQL version.', 'woocommerce' ),
  216. 'type' => 'string',
  217. 'context' => array( 'view' ),
  218. 'readonly' => true,
  219. ),
  220. 'mysql_version_string' => array(
  221. 'description' => __( 'MySQL version string.', 'woocommerce' ),
  222. 'type' => 'string',
  223. 'context' => array( 'view' ),
  224. 'readonly' => true,
  225. ),
  226. 'default_timezone' => array(
  227. 'description' => __( 'Default timezone.', 'woocommerce' ),
  228. 'type' => 'string',
  229. 'context' => array( 'view' ),
  230. 'readonly' => true,
  231. ),
  232. 'fsockopen_or_curl_enabled' => array(
  233. 'description' => __( 'Is fsockopen/cURL enabled?', 'woocommerce' ),
  234. 'type' => 'boolean',
  235. 'context' => array( 'view' ),
  236. 'readonly' => true,
  237. ),
  238. 'soapclient_enabled' => array(
  239. 'description' => __( 'Is SoapClient class enabled?', 'woocommerce' ),
  240. 'type' => 'boolean',
  241. 'context' => array( 'view' ),
  242. 'readonly' => true,
  243. ),
  244. 'domdocument_enabled' => array(
  245. 'description' => __( 'Is DomDocument class enabled?', 'woocommerce' ),
  246. 'type' => 'boolean',
  247. 'context' => array( 'view' ),
  248. 'readonly' => true,
  249. ),
  250. 'gzip_enabled' => array(
  251. 'description' => __( 'Is GZip enabled?', 'woocommerce' ),
  252. 'type' => 'boolean',
  253. 'context' => array( 'view' ),
  254. 'readonly' => true,
  255. ),
  256. 'mbstring_enabled' => array(
  257. 'description' => __( 'Is mbstring enabled?', 'woocommerce' ),
  258. 'type' => 'boolean',
  259. 'context' => array( 'view' ),
  260. 'readonly' => true,
  261. ),
  262. 'remote_post_successful' => array(
  263. 'description' => __( 'Remote POST successful?', 'woocommerce' ),
  264. 'type' => 'boolean',
  265. 'context' => array( 'view' ),
  266. 'readonly' => true,
  267. ),
  268. 'remote_post_response' => array(
  269. 'description' => __( 'Remote POST response.', 'woocommerce' ),
  270. 'type' => 'string',
  271. 'context' => array( 'view' ),
  272. 'readonly' => true,
  273. ),
  274. 'remote_get_successful' => array(
  275. 'description' => __( 'Remote GET successful?', 'woocommerce' ),
  276. 'type' => 'boolean',
  277. 'context' => array( 'view' ),
  278. 'readonly' => true,
  279. ),
  280. 'remote_get_response' => array(
  281. 'description' => __( 'Remote GET response.', 'woocommerce' ),
  282. 'type' => 'string',
  283. 'context' => array( 'view' ),
  284. 'readonly' => true,
  285. ),
  286. ),
  287. ),
  288. 'database' => array(
  289. 'description' => __( 'Database.', 'woocommerce' ),
  290. 'type' => 'object',
  291. 'context' => array( 'view' ),
  292. 'readonly' => true,
  293. 'properties' => array(
  294. 'wc_database_version' => array(
  295. 'description' => __( 'WC database version.', 'woocommerce' ),
  296. 'type' => 'string',
  297. 'context' => array( 'view' ),
  298. 'readonly' => true,
  299. ),
  300. 'database_prefix' => array(
  301. 'description' => __( 'Database prefix.', 'woocommerce' ),
  302. 'type' => 'string',
  303. 'context' => array( 'view' ),
  304. 'readonly' => true,
  305. ),
  306. 'maxmind_geoip_database' => array(
  307. 'description' => __( 'MaxMind GeoIP database.', 'woocommerce' ),
  308. 'type' => 'string',
  309. 'context' => array( 'view' ),
  310. 'readonly' => true,
  311. ),
  312. 'database_tables' => array(
  313. 'description' => __( 'Database tables.', 'woocommerce' ),
  314. 'type' => 'array',
  315. 'context' => array( 'view' ),
  316. 'readonly' => true,
  317. 'items' => array(
  318. 'type' => 'string',
  319. ),
  320. ),
  321. ),
  322. ),
  323. 'active_plugins' => array(
  324. 'description' => __( 'Active plugins.', 'woocommerce' ),
  325. 'type' => 'array',
  326. 'context' => array( 'view' ),
  327. 'readonly' => true,
  328. 'items' => array(
  329. 'type' => 'string',
  330. ),
  331. ),
  332. 'theme' => array(
  333. 'description' => __( 'Theme.', 'woocommerce' ),
  334. 'type' => 'object',
  335. 'context' => array( 'view' ),
  336. 'readonly' => true,
  337. 'properties' => array(
  338. 'name' => array(
  339. 'description' => __( 'Theme name.', 'woocommerce' ),
  340. 'type' => 'string',
  341. 'context' => array( 'view' ),
  342. 'readonly' => true,
  343. ),
  344. 'version' => array(
  345. 'description' => __( 'Theme version.', 'woocommerce' ),
  346. 'type' => 'string',
  347. 'context' => array( 'view' ),
  348. 'readonly' => true,
  349. ),
  350. 'version_latest' => array(
  351. 'description' => __( 'Latest version of theme.', 'woocommerce' ),
  352. 'type' => 'string',
  353. 'context' => array( 'view' ),
  354. 'readonly' => true,
  355. ),
  356. 'author_url' => array(
  357. 'description' => __( 'Theme author URL.', 'woocommerce' ),
  358. 'type' => 'string',
  359. 'format' => 'uri',
  360. 'context' => array( 'view' ),
  361. 'readonly' => true,
  362. ),
  363. 'is_child_theme' => array(
  364. 'description' => __( 'Is this theme a child theme?', 'woocommerce' ),
  365. 'type' => 'boolean',
  366. 'context' => array( 'view' ),
  367. 'readonly' => true,
  368. ),
  369. 'has_woocommerce_support' => array(
  370. 'description' => __( 'Does the theme declare WooCommerce support?', 'woocommerce' ),
  371. 'type' => 'boolean',
  372. 'context' => array( 'view' ),
  373. 'readonly' => true,
  374. ),
  375. 'has_woocommerce_file' => array(
  376. 'description' => __( 'Does the theme have a woocommerce.php file?', 'woocommerce' ),
  377. 'type' => 'boolean',
  378. 'context' => array( 'view' ),
  379. 'readonly' => true,
  380. ),
  381. 'has_outdated_templates' => array(
  382. 'description' => __( 'Does this theme have outdated templates?', 'woocommerce' ),
  383. 'type' => 'boolean',
  384. 'context' => array( 'view' ),
  385. 'readonly' => true,
  386. ),
  387. 'overrides' => array(
  388. 'description' => __( 'Template overrides.', 'woocommerce' ),
  389. 'type' => 'array',
  390. 'context' => array( 'view' ),
  391. 'readonly' => true,
  392. 'items' => array(
  393. 'type' => 'string',
  394. ),
  395. ),
  396. 'parent_name' => array(
  397. 'description' => __( 'Parent theme name.', 'woocommerce' ),
  398. 'type' => 'string',
  399. 'context' => array( 'view' ),
  400. 'readonly' => true,
  401. ),
  402. 'parent_version' => array(
  403. 'description' => __( 'Parent theme version.', 'woocommerce' ),
  404. 'type' => 'string',
  405. 'context' => array( 'view' ),
  406. 'readonly' => true,
  407. ),
  408. 'parent_author_url' => array(
  409. 'description' => __( 'Parent theme author URL.', 'woocommerce' ),
  410. 'type' => 'string',
  411. 'format' => 'uri',
  412. 'context' => array( 'view' ),
  413. 'readonly' => true,
  414. ),
  415. ),
  416. ),
  417. 'settings' => array(
  418. 'description' => __( 'Settings.', 'woocommerce' ),
  419. 'type' => 'object',
  420. 'context' => array( 'view' ),
  421. 'readonly' => true,
  422. 'properties' => array(
  423. 'api_enabled' => array(
  424. 'description' => __( 'REST API enabled?', 'woocommerce' ),
  425. 'type' => 'boolean',
  426. 'context' => array( 'view' ),
  427. 'readonly' => true,
  428. ),
  429. 'force_ssl' => array(
  430. 'description' => __( 'SSL forced?', 'woocommerce' ),
  431. 'type' => 'boolean',
  432. 'context' => array( 'view' ),
  433. 'readonly' => true,
  434. ),
  435. 'currency' => array(
  436. 'description' => __( 'Currency.', 'woocommerce' ),
  437. 'type' => 'string',
  438. 'context' => array( 'view' ),
  439. 'readonly' => true,
  440. ),
  441. 'currency_symbol' => array(
  442. 'description' => __( 'Currency symbol.', 'woocommerce' ),
  443. 'type' => 'string',
  444. 'context' => array( 'view' ),
  445. 'readonly' => true,
  446. ),
  447. 'currency_position' => array(
  448. 'description' => __( 'Currency position.', 'woocommerce' ),
  449. 'type' => 'string',
  450. 'context' => array( 'view' ),
  451. 'readonly' => true,
  452. ),
  453. 'thousand_separator' => array(
  454. 'description' => __( 'Thousand separator.', 'woocommerce' ),
  455. 'type' => 'string',
  456. 'context' => array( 'view' ),
  457. 'readonly' => true,
  458. ),
  459. 'decimal_separator' => array(
  460. 'description' => __( 'Decimal separator.', 'woocommerce' ),
  461. 'type' => 'string',
  462. 'context' => array( 'view' ),
  463. 'readonly' => true,
  464. ),
  465. 'number_of_decimals' => array(
  466. 'description' => __( 'Number of decimals.', 'woocommerce' ),
  467. 'type' => 'integer',
  468. 'context' => array( 'view' ),
  469. 'readonly' => true,
  470. ),
  471. 'geolocation_enabled' => array(
  472. 'description' => __( 'Geolocation enabled?', 'woocommerce' ),
  473. 'type' => 'boolean',
  474. 'context' => array( 'view' ),
  475. 'readonly' => true,
  476. ),
  477. 'taxonomies' => array(
  478. 'description' => __( 'Taxonomy terms for product/order statuses.', 'woocommerce' ),
  479. 'type' => 'array',
  480. 'context' => array( 'view' ),
  481. 'readonly' => true,
  482. 'items' => array(
  483. 'type' => 'string',
  484. ),
  485. ),
  486. 'product_visibility_terms' => array(
  487. 'description' => __( 'Terms in the product visibility taxonomy.', 'woocommerce' ),
  488. 'type' => 'array',
  489. 'context' => array( 'view' ),
  490. 'readonly' => true,
  491. 'items' => array(
  492. 'type' => 'string',
  493. ),
  494. ),
  495. ),
  496. ),
  497. 'security' => array(
  498. 'description' => __( 'Security.', 'woocommerce' ),
  499. 'type' => 'object',
  500. 'context' => array( 'view' ),
  501. 'readonly' => true,
  502. 'properties' => array(
  503. 'secure_connection' => array(
  504. 'description' => __( 'Is the connection to your store secure?', 'woocommerce' ),
  505. 'type' => 'boolean',
  506. 'context' => array( 'view' ),
  507. 'readonly' => true,
  508. ),
  509. 'hide_errors' => array(
  510. 'description' => __( 'Hide errors from visitors?', 'woocommerce' ),
  511. 'type' => 'boolean',
  512. 'context' => array( 'view' ),
  513. 'readonly' => true,
  514. ),
  515. ),
  516. ),
  517. 'pages' => array(
  518. 'description' => __( 'WooCommerce pages.', 'woocommerce' ),
  519. 'type' => 'array',
  520. 'context' => array( 'view' ),
  521. 'readonly' => true,
  522. 'items' => array(
  523. 'type' => 'string',
  524. ),
  525. ),
  526. ),
  527. );
  528. return $this->add_additional_fields_schema( $schema );
  529. }
  530. /**
  531. * Return an array of sections and the data associated with each.
  532. *
  533. * @return array
  534. */
  535. public function get_item_mappings() {
  536. return array(
  537. 'environment' => $this->get_environment_info(),
  538. 'database' => $this->get_database_info(),
  539. 'active_plugins' => $this->get_active_plugins(),
  540. 'theme' => $this->get_theme_info(),
  541. 'settings' => $this->get_settings(),
  542. 'security' => $this->get_security_info(),
  543. 'pages' => $this->get_pages(),
  544. );
  545. }
  546. /**
  547. * Get array of environment information. Includes thing like software
  548. * versions, and various server settings.
  549. *
  550. * @return array
  551. */
  552. public function get_environment_info() {
  553. global $wpdb;
  554. // Figure out cURL version, if installed.
  555. $curl_version = '';
  556. if ( function_exists( 'curl_version' ) ) {
  557. $curl_version = curl_version();
  558. $curl_version = $curl_version['version'] . ', ' . $curl_version['ssl_version'];
  559. }
  560. // WP memory limit.
  561. $wp_memory_limit = wc_let_to_num( WP_MEMORY_LIMIT );
  562. if ( function_exists( 'memory_get_usage' ) ) {
  563. $wp_memory_limit = max( $wp_memory_limit, wc_let_to_num( @ini_get( 'memory_limit' ) ) );
  564. }
  565. // Test POST requests.
  566. $post_response = wp_safe_remote_post(
  567. 'https://www.paypal.com/cgi-bin/webscr',
  568. array(
  569. 'timeout' => 10,
  570. 'user-agent' => 'WooCommerce/' . WC()->version,
  571. 'httpversion' => '1.1',
  572. 'body' => array(
  573. 'cmd' => '_notify-validate',
  574. ),
  575. )
  576. );
  577. $post_response_successful = false;
  578. if ( ! is_wp_error( $post_response ) && $post_response['response']['code'] >= 200 && $post_response['response']['code'] < 300 ) {
  579. $post_response_successful = true;
  580. }
  581. // Test GET requests.
  582. $get_response = wp_safe_remote_get( 'https://woocommerce.com/wc-api/product-key-api?request=ping&network=' . ( is_multisite() ? '1' : '0' ) );
  583. $get_response_successful = false;
  584. if ( ! is_wp_error( $post_response ) && $post_response['response']['code'] >= 200 && $post_response['response']['code'] < 300 ) {
  585. $get_response_successful = true;
  586. }
  587. $database_version = wc_get_server_database_version();
  588. // Return all environment info. Described by JSON Schema.
  589. return array(
  590. 'home_url' => get_option( 'home' ),
  591. 'site_url' => get_option( 'siteurl' ),
  592. 'version' => WC()->version,
  593. 'log_directory' => WC_LOG_DIR,
  594. 'log_directory_writable' => (bool) @fopen( WC_LOG_DIR . 'test-log.log', 'a' ),
  595. 'wp_version' => get_bloginfo( 'version' ),
  596. 'wp_multisite' => is_multisite(),
  597. 'wp_memory_limit' => $wp_memory_limit,
  598. 'wp_debug_mode' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
  599. 'wp_cron' => ! ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ),
  600. 'language' => get_locale(),
  601. 'external_object_cache' => wp_using_ext_object_cache(),
  602. 'server_info' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? wc_clean( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '',
  603. 'php_version' => phpversion(),
  604. 'php_post_max_size' => wc_let_to_num( ini_get( 'post_max_size' ) ),
  605. 'php_max_execution_time' => ini_get( 'max_execution_time' ),
  606. 'php_max_input_vars' => ini_get( 'max_input_vars' ),
  607. 'curl_version' => $curl_version,
  608. 'suhosin_installed' => extension_loaded( 'suhosin' ),
  609. 'max_upload_size' => wp_max_upload_size(),
  610. 'mysql_version' => $database_version['number'],
  611. 'mysql_version_string' => $database_version['string'],
  612. 'default_timezone' => date_default_timezone_get(),
  613. 'fsockopen_or_curl_enabled' => ( function_exists( 'fsockopen' ) || function_exists( 'curl_init' ) ),
  614. 'soapclient_enabled' => class_exists( 'SoapClient' ),
  615. 'domdocument_enabled' => class_exists( 'DOMDocument' ),
  616. 'gzip_enabled' => is_callable( 'gzopen' ),
  617. 'mbstring_enabled' => extension_loaded( 'mbstring' ),
  618. 'remote_post_successful' => $post_response_successful,
  619. 'remote_post_response' => ( is_wp_error( $post_response ) ? $post_response->get_error_message() : $post_response['response']['code'] ),
  620. 'remote_get_successful' => $get_response_successful,
  621. 'remote_get_response' => ( is_wp_error( $get_response ) ? $get_response->get_error_message() : $get_response['response']['code'] ),
  622. );
  623. }
  624. /**
  625. * Add prefix to table.
  626. *
  627. * @param string $table Table name.
  628. * @return stromg
  629. */
  630. protected function add_db_table_prefix( $table ) {
  631. global $wpdb;
  632. return $wpdb->prefix . $table;
  633. }
  634. /**
  635. * Get array of database information. Version, prefix, and table existence.
  636. *
  637. * @return array
  638. */
  639. public function get_database_info() {
  640. global $wpdb;
  641. $database_table_sizes = $wpdb->get_results(
  642. $wpdb->prepare(
  643. "SELECT
  644. table_name AS 'name',
  645. round( ( data_length / 1024 / 1024 ), 2 ) 'data',
  646. round( ( index_length / 1024 / 1024 ), 2 ) 'index'
  647. FROM information_schema.TABLES
  648. WHERE table_schema = %s
  649. ORDER BY name ASC;",
  650. DB_NAME
  651. )
  652. );
  653. // WC Core tables to check existence of.
  654. $core_tables = apply_filters(
  655. 'woocommerce_database_tables',
  656. array(
  657. 'woocommerce_sessions',
  658. 'woocommerce_api_keys',
  659. 'woocommerce_attribute_taxonomies',
  660. 'woocommerce_downloadable_product_permissions',
  661. 'woocommerce_order_items',
  662. 'woocommerce_order_itemmeta',
  663. 'woocommerce_tax_rates',
  664. 'woocommerce_tax_rate_locations',
  665. 'woocommerce_shipping_zones',
  666. 'woocommerce_shipping_zone_locations',
  667. 'woocommerce_shipping_zone_methods',
  668. 'woocommerce_payment_tokens',
  669. 'woocommerce_payment_tokenmeta',
  670. 'woocommerce_log',
  671. )
  672. );
  673. if ( get_option( 'db_version' ) < 34370 ) {
  674. $core_tables[] = 'woocommerce_termmeta';
  675. }
  676. /**
  677. * Adding the prefix to the tables array, for backwards compatibility.
  678. *
  679. * If we changed the tables above to include the prefix, then any filters against that table could break.
  680. */
  681. $core_tables = array_map( array( $this, 'add_db_table_prefix' ), $core_tables );
  682. /**
  683. * Organize WooCommerce and non-WooCommerce tables separately for display purposes later.
  684. *
  685. * To ensure we include all WC tables, even if they do not exist, pre-populate the WC array with all the tables.
  686. */
  687. $tables = array(
  688. 'woocommerce' => array_fill_keys( $core_tables, false ),
  689. 'other' => array(),
  690. );
  691. $database_size = array(
  692. 'data' => 0,
  693. 'index' => 0,
  694. );
  695. foreach ( $database_table_sizes as $table ) {
  696. $table_type = in_array( $table->name, $core_tables ) ? 'woocommerce' : 'other';
  697. $tables[ $table_type ][ $table->name ] = array(
  698. 'data' => $table->data,
  699. 'index' => $table->index,
  700. );
  701. $database_size['data'] += $table->data;
  702. $database_size['index'] += $table->index;
  703. }
  704. // Return all database info. Described by JSON Schema.
  705. return array(
  706. 'wc_database_version' => get_option( 'woocommerce_db_version' ),
  707. 'database_prefix' => $wpdb->prefix,
  708. 'maxmind_geoip_database' => WC_Geolocation::get_local_database_path(),
  709. 'database_tables' => $tables,
  710. 'database_size' => $database_size,
  711. );
  712. }
  713. /**
  714. * Get array of counts of objects. Orders, products, etc.
  715. *
  716. * @return array
  717. */
  718. public function get_post_type_counts() {
  719. global $wpdb;
  720. $post_type_counts = $wpdb->get_results( "SELECT post_type AS 'type', count(1) AS 'count' FROM {$wpdb->posts} GROUP BY post_type;" );
  721. return is_array( $post_type_counts ) ? $post_type_counts : array();
  722. }
  723. /**
  724. * Get a list of plugins active on the site.
  725. *
  726. * @return array
  727. */
  728. public function get_active_plugins() {
  729. require_once ABSPATH . 'wp-admin/includes/plugin.php';
  730. require_once ABSPATH . 'wp-admin/includes/update.php';
  731. if ( ! function_exists( 'get_plugin_updates' ) ) {
  732. return array();
  733. }
  734. // Get both site plugins and network plugins.
  735. $active_plugins = (array) get_option( 'active_plugins', array() );
  736. if ( is_multisite() ) {
  737. $network_activated_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
  738. $active_plugins = array_merge( $active_plugins, $network_activated_plugins );
  739. }
  740. $active_plugins_data = array();
  741. $available_updates = get_plugin_updates();
  742. foreach ( $active_plugins as $plugin ) {
  743. $data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
  744. $dirname = dirname( $plugin );
  745. $version_latest = '';
  746. $slug = explode( '/', $plugin );
  747. $slug = explode( '.', end( $slug ) );
  748. $slug = $slug[0];
  749. if ( 'woocommerce' !== $slug && ( strstr( $data['PluginURI'], 'woothemes.com' ) || strstr( $data['PluginURI'], 'woocommerce.com' ) ) ) {
  750. $version_data = get_transient( md5( $plugin ) . '_version_data' );
  751. if ( false === $version_data ) {
  752. $changelog = wp_safe_remote_get( 'http://dzv365zjfbd8v.cloudfront.net/changelogs/' . $dirname . '/changelog.txt' );
  753. $cl_lines = explode( "\n", wp_remote_retrieve_body( $changelog ) );
  754. if ( ! empty( $cl_lines ) ) {
  755. foreach ( $cl_lines as $line_num => $cl_line ) {
  756. if ( preg_match( '/^[0-9]/', $cl_line ) ) {
  757. $date = str_replace( '.', '-', trim( substr( $cl_line, 0, strpos( $cl_line, '-' ) ) ) );
  758. $version = preg_replace( '~[^0-9,.]~', '', stristr( $cl_line, 'version' ) );
  759. $update = trim( str_replace( '*', '', $cl_lines[ $line_num + 1 ] ) );
  760. $version_data = array(
  761. 'date' => $date,
  762. 'version' => $version,
  763. 'update' => $update,
  764. 'changelog' => $changelog,
  765. );
  766. set_transient( md5( $plugin ) . '_version_data', $version_data, DAY_IN_SECONDS );
  767. break;
  768. }
  769. }
  770. }
  771. }
  772. $version_latest = $version_data['version'];
  773. } elseif ( isset( $available_updates[ $plugin ]->update->new_version ) ) {
  774. $version_latest = $available_updates[ $plugin ]->update->new_version;
  775. }
  776. // convert plugin data to json response format.
  777. $active_plugins_data[] = array(
  778. 'plugin' => $plugin,
  779. 'name' => $data['Name'],
  780. 'version' => $data['Version'],
  781. 'version_latest' => $version_latest,
  782. 'url' => $data['PluginURI'],
  783. 'author_name' => $data['AuthorName'],
  784. 'author_url' => esc_url_raw( $data['AuthorURI'] ),
  785. 'network_activated' => $data['Network'],
  786. );
  787. }
  788. return $active_plugins_data;
  789. }
  790. /**
  791. * Get info on the current active theme, info on parent theme (if presnet)
  792. * and a list of template overrides.
  793. *
  794. * @return array
  795. */
  796. public function get_theme_info() {
  797. $active_theme = wp_get_theme();
  798. // Get parent theme info if this theme is a child theme, otherwise
  799. // pass empty info in the response.
  800. if ( is_child_theme() ) {
  801. $parent_theme = wp_get_theme( $active_theme->template );
  802. $parent_theme_info = array(
  803. 'parent_name' => $parent_theme->name,
  804. 'parent_version' => $parent_theme->version,
  805. 'parent_version_latest' => WC_Admin_Status::get_latest_theme_version( $parent_theme ),
  806. 'parent_author_url' => $parent_theme->{'Author URI'},
  807. );
  808. } else {
  809. $parent_theme_info = array(
  810. 'parent_name' => '',
  811. 'parent_version' => '',
  812. 'parent_version_latest' => '',
  813. 'parent_author_url' => '',
  814. );
  815. }
  816. /**
  817. * Scan the theme directory for all WC templates to see if our theme
  818. * overrides any of them.
  819. */
  820. $override_files = array();
  821. $outdated_templates = false;
  822. $scan_files = WC_Admin_Status::scan_template_files( WC()->plugin_path() . '/templates/' );
  823. foreach ( $scan_files as $file ) {
  824. $located = apply_filters( 'wc_get_template', $file, $file, array(), WC()->template_path(), WC()->plugin_path() . '/templates/' );
  825. if ( file_exists( $located ) ) {
  826. $theme_file = $located;
  827. } elseif ( file_exists( get_stylesheet_directory() . '/' . $file ) ) {
  828. $theme_file = get_stylesheet_directory() . '/' . $file;
  829. } elseif ( file_exists( get_stylesheet_directory() . '/' . WC()->template_path() . $file ) ) {
  830. $theme_file = get_stylesheet_directory() . '/' . WC()->template_path() . $file;
  831. } elseif ( file_exists( get_template_directory() . '/' . $file ) ) {
  832. $theme_file = get_template_directory() . '/' . $file;
  833. } elseif ( file_exists( get_template_directory() . '/' . WC()->template_path() . $file ) ) {
  834. $theme_file = get_template_directory() . '/' . WC()->template_path() . $file;
  835. } else {
  836. $theme_file = false;
  837. }
  838. if ( ! empty( $theme_file ) ) {
  839. $core_version = WC_Admin_Status::get_file_version( WC()->plugin_path() . '/templates/' . $file );
  840. $theme_version = WC_Admin_Status::get_file_version( $theme_file );
  841. if ( $core_version && ( empty( $theme_version ) || version_compare( $theme_version, $core_version, '<' ) ) ) {
  842. if ( ! $outdated_templates ) {
  843. $outdated_templates = true;
  844. }
  845. }
  846. $override_files[] = array(
  847. 'file' => str_replace( WP_CONTENT_DIR . '/themes/', '', $theme_file ),
  848. 'version' => $theme_version,
  849. 'core_version' => $core_version,
  850. );
  851. }
  852. }
  853. $active_theme_info = array(
  854. 'name' => $active_theme->name,
  855. 'version' => $active_theme->version,
  856. 'version_latest' => WC_Admin_Status::get_latest_theme_version( $active_theme ),
  857. 'author_url' => esc_url_raw( $active_theme->{'Author URI'} ),
  858. 'is_child_theme' => is_child_theme(),
  859. 'has_woocommerce_support' => current_theme_supports( 'woocommerce' ),
  860. 'has_woocommerce_file' => ( file_exists( get_stylesheet_directory() . '/woocommerce.php' ) || file_exists( get_template_directory() . '/woocommerce.php' ) ),
  861. 'has_outdated_templates' => $outdated_templates,
  862. 'overrides' => $override_files,
  863. );
  864. return array_merge( $active_theme_info, $parent_theme_info );
  865. }
  866. /**
  867. * Get some setting values for the site that are useful for debugging
  868. * purposes. For full settings access, use the settings api.
  869. *
  870. * @return array
  871. */
  872. public function get_settings() {
  873. // Get a list of terms used for product/order taxonomies.
  874. $term_response = array();
  875. $terms = get_terms( 'product_type', array( 'hide_empty' => 0 ) );
  876. foreach ( $terms as $term ) {
  877. $term_response[ $term->slug ] = strtolower( $term->name );
  878. }
  879. // Get a list of terms used for product visibility.
  880. $product_visibility_terms = array();
  881. $terms = get_terms( 'product_visibility', array( 'hide_empty' => 0 ) );
  882. foreach ( $terms as $term ) {
  883. $product_visibility_terms[ $term->slug ] = strtolower( $term->name );
  884. }
  885. // Return array of useful settings for debugging.
  886. return array(
  887. 'api_enabled' => 'yes' === get_option( 'woocommerce_api_enabled' ),
  888. 'force_ssl' => 'yes' === get_option( 'woocommerce_force_ssl_checkout' ),
  889. 'currency' => get_woocommerce_currency(),
  890. 'currency_symbol' => get_woocommerce_currency_symbol(),
  891. 'currency_position' => get_option( 'woocommerce_currency_pos' ),
  892. 'thousand_separator' => wc_get_price_thousand_separator(),
  893. 'decimal_separator' => wc_get_price_decimal_separator(),
  894. 'number_of_decimals' => wc_get_price_decimals(),
  895. 'geolocation_enabled' => in_array( get_option( 'woocommerce_default_customer_address' ), array( 'geolocation_ajax', 'geolocation' ) ),
  896. 'taxonomies' => $term_response,
  897. 'product_visibility_terms' => $product_visibility_terms,
  898. );
  899. }
  900. /**
  901. * Returns security tips.
  902. *
  903. * @return array
  904. */
  905. public function get_security_info() {
  906. $check_page = 0 < wc_get_page_id( 'shop' ) ? get_permalink( wc_get_page_id( 'shop' ) ) : get_home_url();
  907. return array(
  908. 'secure_connection' => 'https' === substr( $check_page, 0, 5 ),
  909. 'hide_errors' => ! ( defined( 'WP_DEBUG' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG && WP_DEBUG_DISPLAY ) || 0 === intval( ini_get( 'display_errors' ) ),
  910. );
  911. }
  912. /**
  913. * Returns a mini-report on WC pages and if they are configured correctly:
  914. * Present, visible, and including the correct shortcode.
  915. *
  916. * @return array
  917. */
  918. public function get_pages() {
  919. // WC pages to check against.
  920. $check_pages = array(
  921. _x( 'Shop base', 'Page setting', 'woocommerce' ) => array(
  922. 'option' => 'woocommerce_shop_page_id',
  923. 'shortcode' => '',
  924. ),
  925. _x( 'Cart', 'Page setting', 'woocommerce' ) => array(
  926. 'option' => 'woocommerce_cart_page_id',
  927. 'shortcode' => '[' . apply_filters( 'woocommerce_cart_shortcode_tag', 'woocommerce_cart' ) . ']',
  928. ),
  929. _x( 'Checkout', 'Page setting', 'woocommerce' ) => array(
  930. 'option' => 'woocommerce_checkout_page_id',
  931. 'shortcode' => '[' . apply_filters( 'woocommerce_checkout_shortcode_tag', 'woocommerce_checkout' ) . ']',
  932. ),
  933. _x( 'My account', 'Page setting', 'woocommerce' ) => array(
  934. 'option' => 'woocommerce_myaccount_page_id',
  935. 'shortcode' => '[' . apply_filters( 'woocommerce_my_account_shortcode_tag', 'woocommerce_my_account' ) . ']',
  936. ),
  937. _x( 'Terms and conditions', 'Page setting', 'woocommerce' ) => array(
  938. 'option' => 'woocommerce_terms_page_id',
  939. 'shortcode' => '',
  940. ),
  941. );
  942. $pages_output = array();
  943. foreach ( $check_pages as $page_name => $values ) {
  944. $page_id = get_option( $values['option'] );
  945. $page_set = false;
  946. $page_exists = false;
  947. $page_visible = false;
  948. $shortcode_present = false;
  949. $shortcode_required = false;
  950. // Page checks.
  951. if ( $page_id ) {
  952. $page_set = true;
  953. }
  954. if ( get_post( $page_id ) ) {
  955. $page_exists = true;
  956. }
  957. if ( 'publish' === get_post_status( $page_id ) ) {
  958. $page_visible = true;
  959. }
  960. // Shortcode checks.
  961. if ( $values['shortcode'] && get_post( $page_id ) ) {
  962. $shortcode_required = true;
  963. $page = get_post( $page_id );
  964. if ( strstr( $page->post_content, $values['shortcode'] ) ) {
  965. $shortcode_present = true;
  966. }
  967. }
  968. // Wrap up our findings into an output array.
  969. $pages_output[] = array(
  970. 'page_name' => $page_name,
  971. 'page_id' => $page_id,
  972. 'page_set' => $page_set,
  973. 'page_exists' => $page_exists,
  974. 'page_visible' => $page_visible,
  975. 'shortcode' => $values['shortcode'],
  976. 'shortcode_required' => $shortcode_required,
  977. 'shortcode_present' => $shortcode_present,
  978. );
  979. }
  980. return $pages_output;
  981. }
  982. /**
  983. * Get any query params needed.
  984. *
  985. * @return array
  986. */
  987. public function get_collection_params() {
  988. return array(
  989. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
  990. );
  991. }
  992. /**
  993. * Prepare the system status response
  994. *
  995. * @param array $system_status System status data.
  996. * @param WP_REST_Request $request Request object.
  997. * @return WP_REST_Response
  998. */
  999. public function prepare_item_for_response( $system_status, $request ) {
  1000. $data = $this->add_additional_fields_to_object( $system_status, $request );
  1001. $data = $this->filter_response_by_context( $data, 'view' );
  1002. $response = rest_ensure_response( $data );
  1003. /**
  1004. * Filter the system status returned from the REST API.
  1005. *
  1006. * @param WP_REST_Response $response The response object.
  1007. * @param mixed $system_status System status
  1008. * @param WP_REST_Request $request Request object.
  1009. */
  1010. return apply_filters( 'woocommerce_rest_prepare_system_status', $response, $system_status, $request );
  1011. }
  1012. }