class-fl-updater.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. <?php
  2. /**
  3. * Manages remote updates for all Beaver Builder products.
  4. *
  5. * @since 1.0
  6. */
  7. final class FLUpdater {
  8. /**
  9. * The API URL for the Beaver Builder update server.
  10. *
  11. * @since 1.0
  12. * @access private
  13. * @var string $_updates_api_url
  14. */
  15. static private $_updates_api_url = 'http://updates.wpbeaverbuilder.com/';
  16. /**
  17. * An internal array of data for each product.
  18. *
  19. * @since 1.0
  20. * @access private
  21. * @var array $_products
  22. */
  23. static private $_products = array();
  24. /**
  25. * An internal array of remote responses with
  26. * update data for each product.
  27. *
  28. * @since 1.8.4
  29. * @access private
  30. * @var array $_responses
  31. */
  32. static private $_responses = array();
  33. /**
  34. * An internal array of settings for the updater instance.
  35. *
  36. * @since 1.0
  37. * @access private
  38. * @var array $settings
  39. */
  40. private $settings = array();
  41. /**
  42. * Updater constructor method.
  43. *
  44. * @since 1.0
  45. * @param array $settings An array of settings for this instance.
  46. * @return void
  47. */
  48. public function __construct( $settings = array() ) {
  49. $this->settings = $settings;
  50. if ( 'plugin' == $settings['type'] ) {
  51. add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'update_check' ) );
  52. add_filter( 'plugins_api', array( $this, 'plugin_info' ), 99, 3 );
  53. add_action( 'in_plugin_update_message-' . self::get_plugin_file( $settings['slug'] ), array( $this, 'update_message' ), 1, 2 );
  54. } elseif ( 'theme' == $settings['type'] ) {
  55. add_filter( 'pre_set_site_transient_update_themes', array( $this, 'update_check' ) );
  56. }
  57. }
  58. /**
  59. * Get the update data response from the API.
  60. *
  61. * @since 1.7.7
  62. * @return object
  63. */
  64. public function get_response() {
  65. $slug = $this->settings['slug'];
  66. if ( isset( FLUpdater::$_responses[ $slug ] ) ) {
  67. return FLUpdater::$_responses[ $slug ];
  68. }
  69. FLUpdater::$_responses[ $slug ] = FLUpdater::api_request( FLUpdater::$_updates_api_url, array(
  70. 'fl-api-method' => 'update_info',
  71. 'license' => FLUpdater::get_subscription_license(),
  72. 'domain' => network_home_url(),
  73. 'product' => $this->settings['name'],
  74. 'slug' => $this->settings['slug'],
  75. 'version' => $this->settings['version'],
  76. 'php' => phpversion(),
  77. ) );
  78. return FLUpdater::$_responses[ $slug ];
  79. }
  80. /**
  81. * Checks to see if an update is available for the current product.
  82. *
  83. * @since 1.0
  84. * @param object $transient A WordPress transient object with update data.
  85. * @return object
  86. */
  87. public function update_check( $transient ) {
  88. global $pagenow;
  89. if ( 'plugins.php' == $pagenow && is_multisite() ) {
  90. return $transient;
  91. }
  92. if ( ! is_object( $transient ) ) {
  93. $transient = new stdClass();
  94. }
  95. if ( ! isset( $transient->checked ) ) {
  96. $transient->checked = array();
  97. }
  98. $response = $this->get_response();
  99. if ( ! isset( $response->error ) ) {
  100. $transient->last_checked = time();
  101. $transient->checked[ $this->settings['slug'] ] = $this->settings['version'];
  102. if ( 'plugin' == $this->settings['type'] ) {
  103. $plugin = self::get_plugin_file( $this->settings['slug'] );
  104. if ( version_compare( $response->new_version, $this->settings['version'], '>' ) ) {
  105. $transient->response[ $plugin ] = new stdClass();
  106. $transient->response[ $plugin ]->slug = $response->slug;
  107. $transient->response[ $plugin ]->new_version = $response->new_version;
  108. $transient->response[ $plugin ]->url = $response->homepage;
  109. $transient->response[ $plugin ]->package = $response->package;
  110. $transient->response[ $plugin ]->tested = $response->tested;
  111. $transient->response[ $plugin ]->icons = apply_filters( 'fl_updater_icon', array(
  112. '1x' => FL_BUILDER_URL . 'img/beaver-128.png',
  113. '2x' => FL_BUILDER_URL . 'img/beaver-256.png',
  114. 'default' => FL_BUILDER_URL . 'img/beaver-256.png',
  115. ), $response, $this->settings );
  116. if ( empty( $response->package ) ) {
  117. $transient->response[ $plugin ]->upgrade_notice = FLUpdater::get_update_error_message();
  118. }
  119. }
  120. } elseif ( 'theme' == $this->settings['type'] ) {
  121. if ( version_compare( $response->new_version, $this->settings['version'], '>' ) ) {
  122. $transient->response[ $this->settings['slug'] ] = array(
  123. 'new_version' => $response->new_version,
  124. 'url' => $response->homepage,
  125. 'package' => $response->package,
  126. 'tested' => $response->tested,
  127. );
  128. }
  129. }
  130. }
  131. return $transient;
  132. }
  133. /**
  134. * Retrives the data for the plugin info lightbox.
  135. *
  136. * @since 1.0
  137. * @param bool $false
  138. * @param string $action
  139. * @param object $args
  140. * @return object|bool
  141. */
  142. public function plugin_info( $false, $action, $args ) {
  143. if ( 'plugin_information' != $action ) {
  144. return $false;
  145. }
  146. if ( ! isset( $args->slug ) || $args->slug != $this->settings['slug'] ) {
  147. return $false;
  148. }
  149. $response = $this->get_response();
  150. if ( ! isset( $response->error ) ) {
  151. $info = new stdClass();
  152. $info->name = $this->settings['name'];
  153. $info->version = $response->new_version;
  154. $info->slug = $response->slug;
  155. $info->plugin_name = $response->plugin_name;
  156. $info->author = $response->author;
  157. $info->homepage = $response->homepage;
  158. $info->requires = $response->requires;
  159. $info->tested = $response->tested;
  160. $info->last_updated = $response->last_updated;
  161. $info->download_link = $response->package;
  162. $info->sections = (array) $response->sections;
  163. return apply_filters( 'fl_plugin_info_data', $info, $response );
  164. }
  165. return $false;
  166. }
  167. /**
  168. * Shows an update message on the plugins page if an update
  169. * is available but there is no active subscription.
  170. *
  171. * @since 1.0
  172. * @param array $plugin_data An array of data for this plugin.
  173. * @param object $response An object with update data for this plugin.
  174. * @return void
  175. */
  176. public function update_message( $plugin_data, $response ) {
  177. if ( empty( $response->package ) ) {
  178. echo FLUpdater::get_update_error_message( $plugin_data );
  179. }
  180. }
  181. /**
  182. * Static method for initializing an instance of the updater
  183. * for each active product.
  184. *
  185. * @since 1.0
  186. * @return void
  187. */
  188. static public function init() {
  189. include FL_UPDATER_DIR . 'includes/config.php';
  190. foreach ( $config as $path ) {
  191. if ( file_exists( $path ) ) {
  192. require_once $path;
  193. }
  194. }
  195. }
  196. /**
  197. * Static method for adding a product to the updater and
  198. * creating the new instance.
  199. *
  200. * @since 1.0
  201. * @param array $args An array of settings for the product.
  202. * @return void
  203. */
  204. static public function add_product( $args = array() ) {
  205. if ( is_array( $args ) && isset( $args['slug'] ) ) {
  206. if ( 'plugin' == $args['type'] ) {
  207. if ( file_exists( WP_CONTENT_DIR . '/plugins/' . $args['slug'] ) ) {
  208. self::$_products[ $args['name'] ] = $args;
  209. new FLUpdater( self::$_products[ $args['name'] ] );
  210. }
  211. }
  212. if ( 'theme' == $args['type'] ) {
  213. if ( file_exists( WP_CONTENT_DIR . '/themes/' . $args['slug'] ) ) {
  214. self::$_products[ $args['name'] ] = $args;
  215. new FLUpdater( self::$_products[ $args['name'] ] );
  216. }
  217. }
  218. }
  219. }
  220. /**
  221. * Static method for rendering the license form.
  222. *
  223. * @since 1.0
  224. * @return void
  225. */
  226. static public function render_form() {
  227. // Activate a subscription?
  228. if ( isset( $_POST['fl-updater-nonce'] ) ) {
  229. if ( wp_verify_nonce( $_POST['fl-updater-nonce'], 'updater-nonce' ) ) {
  230. self::save_subscription_license( $_POST['license'] );
  231. }
  232. }
  233. $license = self::get_subscription_license();
  234. $subscription = self::get_subscription_info();
  235. // Include the form ui.
  236. include FL_UPDATER_DIR . 'includes/form.php';
  237. }
  238. /**
  239. * Renders available subscriptions and downloads.
  240. *
  241. * @since 1.10
  242. * @param object $subscription
  243. * @return void
  244. */
  245. static public function render_subscriptions( $subscription ) {
  246. if ( isset( $subscription->error ) || ! $subscription->active || ! $subscription->domain->active || ! isset( $subscription->downloads ) ) {
  247. return;
  248. }
  249. if ( ! FLBuilderModel::is_white_labeled() ) {
  250. include FL_UPDATER_DIR . 'includes/subscriptions.php';
  251. }
  252. }
  253. /**
  254. * Static method for getting the subscription license key.
  255. *
  256. * @since 1.0
  257. * @return string
  258. */
  259. static public function get_subscription_license() {
  260. $value = get_site_option( 'fl_themes_subscription_email' );
  261. return $value ? $value : '';
  262. }
  263. /**
  264. * Static method for updating the subscription license.
  265. *
  266. * @since 1.0
  267. * @param string $license The new license key.
  268. * @return $response mixed
  269. */
  270. static public function save_subscription_license( $license ) {
  271. $response = FLUpdater::api_request(self::$_updates_api_url, array(
  272. 'fl-api-method' => 'activate_domain',
  273. 'license' => $license,
  274. 'domain' => network_home_url(),
  275. 'products' => json_encode( self::$_products ),
  276. ));
  277. update_site_option( 'fl_themes_subscription_email', $license );
  278. return $response;
  279. }
  280. /**
  281. * Static method for retrieving the subscription info.
  282. *
  283. * @since 1.0
  284. * @return bool
  285. */
  286. static public function get_subscription_info() {
  287. return self::api_request(self::$_updates_api_url, array(
  288. 'fl-api-method' => 'subscription_info',
  289. 'domain' => network_home_url(),
  290. 'license' => FLUpdater::get_subscription_license(),
  291. ));
  292. }
  293. /**
  294. * Returns an update message for if an update
  295. * is available but there is no active subscription.
  296. *
  297. * @since 1.6.4.3
  298. * @param array $plugin_data An array of data for this plugin.
  299. * @return string
  300. */
  301. static private function get_update_error_message( $plugin_data = null ) {
  302. $subscription = FLUpdater::get_subscription_info();
  303. $license = get_site_option( 'fl_themes_subscription_email' );
  304. $message = '';
  305. // updates-core.php
  306. if ( ! $plugin_data ) {
  307. if ( ! $license ) {
  308. $message = __( 'Please enter a valid license key to enable automatic updates.', 'fl-builder' );
  309. } else {
  310. $message = __( 'Please subscribe to enable automatic updates for this plugin.', 'fl-builder' );
  311. }
  312. } else { // plugins.php
  313. if ( ! $license ) {
  314. $link = sprintf( '<a href="%s" target="_blank" style="color: #fff; text-decoration: underline;">%s &raquo;</a>', admin_url( '/options-general.php?page=fl-builder-settings#license' ), __( 'Enter License Key', 'fl-builder' ) );
  315. $text = sprintf( __( 'Please enter a valid license key to enable automatic updates. %s', 'fl-builder' ), $link );
  316. } else {
  317. $link = sprintf( '<a href="%s" target="_blank" style="color: #fff; text-decoration: underline;">%s &raquo;</a>', $plugin_data['PluginURI'], __( 'Subscribe Now', 'fl-builder' ) );
  318. $text = sprintf( __( 'Please subscribe to enable automatic updates for this plugin. %s', 'fl-builder' ), $link );
  319. }
  320. $message .= '<span style="display:block;padding:10px 20px;margin:10px 0; background: #d54e21; color: #fff;">';
  321. $message .= __( '<strong>UPDATE UNAVAILABLE!</strong>', 'fl-builder' );
  322. $message .= '&nbsp;&nbsp;&nbsp;';
  323. $message .= $text;
  324. $message .= '</span>';
  325. }
  326. return $message;
  327. }
  328. /**
  329. * Static method for retrieving the plugin file path for a
  330. * product relative to the plugins directory.
  331. *
  332. * @since 1.0
  333. * @access private
  334. * @param string $slug The product slug.
  335. * @return string
  336. */
  337. static private function get_plugin_file( $slug ) {
  338. if ( 'bb-plugin' == $slug ) {
  339. $file = $slug . '/fl-builder.php';
  340. } else {
  341. $file = $slug . '/' . $slug . '.php';
  342. }
  343. return $file;
  344. }
  345. /**
  346. * Static method for sending a request to the store
  347. * or update API.
  348. *
  349. * @since 1.0
  350. * @access private
  351. * @param string $api_url The API URL to use.
  352. * @param array $args An array of args to send along with the request.
  353. * @return mixed The response or false if there is an error.
  354. */
  355. static private function api_request( $api_url = false, $args = array() ) {
  356. if ( $api_url ) {
  357. $params = array();
  358. foreach ( $args as $key => $val ) {
  359. $params[] = $key . '=' . urlencode( $val );
  360. }
  361. return self::remote_get( $api_url . '?' . implode( '&', $params ) );
  362. }
  363. return false;
  364. }
  365. /**
  366. * Get a remote response.
  367. *
  368. * @since 1.0
  369. * @access private
  370. * @param string $url The URL to get.
  371. * @return mixed The response or false if there is an error.
  372. */
  373. static private function remote_get( $url ) {
  374. $request = wp_remote_get( $url );
  375. $error = new stdClass();
  376. $error->error = 'connection';
  377. if ( is_wp_error( $request ) ) {
  378. return $error;
  379. }
  380. if ( wp_remote_retrieve_response_code( $request ) != 200 ) {
  381. return $error;
  382. }
  383. $body = wp_remote_retrieve_body( $request );
  384. if ( is_wp_error( $body ) ) {
  385. return $error;
  386. }
  387. $body_decoded = json_decode( $body );
  388. if ( ! is_object( $body_decoded ) ) {
  389. return $error;
  390. }
  391. return $body_decoded;
  392. }
  393. }