diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b7e87e2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.DS_Store +composer.lock +/node_modules/ +/vendor/ +/logs/ +/reports/ diff --git a/404.php b/404.php index cdf2e4e4..2c7be533 100644 --- a/404.php +++ b/404.php @@ -1,12 +1,13 @@ __( 'Post Options', 'tm-beans' ) ) ); - } diff --git a/lib/admin/updater.php b/lib/admin/updater.php index ed21af1d..9b1b5fb6 100644 --- a/lib/admin/updater.php +++ b/lib/admin/updater.php @@ -2,26 +2,34 @@ /** * Handles Beans updates. * - * @package Beans - * @since 1.0.0 + * @package Beans\Framework\Admin + * + * @since 1.0.0 */ add_filter( 'site_transient_update_themes', 'beans_updater' ); /** - * Retrieve product data from Beans REST API. + * Retrieve product data from the Beans REST API. * - * Data are cached in a 24 hours transients and will be returned if found to avoid long loading time. + * Data is cached in a transient for 24 hours. Product data will only be retrieved + * if no transient is found to avoid long loading times. * + * @since 1.0.0 * @ignore + * @access private + * + * @param object $value Update check object. + * + * @return object Modified update check object. */ function beans_updater( $value ) { - // Stop here if the current user is not a super admin user. + // Stop here if the current user is not a super admin. if ( ! is_super_admin() ) { return; } - $data = get_site_transient( 'beans_updater' ); + $data = get_site_transient( 'beans_updater' ); $theme = wp_get_theme( 'tm-beans' ); if ( ! $theme->exists() ) { @@ -30,31 +38,24 @@ function beans_updater( $value ) { $current_version = $theme->get( 'Version' ); - // Query Beans REST API if the transient is expired. + // Query the Beans REST API if the transient is expired. if ( empty( $data ) ) { - - $response = wp_remote_get( 'http://www.getbeans.io/rest-api/', array( 'sslverify' => false ) ); + $response = wp_remote_get( 'https://www.getbeans.io/rest-api/' ); // Retrieve data from the body and decode json format. $data = json_decode( wp_remote_retrieve_body( $response ), true ); - // Stop here if the is an error. + // Stop here if there is an error, set a temporary transient and bail out. if ( is_wp_error( $response ) || isset( $data['error'] ) ) { - - // Set temporary transient. set_site_transient( 'beans_updater', array( 'version' => $current_version ), 30 * MINUTE_IN_SECONDS ); - return $value; - } set_site_transient( 'beans_updater', $data, 24 * HOUR_IN_SECONDS ); - } // Return data if Beans is not up to date. if ( version_compare( $current_version, beans_get( 'version', $data ), '<' ) ) { - $value->response[ $data['path'] ] = array( 'slug' => $data['slug'], 'name' => $data['name'], @@ -64,23 +65,22 @@ function beans_updater( $value ) { 'tested' => $data['tested'], 'requires' => $data['requires'], ); - return $value; - } return $value; - } add_action( 'load-update-core.php', 'beans_updater_clear_transient' ); /** * Clear updater transient. * + * @since 1.0.0 * @ignore + * @access private + * + * @return void */ function beans_updater_clear_transient() { - delete_site_transient( 'beans_updater' ); - } diff --git a/lib/admin/wp-customize.php b/lib/admin/wp-customize.php index 998d1afb..7cd1fba0 100644 --- a/lib/admin/wp-customize.php +++ b/lib/admin/wp-customize.php @@ -2,7 +2,9 @@ /** * Add Beans options to the WordPress Customizer. * - * @package Admin + * @package Beans\Framework\Admin + * + * @since 1.0.0 */ beans_add_smart_action( 'customize_preview_init', 'beans_do_enqueue_wp_customize_assets' ); @@ -10,11 +12,14 @@ * Enqueue Beans assets for the WordPress Customizer. * * @since 1.0.0 + * + * @return void */ function beans_do_enqueue_wp_customize_assets() { - - wp_enqueue_script( 'beans-wp-customize-preview', BEANS_ADMIN_JS_URL . 'wp-customize-preview.js', array( 'jquery', 'customize-preview' ), BEANS_VERSION, true ); - + wp_enqueue_script( 'beans-wp-customize-preview', BEANS_ADMIN_JS_URL . 'wp-customize-preview.js', array( + 'jquery', + 'customize-preview', + ), BEANS_VERSION, true ); } beans_add_smart_action( 'customize_register', 'beans_do_register_wp_customize_options' ); @@ -22,9 +27,10 @@ function beans_do_enqueue_wp_customize_assets() { * Add Beans options to the WordPress Customizer. * * @since 1.0.0 + * + * @return void */ function beans_do_register_wp_customize_options() { - $fields = array( array( 'id' => 'beans_logo_image', @@ -40,29 +46,37 @@ function beans_do_register_wp_customize_options() { // Only show the layout options if more than two layouts are registered. if ( count( $options ) > 2 ) { - $fields = array( array( - 'id' => 'beans_layout', - 'label' => __( 'Default Layout', 'tm-beans' ), - 'type' => 'radio', - 'default' => beans_get_default_layout(), - 'options' => $options, + 'id' => 'beans_layout', + 'label' => __( 'Default Layout', 'tm-beans' ), + 'type' => 'radio', + 'default' => beans_get_default_layout(), + 'options' => $options, ), ); - beans_register_wp_customize_options( $fields, 'beans_layout', array( 'title' => __( 'Default Layout', 'tm-beans' ), 'priority' => 1000 ) ); - + beans_register_wp_customize_options( + $fields, + 'beans_layout', + array( + 'title' => __( 'Default Layout', 'tm-beans' ), + 'priority' => 1000, + ) + ); } $fields = array( array( - 'id' => 'beans_viewport_width_group', - 'label' => __( 'Viewport Width', 'tm-beans' ), - 'type' => 'group', - 'fields' => array( + 'id' => 'beans_viewport_width_group', + 'label' => __( 'Viewport Width - for Previewing Only', 'tm-beans' ), + 'description' => __( 'Slide left or right to change the viewport width. Publishing will not change the width of your website.', 'tm-beans' ), + 'type' => 'group', + 'transport' => 'postMessage', + 'fields' => array( array( 'id' => 'beans_enable_viewport_width', + 'label' => __( 'Enable to change the viewport width.', 'tm-beans' ), 'type' => 'activation', 'default' => false, ), @@ -78,12 +92,15 @@ function beans_do_register_wp_customize_options() { ), ), array( - 'id' => 'beans_viewport_height_group', - 'label' => __( 'Viewport Height', 'tm-beans' ), - 'type' => 'group', - 'fields' => array( + 'id' => 'beans_viewport_height_group', + 'label' => __( 'Viewport Height - for Previewing Only', 'tm-beans' ), + 'description' => __( 'Slide left or right to change the viewport height. Publishing will not change the height of your website.', 'tm-beans' ), + 'type' => 'group', + 'transport' => 'postMessage', + 'fields' => array( array( 'id' => 'beans_enable_viewport_height', + 'label' => __( 'Enable to change the viewport height.', 'tm-beans' ), 'type' => 'activation', 'default' => false, ), @@ -100,6 +117,12 @@ function beans_do_register_wp_customize_options() { ), ); - beans_register_wp_customize_options( $fields, 'beans_preview', array( 'title' => __( 'Preview Tools', 'tm-beans' ), 'priority' => 1010 ) ); - + beans_register_wp_customize_options( + $fields, + 'beans_preview', + array( + 'title' => __( 'Preview Tools', 'tm-beans' ), + 'priority' => 1010, + ) + ); } diff --git a/lib/api/actions/class-beans-anonymous-action.php b/lib/api/actions/class-beans-anonymous-action.php new file mode 100644 index 00000000..25255e9b --- /dev/null +++ b/lib/api/actions/class-beans-anonymous-action.php @@ -0,0 +1,56 @@ +callback = $callback; + + add_action( $hook, array( $this, 'callback' ), $priority, $number_args ); + } + + /** + * Get action content and set it as the callback. + * + * @since 1.5.0 + * + * @return void + */ + public function callback() { + echo call_user_func_array( $this->callback[0], $this->callback[1] ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped -- The callback handles escaping its output, as Beans does not know what HTML or content will be passed back to it. + } +} diff --git a/lib/api/actions/class.php b/lib/api/actions/class.php deleted file mode 100644 index 4cab2c38..00000000 --- a/lib/api/actions/class.php +++ /dev/null @@ -1,49 +0,0 @@ -callback = $callback; - - add_action( $hook, array( $this, 'callback' ), $priority, $args ); - - } - - /** - * Get action content and set it as the callback. - */ - public function callback() { - - echo call_user_func_array( $this->callback[0], $this->callback[1] ); - - } -} diff --git a/lib/api/actions/functions.php b/lib/api/actions/functions.php index f66840ec..379ffd88 100644 --- a/lib/api/actions/functions.php +++ b/lib/api/actions/functions.php @@ -5,31 +5,33 @@ * While WordPress requires two or three arguments to remove an action, Beans * actions can be modified, replaced, removed or reset using only the ID as a reference. * - * @package API\Actions + * @package Beans\Framework\API\Actions + * + * @since 1.5.0 */ /** - * Hooks a function on to a specific action. + * Hooks a callback (function or method) to a specific action event. * - * This function is similar to {@link http://codex.wordpress.org/Function_Reference/add_action add_action()} - * with the exception of being registered by ID in order to be manipulated by the other Beans Actions functions. + * This function is similar to {@link https://codex.wordpress.org/Function_Reference/add_action add_action()} + * with the exception of being registered by ID within Beans in order to be manipulated by the other Beans + * Actions functions. * * @since 1.0.0 - * - * @param string $id A unique string used as a reference. - * @param string $hook The name of the action to which the $callback is hooked. - * @param callback $callback The name of the function you wish to be called. - * @param int $priority Optional. Used to specify the order in which the functions - * associated with a particular action are executed. Default 10. - * Lower numbers correspond with earlier execution, - * and functions with the same priority are executed - * in the order in which they were added to the action. - * @param int $args Optional. The number of arguments the function accepts. Default 1. - * - * @return bool Will always return true. + * @since 1.5.0 Returns false when action is not added via add_action. + * + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param string $hook The name of the action to which the `$callback` is hooked. + * @param callable $callback The name of the function|method you wish to be called when the action event fires. + * @param int $priority Optional. Used to specify the order in which the callbacks associated with a particular + * action are executed. Default is 10. + * Lower numbers correspond with earlier execution. Callbacks with the same priority + * are executed in the order in which they were added to the action. + * @param int $args Optional. The number of arguments the callback accepts. Default is 1. + * + * @return bool */ function beans_add_action( $id, $hook, $callback, $priority = 10, $args = 1 ) { - $action = array( 'hook' => $hook, 'callback' => $callback, @@ -37,301 +39,302 @@ function beans_add_action( $id, $hook, $callback, $priority = 10, $args = 1 ) { 'args' => $args, ); - // Replace original if set. - if ( $replaced = _beans_get_action( $id, 'replaced' ) ) { - $action = array_merge( $action, $replaced ); + $replaced_action = _beans_get_action( $id, 'replaced' ); + + // If the ID is set to be "replaced", then replace that(those) parameter(s). + if ( ! empty( $replaced_action ) ) { + $action = array_merge( $action, $replaced_action ); } $action = _beans_set_action( $id, $action, 'added', true ); - // Stop here if removed. + // If the ID is set to be "removed", then bail out. if ( _beans_get_action( $id, 'removed' ) ) { - return; + return false; } - // Merge modified. - if ( $modified = _beans_get_action( $id, 'modified' ) ) { - $action = array_merge( $action, $modified ); - } + $modified_action = _beans_get_action( $id, 'modified' ); - // Validate action arguments. - if ( count( $action ) == 4 ) { - add_action( $action['hook'], $action['callback'], $action['priority'], $action['args'] ); + // If the ID is set to be "modified", then modify that(those) parameter(s). + if ( ! empty( $modified_action ) ) { + $action = array_merge( $action, $modified_action ); } - return true; - + return add_action( $action['hook'], $action['callback'], $action['priority'], $action['args'] ); } /** * Set {@see beans_add_action()} using the callback argument as the action ID. * - * This function is a shortcut of {@see beans_add_action()}. It does't require an ID - * to be specified and uses the callback argument instead. + * This function is a shortcut of {@see beans_add_action()}. It does't require a Beans ID as it uses the + * callback argument instead. * * @since 1.0.0 + * @since 1.5.0 Returns false when action is not added via add_action. * - * @param string $hook The name of the action to which the $callback is hooked. - * @param callback $callback The name of the function you wish to be called. Used to set the action ID. - * @param int $priority Optional. Used to specify the order in which the functions - * associated with a particular action are executed. Default 10. - * Lower numbers correspond with earlier execution, - * and functions with the same priority are executed - * in the order in which they were added to the action. - * @param int $args Optional. The number of arguments the function accept. Default 1. + * @param string $hook The name of the action to which the `$callback` is hooked. + * @param callable $callback The name of the function|method you wish to be called when the action event fires. + * @param int $priority Optional. Used to specify the order in which the callbacks associated with a particular + * action are executed. Default is 10. + * Lower numbers correspond with earlier execution. Callbacks with the same priority + * are executed in the order in which they were added to the action. + * @param int $args Optional. The number of arguments the callback accepts. Default is 1. * - * @return bool Will always return true. + * @return bool */ function beans_add_smart_action( $hook, $callback, $priority = 10, $args = 1 ) { - return beans_add_action( $callback, $hook, $callback, $priority, $args ); - } /** - * Modify an action. + * Modify one or more of the arguments for the given action, i.e. referenced by its Bean's ID. * - * This function modifies an action registered using {@see beans_add_action()} or - * {@see beans_add_smart_action()}. Each optional argument must be set to NULL to keep the orginal value. + * This function modifies a registered action using {@see beans_add_action()} or + * {@see beans_add_smart_action()}. Each optional argument must be set to NULL to keep the original value. * * The original action can be reset using {@see beans_reset_action()}. * * @since 1.0.0 - * - * @param string $id The action ID. - * @param string $hook Optional. The name of the new action to which the $callback is hooked. - * Use NULL to keep the original value. - * @param callback $callback Optional. The name of the new function you wish to be called. - * Use NULL to keep the original value. - * @param int $priority Optional. The new priority. - * Use NULL to keep the original value. - * @param int $args Optional. The new number of arguments the function accept. - * Use NULL to keep the original value. - * - * @return bool Will always return true. + * @since 1.5.0 Improved action parameter filtering. + * + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param string|null $hook Optional. The new action's event name to which the $callback is hooked. + * Use NULL to keep the original value. + * @param callable|null $callback Optional. The new callback (function or method) you wish to be called. + * Use NULL to keep the original value. + * @param int|null $priority Optional. The new priority. + * Use NULL to keep the original value. + * @param int|null $args Optional. The new number of arguments the $callback accepts. + * Use NULL to keep the original value. + * + * @return bool */ function beans_modify_action( $id, $hook = null, $callback = null, $priority = null, $args = null ) { + $action = _beans_build_action_array( $hook, $callback, $priority, $args ); - // Remove action. - if ( $current = _beans_get_current_action( $id ) ) { - remove_action( $current['hook'], $current['callback'], $current['priority'], $current['args'] ); + // If no changes were passed in, there's nothing to modify. Bail out. + if ( empty( $action ) ) { + return false; } - $action = array_filter( array( - 'hook' => $hook, - 'callback' => $callback, - 'priority' => $priority, - 'args' => $args, - ) ); - - // Merge modified. - $action = _beans_merge_action( $id, $action, 'modified' ); - - // Replace if needed. - if ( $current ) { + $current_action = _beans_get_current_action( $id ); - $action = array_merge( $current, $action ); + // If the action is registered, let's remove it. + if ( ! empty( $current_action ) ) { + remove_action( $current_action['hook'], $current_action['callback'], $current_action['priority'] ); + } - add_action( $action['hook'], $action['callback'], $action['priority'], $action['args'] ); + // Merge the modified parameters and register with Beans. + $action = _beans_merge_action( $id, $action, 'modified' ); + // If there is no action to modify, bail out. + if ( empty( $current_action ) ) { + return false; } - return true; + // Overwrite the modified parameters. + $action = array_merge( $current_action, $action ); + return add_action( $action['hook'], $action['callback'], $action['priority'], $action['args'] ); } /** - * Modify an action hook. + * Modify one or more of the arguments for the given action, i.e. referenced by its Bean's ID. * * This function is a shortcut of {@see beans_modify_action()}. * * @since 1.0.0 + * @since 1.5.0 Return false if the hook is empty or not a string. * - * @param string $id The action ID. - * @param string $hook Optional. The name of the new action to which the $callback is hooked. Use NULL to - * keep the original value. + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param string $hook The new action's event name to which the callback is hooked. * - * @return bool Will always return true. + * @return bool */ function beans_modify_action_hook( $id, $hook ) { - return beans_modify_action( $id, $hook ); + if ( empty( $hook ) || ! is_string( $hook ) ) { + return false; + } + return beans_modify_action( $id, $hook ); } /** - * Modify an action callback. + * Modify the callback of the given action, i.e. referenced by its Bean's ID. * * This function is a shortcut of {@see beans_modify_action()}. * * @since 1.0.0 + * @since 1.5.0 Return false if the callback is empty. * - * @param string $id The action ID. - * @param string $callback Optional. The name of the new function you wish to be called. Use NULL to keep - * the original value. + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param callable $callback The new callback (function or method) you wish to be called. * - * @return bool Will always return true. + * @return bool */ function beans_modify_action_callback( $id, $callback ) { - return beans_modify_action( $id, null, $callback ); + if ( empty( $callback ) ) { + return false; + } + return beans_modify_action( $id, null, $callback ); } /** - * Modify an action priority. + * Modify the priority of the given action, i.e. referenced by its Bean's ID. * * This function is a shortcut of {@see beans_modify_action()}. * * @since 1.0.0 * - * @param string $id The action ID. - * @param int $priority Optional. The new priority. Use NULL to keep the original value. + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param int|string $priority The new priority. * - * @return bool Will always return true. + * @return bool */ -function beans_modify_action_priority( $id, $callback ) { - - return beans_modify_action( $id, null, null, $callback ); - +function beans_modify_action_priority( $id, $priority ) { + return beans_modify_action( $id, null, null, $priority ); } /** - * Modify an action arguments. + * Modify the number of arguments of the given action, i.e. referenced by its Bean's ID. * * This function is a shortcut of {@see beans_modify_action()}. * * @since 1.0.0 * - * @param string $id The action ID. - * @param int $args Optional. The new number of arguments the function accepts. Use NULL to keep the - * original value. + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param int|string $number_of_args The new number of arguments the $callback accepts. * - * @return bool Will always return true. + * @return bool */ -function beans_modify_action_arguments( $id, $args ) { - - return beans_modify_action( $id, null, null, null, $args ); - +function beans_modify_action_arguments( $id, $number_of_args ) { + return beans_modify_action( $id, null, null, null, $number_of_args ); } /** - * Replace an action. + * Replace one or more of the arguments for the given action, i.e. referenced by its Bean's ID. * * This function replaces an action registered using {@see beans_add_action()} or * {@see beans_add_smart_action()}. Each optional argument must be set to NULL to keep - * the orginal value. + * the original value. * - * While {@see beans_modify_action()} will keep the original value registered, this function - * will overwrite the original action. If the action is reset using {@see beans_reset_action()}, - * the replaced values will be used. + * This function is not resettable as it overwrites the original action's argument(s). + * That means using {@see beans_reset_action()} will not restore the original action. * * @since 1.0.0 - * - * @param string $id The action ID. - * @param string $hook Optional. The name of the new action to which the $callback is hooked. - * Use NULL to keep the original value. - * @param callback $callback Optional. The name of the new function you wish to be called. - * Use NULL to keep the original value. - * @param int $priority Optional. The new priority. - * Use NULL to keep the original value. - * @param int $args Optional. The new number of arguments the function accepts. - * Use NULL to keep the original value. - * - * @return bool Will always return true. + * @since 1.5.0 Returns false when no replacement arguments are passed. + * + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param string|null $hook Optional. The new action's event name to which the $callback is hooked. + * Use NULL to keep the original value. + * @param callable|null $callback Optional. The new callback (function or method) you wish to be called. + * Use NULL to keep the original value. + * @param int|null $priority Optional. The new priority. + * Use NULL to keep the original value. + * @param int|null $args Optional. The new number of arguments the $callback accepts. + * Use NULL to keep the original value. + * + * @return bool */ function beans_replace_action( $id, $hook = null, $callback = null, $priority = null, $args = null ) { + $action = _beans_build_action_array( $hook, $callback, $priority, $args ); - $action = array( - 'hook' => $hook, - 'callback' => $callback, - 'priority' => $priority, - 'args' => $args, - ); + // If no changes were passed in, there's nothing to modify. Bail out. + if ( empty( $action ) ) { + return false; + } - // Set and get the latest replaced. - $action = _beans_merge_action( $id, array_filter( $action ), 'replaced' ); + // Set and get the latest "replaced" action. + $action = _beans_merge_action( $id, $action, 'replaced' ); - // Set and get the latest added. - $action = _beans_merge_action( $id, $action, 'added' ); + // Modify the action. + $is_modified = beans_modify_action( $id, $hook, $callback, $priority, $args ); - return beans_modify_action( $id, $hook, $callback, $priority, $args ); + // If there's a current action, merge it with the replaced one; else, it will be replaced when the original is added. + if ( $is_modified ) { + _beans_merge_action( $id, $action, 'added' ); + } + return $is_modified; } /** - * Replace an action hook. + * Replace the action's event name (hook) for the given action, i.e. referenced by its Bean's ID. * * This function is a shortcut of {@see beans_replace_action()}. * * @since 1.0.0 + * @since 1.5.0 Return false if the hook is empty or not a string. * - * @param string $id The action ID. - * @param string $hook Optional. The name of the new action to which the $callback is hooked. Use NULL to keep - * the original value. + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param string $hook The new action's event name to which the callback is hooked. * - * @return bool Will always return true. + * @return bool */ function beans_replace_action_hook( $id, $hook ) { - return beans_replace_action( $id, $hook ); + if ( empty( $hook ) || ! is_string( $hook ) ) { + return false; + } + return beans_replace_action( $id, $hook ); } /** - * Replace an action callback. + * Replace the callback of the given action, i.e. referenced by its Bean's ID. * * This function is a shortcut of {@see beans_replace_action()}. * * @since 1.0.0 + * @since 1.5.0 Return false if the callback is empty. * - * @param string $id The action ID. - * @param string $callback Optional. The name of the new function you wish to be called. Use NULL to keep - * the original value. + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param string $callback The new callback (function or method) you wish to be called. * - * @return bool Will always return true. + * @return bool */ function beans_replace_action_callback( $id, $callback ) { - return beans_replace_action( $id, null, $callback ); + if ( empty( $callback ) ) { + return false; + } + return beans_replace_action( $id, null, $callback ); } /** - * Replace an action priority. + * Replace the priority of the given action, i.e. referenced by its Bean's ID. * * This function is a shortcut of {@see beans_replace_action()}. * * @since 1.0.0 * - * @param string $id The action ID. - * @param int $priority Optional. The new priority. Use NULL to keep the original value. + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param int $priority The new priority. * - * @return bool Will always return true. + * @return bool */ -function beans_replace_action_priority( $id, $callback ) { - +function beans_replace_action_priority( $id, $priority ) { return beans_replace_action( $id, null, null, $priority ); - } /** - * Replace an action argument. + * Replace the number of arguments of the given action, i.e. referenced by its Bean's ID. * * This function is a shortcut of {@see beans_replace_action()}. * * @since 1.0.0 * - * @param string $id The action ID. - * @param int $args Optional. The new number of arguments the function accepts. Use NULL to keep the original - * value. + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param int $args The new number of arguments the $callback accepts. * - * @return bool Will always return true. + * @return bool */ function beans_replace_action_arguments( $id, $args ) { - return beans_replace_action( $id, null, null, null, $args ); - } /** @@ -340,55 +343,73 @@ function beans_replace_action_arguments( $id, $args ) { * This function removes an action registered using {@see beans_add_action()} or * {@see beans_add_smart_action()}. The original action can be re-added using {@see beans_reset_action()}. * + * This function is "load order" agnostic, meaning that you can remove an action before it's added. + * * @since 1.0.0 + * @since 1.5.0 When no current action, sets "removed" to default configuration. * - * @param string $id The action ID. + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. * - * @return bool Will always return true. + * @return bool */ function beans_remove_action( $id ) { + $action = _beans_get_current_action( $id ); - // Remove. - if ( $action = _beans_get_current_action( $id ) ) { - remove_action( $action['hook'], $action['callback'], $action['priority'], $action['args'] ); + // When there is a current action, remove it. + if ( ! empty( $action ) ) { + remove_action( $action['hook'], $action['callback'], $action['priority'] ); + } else { + // If the action is not registered yet, set it to a default configuration. + $action = array( + 'hook' => null, + 'callback' => null, + 'priority' => null, + 'args' => null, + ); } - // Register as removed. - _beans_set_action( $id, $action, 'removed' ); - - return true; - + // Store as "removed". + return _beans_set_action( $id, $action, 'removed' ); } /** * Reset an action. * * This function resets an action registered using {@see beans_add_action()} or - * {@see beans_add_smart_action()}. If the original values were replaced using - * {@see beans_replace_action()}, these values will be used. + * {@see beans_add_smart_action()}. + * + * If the original values were replaced using {@see beans_replace_action()}, these values will be used, as + * {@see beans_replace_action()} is not resettable. * * @since 1.0.0 + * @since 1.5.0 Bail out if the action does not need to be reset. * - * @param string $id The action ID. + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. * - * @return bool Will always return true. + * @return bool */ function beans_reset_action( $id ) { - _beans_unset_action( $id, 'modified' ); _beans_unset_action( $id, 'removed' ); $action = _beans_get_action( $id, 'added' ); - if ( $current = _beans_get_current_action( $id ) ) { + // If there is no "added" action, bail out. + if ( empty( $action ) ) { + return false; + } - remove_action( $current['hook'], $current['callback'], $current['priority'], $current['args'] ); - add_action( $action['hook'], $action['callback'], $action['priority'], $action['args'] ); + $current = _beans_get_current_action( $id ); + // If there's no current action, return the "added" action. + if ( empty( $current ) ) { + return $action; } - return $action; + remove_action( $current['hook'], $current['callback'], $current['priority'] ); + add_action( $action['hook'], $action['callback'], $action['priority'], $action['args'] ); + return $action; } /** @@ -408,204 +429,327 @@ function beans_reset_action( $id ) { } /** - * Get action. + * Get the action's configuration for the given ID and status. Returns `false` if the action is not registered with + * Beans. * + * @since 1.0.0 * @ignore + * @access private + * + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param string $status Status for which to get the action. + * + * @return array|bool */ function _beans_get_action( $id, $status ) { - global $_beans_registered_actions; - $id = _beans_unique_action_id( $id ); + $registered_actions = beans_get( $status, $_beans_registered_actions ); - if ( ! $registered = beans_get( $status, $_beans_registered_actions ) ) { + // If the status is empty, return false, as no actions are registered. + if ( empty( $registered_actions ) ) { return false; } - if ( ! $action = beans_get( $id, $registered ) ) { + $id = _beans_unique_action_id( $id ); + $action = beans_get( $id, $registered_actions ); + + // If the action is empty, return false. + if ( empty( $action ) ) { return false; } - return (array) json_decode( $action ); - + return $action; } /** - * Set action. + * Store the action's configuration for the given ID and status. + * + * What happens if the action's configuration is already registered? If the `$overwrite` flag is set to `true`, + * then the new action's configuration is stored, overwriting the previous one. Else, the registered action's + * configuration is returned. * + * @since 1.0.0 * @ignore + * @access private + * + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param array|mixed $action The action configuration to store. + * @param string $status Status for which to store the action. + * @param bool $overwrite Optional. When set to `true`, the new action's configuration is stored, overwriting a + * previously stored configuration (if one exists). + * + * @return array|mixed */ function _beans_set_action( $id, $action, $status, $overwrite = false ) { - - global $_beans_registered_actions; - $id = _beans_unique_action_id( $id ); - // Return action which already exist unless overwrite is set to true. - if ( ! $overwrite && ( $_action = _beans_get_action( $id, $status ) ) ) { - return $_action; + // If not overwriting, return the registered action (if it's registered). + if ( ! $overwrite ) { + $registered_action = _beans_get_action( $id, $status ); + + if ( ! empty( $registered_action ) ) { + return $registered_action; + } } - $_beans_registered_actions[ $status ][ $id ] = json_encode( $action ); + if ( ! empty( $action ) || 'removed' === $status ) { + global $_beans_registered_actions; + $_beans_registered_actions[ $status ][ $id ] = $action; + } return $action; - } /** - * Unset action. + * Unset the action's configuration for the given ID and status. Returns `false` if there are is no action + * registered with Beans actions for the given ID and status. Else, returns true when complete. * + * @since 1.0.0 * @ignore + * @access private + * + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param string $status Status for which to get the action. + * + * @return bool */ function _beans_unset_action( $id, $status ) { - - global $_beans_registered_actions; - $id = _beans_unique_action_id( $id ); - // Stop here if the action doesn't exist. - if ( ! _beans_get_action( $id, $status ) ) { + // Bail out if the ID is not registered for the given status. + if ( false === _beans_get_action( $id, $status ) ) { return false; } + global $_beans_registered_actions; unset( $_beans_registered_actions[ $status ][ $id ] ); return true; - } /** - * Merge action. + * Merge the action's configuration and then store it for the given ID and status. + * + * If the action's configuration has not already been registered with Beans, just store it. * + * @since 1.0.0 * @ignore + * @access private + * + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * @param array $action The new action's configuration to merge and then store. + * @param string $status Status for which to merge/store this action. + * + * @return array */ -function _beans_merge_action( $id, $action, $status ) { +function _beans_merge_action( $id, array $action, $status ) { + $id = _beans_unique_action_id( $id ); + $registered_action = _beans_get_action( $id, $status ); - global $_beans_registered_actions; - - $id = _beans_unique_action_id( $id ); - - if ( $_action = _beans_get_action( $id, $status ) ) { - $action = array_merge( $_action, $action ); + // If the action's configuration is already registered with Beans, merge the new configuration with it. + if ( ! empty( $registered_action ) ) { + $action = array_merge( $registered_action, $action ); } + // Now store/register it. return _beans_set_action( $id, $action, $status, true ); - } /** - * Check all action status and return the current action. + * Get the current action, meaning get from the "added" and/or "modified" statuses. * + * @since 1.0.0 + * @since 1.5.0 Bails out if there is no "added" action registered. * @ignore + * @access private + * + * @param string $id The action's Beans ID, a unique ID tracked within Beans for this action. + * + * @return array|bool */ function _beans_get_current_action( $id ) { - $action = array(); - + // Bail out if the action is "removed". if ( _beans_get_action( $id, 'removed' ) ) { return false; } - if ( $added = _beans_get_action( $id, 'added' ) ) { - $action = $added; + $added = _beans_get_action( $id, 'added' ); + + // If there is no "added" action registered, bail out. + if ( empty( $added ) ) { + return false; } - if ( $modified = _beans_get_action( $id, 'modified' ) ) { - $action = array_merge( $action, $modified ); + $modified = _beans_get_action( $id, 'modified' ); + + // If the action is set to be modified, merge the changes and return the action. + if ( ! empty( $modified ) ) { + return array_merge( $added, $modified ); } - // Stop here if the action is invalid. - if ( 4 != count( $action ) ) { - return false; + return $added; +} + +/** + * Build the action's array for only the valid given arguments. + * + * @since 1.5.0 + * + * @param string|null $hook Optional. The action event's name to which the $callback is hooked. + * Valid when not falsey, + * i.e. (meaning not `null`, `false`, `0`, `0.0`, an empty string, or empty array). + * @param callable|null $callback Optional. The callback (function or method) you wish to be called when the event + * fires. Valid when not falsey, i.e. (meaning not `null`, `false`, `0`, `0.0`, an empty + * string, or empty array). + * @param int|null $priority Optional. Used to specify the order in which the functions associated with a + * particular action are executed. Valid when it's numeric, including 0. + * @param int|null $args Optional. The number of arguments the callback accepts. + * Valid when it's numeric, including 0. + * + * @return array + */ +function _beans_build_action_array( $hook = null, $callback = null, $priority = null, $args = null ) { + $action = array(); + + if ( ! empty( $hook ) ) { + $action['hook'] = $hook; } - return $action; + if ( ! empty( $callback ) ) { + $action['callback'] = $callback; + } + + foreach ( array( 'priority', 'args' ) as $arg_name ) { + $arg = ${$arg_name}; + + if ( is_numeric( $arg ) ) { + $action[ $arg_name ] = (int) $arg; + } + } + return $action; } /** * Add anonymous callback using a class since php 5.2 is still supported. * + * @since 1.5.0 * @ignore + * @access private + * + * @param string $hook The name of the action to which the $callback is hooked. + * @param array $callback The callback to register to the given $hook and arguments to pass. + * @param int $priority Optional. Used to specify the order in which the functions + * associated with a particular action are executed. Default 10. + * Lower numbers correspond with earlier execution, + * and functions with the same priority are executed + * in the order in which they were added to the action. + * @param int $number_args Optional. The number of arguments the function accepts. Default 1. + * + * @return _Beans_Anonymous_Action */ -function _beans_add_anonymous_action( $hook, $callback, $priority = 10, $args = 1 ) { - - require_once( BEANS_API_PATH . 'actions/class.php' ); - - new _Beans_Anonymous_Actions( $hook, $callback, $priority, $args ); +function _beans_add_anonymous_action( $hook, array $callback, $priority = 10, $number_args = 1 ) { + require_once BEANS_API_PATH . 'actions/class-beans-anonymous-action.php'; + return new _Beans_Anonymous_Action( $hook, $callback, $priority, $number_args ); } /** * Render action which can therefore be stored in a variable. * + * @since 1.5.0 * @ignore + * @access private + * + * @param mixed $hook Hook and possibly sub-hooks to be rendered. + * + * @return bool|null|string */ function _beans_render_action( $hook ) { - $args = func_get_args(); - // Return simple action if no sub-hook is set. - if ( ! preg_match_all( '#\[(.*?)\]#', $args[0], $matches ) ) { - - if ( has_filter( $args[0] ) ) { - return call_user_func_array( 'beans_render_function', array_merge( array( 'do_action' ), $args ) ); - } else { - return false; - } + // Return simple action if no sub-hook(s) is(are) set. + if ( ! preg_match_all( '#\[(.*?)\]#', $args[0], $sub_hooks ) ) { + return _beans_when_has_action_do_render( $args ); } - $output = null; - $prefix = current( explode( '[', $args[0] ) ); + $output = null; + $prefix = current( explode( '[', $args[0] ) ); $variable_prefix = $prefix; - $suffix = preg_replace( '/^.*\]\s*/', '', $args[0] ); + $suffix = preg_replace( '/^.*\]\s*/', '', $args[0] ); // Base hook. $args[0] = $prefix . $suffix; - if ( has_filter( $args[0] ) ) { - $output .= call_user_func_array( 'beans_render_function', array_merge( array( 'do_action' ), $args ) ); - } + // If the base hook is registered, render it. + _beans_when_has_action_do_render( $args, $output ); - foreach ( $matches[0] as $i => $subhook ) { + foreach ( (array) $sub_hooks[0] as $index => $sub_hook ) { + $variable_prefix .= $sub_hook; - $variable_prefix = $variable_prefix . $subhook; - $levels = array( $prefix . $subhook . $suffix ); + $levels = array( $prefix . $sub_hook . $suffix ); // Cascade sub-hooks. - if ( $i > 0 ) { - - $levels[] = str_replace( $subhook, '', $hook ); + if ( $index > 0 ) { $levels[] = $variable_prefix . $suffix; - } // Apply sub-hooks. foreach ( $levels as $level ) { - $args[0] = $level; - if ( has_filter( $args[0] ) ) { - $output .= call_user_func_array( 'beans_render_function', array_merge( array( 'do_action' ), $args ) ); - } + // If the level is registered, render it. + _beans_when_has_action_do_render( $args, $output ); - // Apply filter whithout square brackets for backwards compatibility. + // Apply filter without square brackets for backwards compatibility. $args[0] = preg_replace( '#(\[|\])#', '', $args[0] ); - if ( has_filter( $args[0] ) ) { - $output .= call_user_func_array( 'beans_render_function', array_merge( array( 'do_action' ), $args ) ); - } + // If the backwards compatible $args[0] is registered, render it. + _beans_when_has_action_do_render( $args, $output ); } } return $output; +} +/** + * Render all hooked action callbacks by firing {@see do_action()}. The output is captured in the buffer and then + * returned. + * + * @since 1.5.0 + * @ignore + * @access private + * + * @param array $args Array of arguments. + * @param string $output The output to be updated. + * + * @return string|bool + */ +function _beans_when_has_action_do_render( array $args, &$output = '' ) { + + if ( ! has_action( $args[0] ) ) { + return false; + } + + ob_start(); + call_user_func_array( 'do_action', $args ); + $output .= ob_get_clean(); + + return $output; } /** * Make sure the action ID is unique. * + * @since 1.5.0 * @ignore + * @access private + * + * @param mixed $callback Callback to convert into a unique ID. + * + * @return array|string */ function _beans_unique_action_id( $callback ) { @@ -627,13 +771,12 @@ function _beans_unique_action_id( $callback ) { } return get_class( $callback[0] ) . $callback[1]; + } - } elseif ( is_string( $callback[0] ) ) { // Treat static method. - + // Treat static method. + if ( is_string( $callback[0] ) ) { return $callback[0] . '::' . $callback[1]; - } return md5( $callback ); - } diff --git a/lib/api/admin-menu.php b/lib/api/admin-menu.php index 32262410..c14c38ad 100644 --- a/lib/api/admin-menu.php +++ b/lib/api/admin-menu.php @@ -1,8 +1,20 @@
%s
', + __( 'Your current install or file permission prevents Beans from working its magic. Please get in touch with Beans support. We will gladly get you started within 24 - 48 hours (working days).', 'tm-beans' ) + ) ); + + $html .= beans_output( 'beans_compiler_error_contact_text', sprintf( + '%s', + __( 'Contact Beans Support', 'tm-beans' ) + ) ); + + $html .= beans_output( 'beans_compiler_error_report_text', sprintf( + '%1$s. %2$s
', + __( 'Send us an automatic report', 'tm-beans' ), + __( 'We respect your time and understand you might not be able to contact us.', 'tm-beans' ) + ) ); + + wp_die( wp_kses_post( $html ) ); + } + + /** + * Send report. + * + * @since 1.0.0 + * @since 1.5.0 Changed access to private. + * + * @return void + */ + private function report() { + // Send report. + wp_mail( + 'hello@getbeans.io', + 'Compiler error', + 'Compiler error reported by ' . home_url(), + array( + 'MIME-Version: 1.0' . "\r\n", + 'Content-type: text/html; charset=utf-8' . "\r\n", + "X-Mailer: PHP \r\n", + 'From: ' . wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) . ' < ' . get_option( 'admin_email' ) . '>' . "\r\n", + 'Reply-To: ' . get_option( 'admin_email' ) . "\r\n", + ) + ); + + // Die and display message. + $message = beans_output( + 'beans_compiler_report_error_text', + sprintf( + '%s
', + __( 'Thanks for your contribution by reporting this issue. We hope to hear from you again.', 'tm-beans' ) + ) + ); + + wp_die( wp_kses_post( $message ) ); + } + + /** + * Set the filename for the compiled asset. + * + * This method has been replaced with {@see set_filename()}. + * + * @since 1.0.0 + * @deprecated 1.5.0. + */ + public function set_filname() { + _deprecated_function( __METHOD__, '1.5.0', 'set_filename' ); + + $this->set_filename(); + } + + /** + * Get the property's value. + * + * @since 1.5.0 + * + * @param string $property Name of the property to get. + * + * @return mixed + */ + public function __get( $property ) { + + if ( property_exists( $this, $property ) ) { + return $this->{$property}; + } + } +} diff --git a/lib/api/compiler/class-beans-page-compiler.php b/lib/api/compiler/class-beans-page-compiler.php new file mode 100644 index 00000000..2ee3150b --- /dev/null +++ b/lib/api/compiler/class-beans-page-compiler.php @@ -0,0 +1,306 @@ +do_not_compile_styles() ) { + return; + } + + $this->processed_handles = array(); + $styles = $this->compile_enqueued( 'style' ); + + if ( empty( $styles ) ) { + return; + } + + beans_compile_css_fragments( 'beans', $styles, array( 'version' => null ) ); + } + + /** + * Checks if the page's styles should not be compiled. + * + * @since 1.5.0 + * + * @return bool + */ + private function do_not_compile_styles() { + return ! beans_get_component_support( 'wp_styles_compiler' ) || ! get_option( 'beans_compile_all_styles', false ) || _beans_is_compiler_dev_mode(); + } + + /** + * Enqueue the compiled WP scripts. + * + * @since 1.0.0 + * + * @return void + */ + public function compile_page_scripts() { + + if ( $this->do_not_compile_scripts() ) { + return; + } + + $this->processed_handles = array(); + $scripts = $this->compile_enqueued( 'script' ); + + if ( empty( $scripts ) ) { + return; + } + + $this->dequeued_scripts = $scripts; + add_action( 'wp_print_scripts', array( $this, 'dequeue_scripts' ), 9999 ); + + beans_compile_js_fragments( 'beans', $scripts, array( + 'in_footer' => 'aggressive' === get_option( 'beans_compile_all_scripts_mode', 'aggressive' ), + 'version' => null, + ) ); + } + + /** + * Checks if the page's scripts should not be compiled. + * + * @since 1.5.0 + * + * @return bool + */ + private function do_not_compile_scripts() { + return ! beans_get_component_support( 'wp_scripts_compiler' ) || ! get_option( 'beans_compile_all_scripts', false ) || _beans_is_compiler_dev_mode(); + } + + /** + * Compile all of the enqueued assets, i.e. all assets that are registered with WordPress. + * + * @since 1.0.0 + * @ignore + * @access private + * + * @param string $type Type of asset, e.g. style or script. + * @param string|array $dependencies Optional. The asset's dependency(ies). Default is an empty string. + * + * @return array + */ + private function compile_enqueued( $type, $dependencies = '' ) { + $assets = beans_get( "wp_{$type}s", $GLOBALS ); + + if ( ! $assets ) { + return array(); + } + + if ( ! $dependencies ) { + $dependencies = $assets->queue; + } + + $fragments = array(); + + foreach ( $dependencies as $handle ) { + + if ( $this->do_not_compile_asset( $handle ) ) { + continue; + } + + if ( $this->did_handle( $handle ) ) { + continue; + } + + $asset = beans_get( $handle, $assets->registered ); + + if ( ! $asset ) { + continue; + } + + $this->get_deps_to_be_compiled( $type, $asset, $fragments ); + + if ( empty( $asset->src ) ) { + continue; + } + + if ( 'style' === $type ) { + $this->maybe_add_media_query_to_src( $asset ); + + $assets->done[] = $handle; + } + + $fragments[ $handle ] = $asset->src; + } + + return $fragments; + } + + /** + * Checks if the handle has already been processed. If no, it stores the handle. + * + * Note: This check eliminates processing dependencies that are in more than one asset. For example, if more than + * one script requires 'jquery', then this check ensures we only process jquery's dependencies once. + * + * @since 1.5.0 + * + * @param string $handle The asset's handle. + * + * @return bool + */ + private function did_handle( $handle ) { + if ( in_array( $handle, $this->processed_handles, true ) ) { + return true; + } + + $this->processed_handles[] = $handle; + + return false; + } + + /** + * When the args are not set to "all," adds the media query to the asset's src. + * + * @since 1.5.0 + * + * @param _WP_Dependency $asset The given asset. + * + * @return void + */ + private function maybe_add_media_query_to_src( $asset ) { + // Add compiler media query if set. + if ( 'all' === $asset->args ) { + return; + } + + $asset->src = add_query_arg( array( 'beans_compiler_media_query' => $asset->args ), $asset->src ); + } + + /** + * Checks the given asset's handle to determine if it should not be compiled. + * + * @since 1.5.0 + * + * @param string $handle The asset handle to check. + * + * @return bool + */ + private function do_not_compile_asset( $handle ) { + return in_array( $handle, $this->handles_not_to_compile, true ); + } + + /** + * Get the asset's dependencies to be compiled. + * + * @since 1.5.0 + * + * @param string $type Type of asset. + * @param _WP_Dependency $asset Instance of the asset. + * @param array $srcs Array of compiled asset srcs to be compiled. Passed by reference. + * + * @return void + */ + private function get_deps_to_be_compiled( $type, $asset, array &$srcs ) { + + if ( empty( $asset->deps ) ) { + return; + } + + foreach ( $this->compile_enqueued( $type, $asset->deps, true ) as $dep_handle => $dep_src ) { + + if ( empty( $dep_src ) ) { + continue; + } + + $srcs[ $dep_handle ] = $dep_src; + } + } + + /** + * Dequeue scripts which have been compiled, grab localized + * data and add it inline. + * + * @since 1.0.0 + * + * @return void + */ + public function dequeue_scripts() { + + if ( empty( $this->dequeued_scripts ) ) { + return; + } + + global $wp_scripts; + $localized = ''; + + // Fetch the localized content and dequeue script. + foreach ( $this->dequeued_scripts as $handle => $src ) { + $script = beans_get( $handle, $wp_scripts->registered ); + + if ( ! $script ) { + continue; + } + + if ( isset( $script->extra['data'] ) ) { + $localized .= $script->extra['data'] . "\n"; + } + + $wp_scripts->done[] = $handle; + } + + if ( empty( $localized ) ) { + return; + } + + // Add localized content since it was removed with dequeue scripts. + require dirname( __FILE__ ) . '/views/localized-content.php'; + } +} diff --git a/lib/api/compiler/class-compiler.php b/lib/api/compiler/class-compiler.php deleted file mode 100644 index 8aaa19c9..00000000 --- a/lib/api/compiler/class-compiler.php +++ /dev/null @@ -1,617 +0,0 @@ - false, - 'type' => false, - 'format' => false, - 'fragments' => array(), - 'depedencies' => false, - 'in_footer' => false, - 'minify_js' => false, - 'version' => false, - ); - - $this->compiler = array_merge( $defaults, $args ); - $this->dir = beans_get_compiler_dir( is_admin() ) . $this->compiler['id']; - $this->url = beans_get_compiler_url( is_admin() ) . $this->compiler['id']; - - $this->set_fragments(); - $this->set_filname(); - - if ( ! $this->cache_file_exist() ) { - - $this->filesystem(); - $this->maybe_make_dir(); - $this->cache_file(); - - } - - $this->enqueue_file(); - - // Keep it safe and reset WP Filsystem method. - remove_filter( 'filesystem_method', array( $this, 'filesystem_method' ) ); - - } - - /** - * Set WP Filsystem method. - */ - public function filesystem_method() { - - return 'direct'; - - } - - /** - * Initialise WP Filsystem. - */ - public function filesystem() { - - // Initialize the WordPress Filsystem. - if ( ! isset( $GLOBALS['wp_filesystem'] ) || empty( $GLOBALS['wp_filesystem'] ) ) { - - require_once( ABSPATH . '/wp-admin/includes/file.php' ); - - if ( ! WP_Filesystem() ) { - return $this->kill(); - } - } - - return true; - - } - - /** - * Make directory. - */ - public function maybe_make_dir() { - - if ( ! @is_dir( $this->dir ) ) { - wp_mkdir_p( $this->dir ); - } - - if ( ! is_writable( $this->dir ) ) { - return false; - } - - return true; - - } - - /** - * Set class fragments. - */ - public function set_fragments() { - - global $_beans_compiler_added_fragments; - - if ( $added_fragments = beans_get( $this->compiler['id'], $_beans_compiler_added_fragments[ $this->compiler['format'] ] ) ) { - $this->compiler['fragments'] = array_merge( $this->compiler['fragments'], $added_fragments ); - } - - /** - * Filter the compiler fragment files. - * - * The dynamic portion of the hook name, $this->compiler['id'], refers to the compiler id used as a reference. - * - * @since 1.0.0 - * - * @param array $fragments An array of fragment files. - */ - $this->compiler['fragments'] = apply_filters( 'beans_compiler_fragments_' . $this->compiler['id'], $this->compiler['fragments'] ); - - } - - /** - * Set class filname. - */ - public function set_filname() { - - $hash = substr( md5( @serialize( $this->compiler ) ), 0, 7 ); - - // Stop here and return filename if not in dev mode or if not using filesystem. - if ( ! _beans_is_compiler_dev_mode() || ! @is_dir( $this->dir ) ) { - return $this->compiler['filename'] = $hash . '.' . $this->get_extension(); - } - - $fragments_filemtime = array(); - - // Check for internal file changes. - foreach ( $this->compiler['fragments'] as $id => $fragment ) { - - // Ignore if the fragment is a function. - if ( $this->is_function( $fragment ) ) { - continue; - } - - // Only check file time for internal files. - if ( false !== strpos( $fragment, $_SERVER['HTTP_HOST'] ) || true == preg_match( '#^\/[^\/]#', $fragment ) ) { - $fragments_filemtime[ $id ] = @filemtime( beans_url_to_path( $fragment ) ); - } - } - - if ( ! empty( $fragments_filemtime ) ) { - - // Set filemtime hash. - $_hash = substr( md5( @serialize( $fragments_filemtime ) ), 0, 7 ); - - $items = @scandir( $this->dir ); - unset( $items[0], $items[1] ); - - // Clean up other modified files. - foreach ( $items as $item ) { - - // Remove if it contains initial hash, is the same format and doesn't contain the filemtime hash. - if ( false !== stripos( $item, $hash ) && false !== stripos( $item, $this->get_extension() ) && false === stripos( $item, $_hash ) ) { - @unlink( $this->dir . '/' . $item ); - } - } - - // Set the new hash which will trigger to new compiling. - $hash = $hash . '-' . $_hash; - - } - - $this->compiler['filename'] = $hash . '.' . $this->get_extension(); - - } - - /** - * Check if cached file exists. - */ - public function cache_file_exist() { - - if ( ( $filname = beans_get( 'filename', $this->compiler ) ) && file_exists( $this->dir . '/' . $filname ) ) { - return true; - } - - return false; - - } - - /** - * Create cached file. - */ - public function cache_file() { - - $content = $this->combine_fragments(); - $filename = $this->dir . '/' . $this->compiler['filename']; - - // Safe to access filesystem since we made sure it was set. - if ( ! $GLOBALS['wp_filesystem']->put_contents( $filename, $content, FS_CHMOD_FILE ) ) { - return false; - } - - return true; - - } - - /** - * Enqueue cached file. - */ - public function enqueue_file() { - - // Enqueue css. - if ( 'style' == $this->compiler['type'] ) { - return wp_enqueue_style( $this->compiler['id'], $this->get_url(), $this->compiler['depedencies'], $this->compiler['version'] ); - } elseif ( 'script' == $this->compiler['type'] ) { // Enqueue js file. - return wp_enqueue_script( $this->compiler['id'], $this->get_url(), $this->compiler['depedencies'], $this->compiler['version'], $this->compiler['in_footer'] ); - } - - return false; - - } - - /** - * Get cached file url. - */ - public function get_url() { - - $url = trailingslashit( $this->url ) . beans_get( 'filename', $this->compiler ); - - if ( is_ssl() ) { - $url = str_replace( 'http://', 'https://', $url ); - } - - return $url; - - } - - /** - * Get file extension. - */ - public function get_extension() { - - if ( 'style' == $this->compiler['type'] ) { - return 'css'; - } elseif ( 'script' == $this->compiler['type'] ) { - return 'js'; - } - - } - - /** - * Combine fragments content. - */ - public function combine_fragments() { - - $content = ''; - - // Loop through fragments. - foreach ( $this->compiler['fragments'] as $fragment ) { - - // Stop here if the fragment is empty. - if ( empty( $fragment ) ) { - continue; - } - - // Set the current fragment used by other functions. - $this->current_fragment = $fragment; - - // Treat function. - if ( $this->is_function( $fragment ) ) { - - $get_content = $this->get_function_content(); - - } else { // Treat file. - - $get_content = $this->get_internal_content(); - - // Try remote content if the internal content returned false. - if ( ! $get_content ) { - $get_content = $this->get_remote_content(); - } - } - - // Stop here if no content or content is an html page. - if ( ! $get_content || preg_match( '#^\s*\<#', $get_content ) ) { - continue; - } - - // Add the content. - if ( 'style' == $this->compiler['type'] ) { - - $get_content = $this->replace_css_url( $get_content ); - $get_content = $this->add_content_media_query( $get_content ); - - } - - $content .= ( $content ? "\n\n" : '' ) . $get_content; - - } - - return $this->format_content( $content ); - - } - - /** - * Get internal file content. - */ - public function get_internal_content() { - - $fragment = $this->current_fragment; - - if ( ! file_exists( $fragment ) ) { - - // Replace url with path. - $fragment = beans_url_to_path( $fragment ); - - // Stop here if it isn't a valid file. - if ( ! file_exists( $fragment ) || 0 === @filesize( $fragment ) ) { - return false; - } - } - - // Safe to access filesystem since we made sure it was set. - return $GLOBALS['wp_filesystem']->get_contents( $fragment ); - - } - - /** - * Get external file content. - */ - public function get_remote_content() { - - $fragment = $this->current_fragment; - - // Replace double slaches by http. Mostly used for font referencing urls. - if ( true == preg_match( '#^\/\/#', $fragment ) ) { - $fragment = preg_replace( '#^\/\/#', 'http://', $fragment ); - } elseif ( true == preg_match( '#^\/#', $fragment ) ) { // Add domain if it is local but could not be fetched as a file. - $fragment = site_url( $fragment ); - } - - $request = wp_remote_get( $fragment ); - - // If failed to get content, try with ssl url, otherwise go to next fragment. - if ( ! is_wp_error( $request ) && ( ! isset( $request['body'] ) || 200 != $request['response']['code'] ) ) { - - $fragment = preg_replace( '#^http#', 'https', $fragment ); - $request = wp_remote_get( $fragment ); - - if ( ! is_wp_error( $request ) && ( ! isset( $request['body'] ) || 200 != $request['response']['code'] ) ) { - return false; - } - } - - return wp_remote_retrieve_body( $request ); - - } - - /** - * Get function content. - */ - public function get_function_content() { - - return call_user_func( $this->current_fragment ); - - } - - /** - * Wrap content in query. - */ - public function add_content_media_query( $content ) { - - // Ignore if the fragment is a function. - if ( $this->is_function( $this->current_fragment ) ) { - return $content; - } - - $parse_url = parse_url( $this->current_fragment ); - - // Return content if it no media query is set. - if ( ! ( $query = beans_get( 'query', $parse_url ) ) || false === stripos( $query, 'beans_compiler_media_query' ) ) { - return $content; - } - - // Wrap the content in the query. - $new_content = '@media ' . beans_get( 'beans_compiler_media_query', wp_parse_args( $query ) ) . ' {' . "\n"; - - $new_content .= $content . "\n"; - - $new_content .= '}' . "\n"; - - return $new_content; - - } - - /** - * Formal CSS, LESS and JS content. - */ - public function format_content( $content ) { - - if ( 'style' == $this->compiler['type'] ) { - - if ( 'less' == $this->compiler['format'] ) { - - if ( ! class_exists( 'Beans_Lessc' ) ) { - require_once( BEANS_API_PATH . 'compiler/vendors/lessc.php' ); - } - - $less = new Beans_Lessc(); - - $content = $less->compile( $content ); - - } - - if ( ! _beans_is_compiler_dev_mode() ) { - $content = $this->strip_whitespace( $content ); - } - } - - if ( 'script' == $this->compiler['type'] && ! _beans_is_compiler_dev_mode() && $this->compiler['minify_js'] ) { - - if ( ! class_exists( 'JSMin' ) ) { - require_once( BEANS_API_PATH . 'compiler/vendors/js-minifier.php' ); - } - - $js_min = new JSMin( $content ); - - $content = $js_min->min(); - - } - - return $content; - - } - - /** - * Replace CSS url shortcuts with a valid url. - */ - public function replace_css_url( $content ) { - - // Replace css path to urls. - return preg_replace_callback( '#url\s*\(\s*[\'"]*?([^\'"\)]+)[\'"]*\s*\)#i', array( $this, 'css_path_to_url' ) , $content ); - - } - - /** - * replace_css_url() callback. - */ - public function css_path_to_url( $matches, $base_is_path = false ) { - - $base = $this->current_fragment; - - // Stop here if it isn't a internal file or not a valid format. - if ( true == preg_match( '#^(http|https|\/\/|data)#', $matches[1] ) ) { - return $matches[0]; - } - - $explode_path = explode( '../', $matches[1] ); - - // Replace the base part according to the path "../". - foreach ( $explode_path as $value ) { - $base = dirname( $base ); - } - - // Rebuild path. - $replace = preg_replace( '#^\/#', '', $explode_path ); - $rebuilt_path = end( $replace ); - - // Make sure it is a valid base. - if ( '.' === $base ) { - $base = ''; - } - - // Rebuild url and make sure it is a valid one using the beans_path_to_url function. - $url = beans_path_to_url( trailingslashit( $base ) . $rebuilt_path ); - - // Return the rebuilt path converted to url. - return 'url("' . $url . '")'; - - } - - /** - * Minify CSS. - */ - public function strip_whitespace( $content ) { - - $replace = array( - '#/\*.*?\*/#s' => '', // Strip comments. - '#\s\s+#' => ' ', // Strip excess whitespace. - ); - - $search = array_keys( $replace ); - $content = preg_replace( $search, $replace, $content ); - - $replace = array( - ': ' => ':', - '; ' => ';', - ' {' => '{', - ' }' => '}', - ', ' => ',', - '{ ' => '{', - ';}' => '}', // Strip optional semicolons. - ',\n' => ',', // Don't wrap multiple selectors. - '\n}' => '}', // Don't wrap closing braces. - '} ' => "}\n", // Put each rule on it's own line. - '\n' => '', // Take out all line breaks - ); - - $search = array_keys( $replace ); - - return trim( str_replace( $search, $replace, $content ) ); - - } - - /** - * Is the fragement a function. - */ - public function is_function( $fragment ) { - - if ( is_array( $fragment ) || is_callable( $fragment ) ) { - return true; - } - - return false; - - } - - /** - * Kill it :( - */ - public function kill() { - - // Send report if set. - if ( beans_get( 'beans_send_compiler_report' ) ) { - $this->report(); - } - - $html = beans_output( 'beans_compiler_error_title_text', sprintf( - '
%s
', - __( 'Your current install or file permission prevents Beans from working its magic. Please get in touch with Beans support, we will gladly get you started within 24 - 48 hours (working days).', 'tm-beans' ) - ) ); - - $html .= beans_output( 'beans_compiler_error_contact_text', sprintf( - '%s', - __( 'Contact Beans Support', 'tm-beans' ) - ) ); - - $html .= beans_output( 'beans_compiler_error_report_text', sprintf( - '%1$s. %2$s
', - __( 'Send us an automatic report', 'tm-beans' ), - __( 'We respect your time and understand you might not be able to contact us.', 'tm-beans' ) - ) ); - - wp_die( $html ); - - } - - /** - * Send report. - */ - public function report() { - - // Send report. - $send = wp_mail( - 'hello@getbeans.io', - 'Compiler error', - 'Compiler error reported by ' . home_url(), - array( - 'MIME-Version: 1.0' . "\r\n", - 'Content-type: text/html; charset=utf-8' . "\r\n", - "X-Mailer: PHP \r\n", - 'From: ' . wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) . ' < ' . get_option( 'admin_email' ) . '>' . "\r\n", - 'Reply-To: ' . get_option( 'admin_email' ) . "\r\n", - ) - ); - - // Die and display message. - wp_die( beans_output( 'beans_compiler_report_error_text', sprintf( - '%s
', - __( 'Thanks for your contribution by reporting this issue. We hope to hear from you again.', 'tm-beans' ) - ) ) ); - - } -} diff --git a/lib/api/compiler/class-options.php b/lib/api/compiler/class-options.php deleted file mode 100644 index 7a04f9df..00000000 --- a/lib/api/compiler/class-options.php +++ /dev/null @@ -1,160 +0,0 @@ - 'beans_compiler_items', - 'type' => 'flush_cache', - 'description' => __( 'Clear CSS and Javascript cached files. New cached versions will be compiled on page load.', 'tm-beans' ), - ), - ); - - // Add styles compiler option only if supported - if ( beans_get_component_support( 'wp_styles_compiler' ) ) { - $fields = array_merge( $fields, array( - array( - 'id' => 'beans_compile_all_styles', - 'label' => false, - 'checkbox_label' => __( 'Compile all WordPress styles', 'tm-beans' ), - 'type' => 'checkbox', - 'default' => false, - 'description' => __( 'Compile and cache all the CSS files that have been enqueued to the WordPress head.', 'tm-beans' ), - ), - ) ); - } - - // Add scripts compiler option only if supported - if ( beans_get_component_support( 'wp_scripts_compiler' ) ) { - $fields = array_merge( $fields, array( - array( - 'id' => 'beans_compile_all_scripts_group', - 'label' => __( 'Compile all WordPress scripts', 'tm-beans' ), - 'type' => 'group', - 'fields' => array( - array( - 'id' => 'beans_compile_all_scripts', - 'type' => 'activation', - 'default' => false, - ), - array( - 'id' => 'beans_compile_all_scripts_mode', - 'type' => 'select', - 'default' => 'aggressive', - 'attributes' => array( 'style' => 'margin: -3px 0 0 -8px;' ), - 'options' => array( - 'aggressive' => __( 'Aggressive', 'tm-beans' ), - 'standard' => __( 'Standard', 'tm-beans' ), - ), - ), - ), - 'description' => __( 'Compile and cache all the Javascript files that have been enqueued to the WordPress head.JavaSript is outputted in the footer if the level is set to Aggressive and might conflict with some third party plugins which are not following WordPress standards.', 'tm-beans' ), - ), - ) ); - } - - beans_register_options( $fields, 'beans_settings', 'compiler_options', array( - 'title' => __( 'Compiler options', 'tm-beans' ), - 'context' => 'normal', - ) ); - - } - - /** - * Flush images for all folders set. - */ - public function flush() { - - if ( ! beans_post( 'beans_flush_compiler_cache' ) ) { - return; - } - - beans_remove_dir( beans_get_compiler_dir() ); - - } - - /** - * Cache cleaner notice. - */ - public function admin_notice() { - - if ( ! beans_post( 'beans_flush_compiler_cache' ) ) { - return; - } - - ?> -