관리-도구
편집 파일: class-wpcode-snippet.php
<?php /** * The main class to work with single WPCode snippets. * * @package wpcode */ /** * WPCode_Snippet class. */ class WPCode_Snippet { /** * Post type used to store. * * @var string */ public $post_type = 'wpcode'; /** * Location taxonomy name. * * @var string */ public $location_taxonomy = 'wpcode_location'; /** * Code type taxonomy name. * * @var string */ public $code_type_taxonomy = 'wpcode_type'; /** * Tags taxonomy name. * * @var string */ public $tags_taxonomy = 'wpcode_tags'; /** * The snippet id. * * @var int */ public $id; /** * The WP_Post object for the snippet. * * @var WP_Post */ public $post_data; /** * The snippet title. * * @var string */ public $title; /** * The actual snippet code. * * @var string */ public $code; /** * The code language/type. * * @var string */ public $code_type; /** * The location where the snippet is added. * * @var string */ public $location; /** * Auto-insert or use shortcode? * * @var int */ public $auto_insert; /** * The insert number for paragraphs or posts in an archive. * * @var int */ public $insert_number; /** * If the snippet is active or not, for now this is handled by the post status draft vs published. * * @var bool */ public $active; /** * An array of tags. * * @var string[] */ public $tags; /** * When we load the location from the terms let's store the object here. * * @var WP_Term */ private $location_term; /** * When we load the code type from the terms let's store the object here. * * @var WP_Term */ private $code_type_term; /** * The tag terms. * * @var WP_Term[] */ private $tags_terms; /** * Whether the conditional rules are enabled or disabled. * * @var bool */ private $use_rules; /** * The conditional logic rules. * * @var array */ private $rules; /** * The priority for loading. * * @var int */ private $priority; /** * The library id, if the snippet is created from the snippet library. * * @var int */ private $library_id; /** * The cloud id, if the snippet has been saved to the user's cloud. * * @var string */ private $cloud_id; /** * The custom shortcode name. * * @var string */ public $custom_shortcode; /** * The version of the snippet from the library. * * @var string */ private $library_version; /** * The snippet note. * * @var string */ private $note; /** * The name of the generator used for this snippet (if any). * * @var string */ private $generator; /** * The generator fields. * * @var array */ private $generator_data; /** * The type of device to load this snippet on. * * @var string */ public $device_type; /** * Schedule parameters for this snippet. * * @var array */ public $schedule; /** * Compiled code. * * @var string */ public $compiled_code; /** * Location extra parameters. * This is used to store extra parameters for the location. * * @var array */ public $location_extra; /** * Get an array of the shortcode attributes for this snippet. * * @var array */ public $shortcode_attributes; /** * Used to store the shortcode attributes values. * * @var array */ public $attributes; /** * Whether this snippet should be loaded as a file, specific to CSS and JS snippets. * * @var bool */ public $load_as_file; /** * The last time the snippet was modified. * * @var int */ public $modified; /** * Constructor. If the post passed is not the correct post type * the object will clear itself. * * @param array|int|WP_Post $snippet Load a snippet by id, WP_Post or array. */ public function __construct( $snippet ) { $snippet = apply_filters( 'wpcode_load_snippet', $snippet ); if ( is_int( $snippet ) ) { $this->load_from_id( $snippet ); } elseif ( $snippet instanceof WP_Post ) { $this->post_data = $snippet; } elseif ( is_array( $snippet ) ) { $this->load_from_array( $snippet ); } if ( isset( $this->post_data ) && $this->post_type !== $this->post_data->post_type ) { unset( $this->post_data ); unset( $this->id ); } } /** * Load a snippet by its ID. * * @param int $snippet_id The snippet id. * * @return void */ public function load_from_id( $snippet_id ) { $this->post_data = get_post( $snippet_id ); if ( $this->post_data ) { $this->id = $this->post_data->ID; } } /** * Load snippet from array - useful for creating a new snippet. * * @param array $snippet_data The array of data to load. * * @return void */ public function load_from_array( $snippet_data ) { $available_options = get_object_vars( $this ); foreach ( $available_options as $key => $value ) { if ( isset( $snippet_data[ $key ] ) ) { $this->$key = $snippet_data[ $key ]; } } } /** * Get the snippet location. * * @return string */ public function get_location() { if ( ! isset( $this->location ) ) { $this->set_location(); } return $this->location; } /** * Grab and set the location term and location string. * * @return void */ public function set_location() { // If something below fails, let's not try again. $this->location = ''; $this->location_term = $this->get_single_term( $this->location_taxonomy ); if ( $this->location_term ) { $this->location = $this->location_term->slug; } } /** * Get a single term for this snippet based on the taxonomy. * * @param string $taxonomy The taxonomy to grab data for. * * @return false|WP_Term */ public function get_single_term( $taxonomy ) { if ( ! isset( $this->post_data ) ) { return false; } $taxonomy_terms = wp_get_post_terms( $this->post_data->ID, $taxonomy ); if ( ! empty( $taxonomy_terms ) && ! is_wp_error( $taxonomy_terms ) ) { return $taxonomy_terms[0]; } return false; } /** * Get the author from the post object. * * @return int */ public function get_snippet_author() { if ( isset( $this->post_data ) ) { return $this->post_data->post_author; } return 0; } /** * Get the post data object. * * @return WP_Post */ public function get_post_data() { return isset( $this->post_data ) ? $this->post_data : null; } /** * Get the snippet title. * * @return string */ public function get_title() { if ( ! isset( $this->title ) ) { $this->title = isset( $this->post_data ) ? $this->post_data->post_title : ''; } return $this->title; } /** * Get the snippet code. * * @return string */ public function get_code() { if ( ! isset( $this->code ) ) { $this->code = isset( $this->post_data ) ? $this->post_data->post_content : ''; } return $this->code; } /** * Get location term. * * @return WP_Term */ public function get_location_term() { if ( ! isset( $this->location_term ) ) { $this->set_location(); } return $this->location_term; } /** * Get the code type term. * * @return WP_Term */ public function get_code_type_term() { if ( ! isset( $this->code_type_term ) ) { $this->set_code_type(); } return $this->code_type_term; } /** * Is the snippet active? * * @return boolean */ public function is_active() { if ( ! isset( $this->active ) ) { $this->active = isset( $this->post_data->post_status ) && 'publish' === $this->post_data->post_status; } return $this->active; } /** * Shorthand for activating. * * @return void */ public function activate() { $this->active = true; $this->get_id(); $this->save(); } /** * Get the snippet id. * * @return int */ public function get_id() { if ( ! isset( $this->id ) ) { $this->id = isset( $this->post_data ) ? $this->post_data->ID : 0; } return $this->id; } /** * Store current object in the db. * * @return int|false */ public function save() { // Allow to prevent saving the snippet. $pre_save = apply_filters( 'wpcode_pre_save_snippet', false, $this ); if ( false !== $pre_save ) { return $pre_save; } $post_args = array( 'post_type' => $this->post_type, ); if ( isset( $this->id ) && 0 !== $this->id ) { $post_args['ID'] = $this->id; $this->load_from_id( $this->id ); } if ( isset( $this->title ) ) { $post_args['post_title'] = $this->title; } if ( isset( $this->code ) ) { $post_args['post_content'] = $this->code; } // If the user is not allowed to activate/deactivate snippets, prevent it and show error. if ( ! current_user_can( 'wpcode_activate_snippets', $this ) ) { wpcode()->error->add_error( array( 'message' => __( 'You are not allowed to change snippet status, please contact your webmaster.', 'insert-headers-and-footers' ), 'type' => 'permissions', ) ); unset( $this->active ); } if ( isset( $this->active ) ) { // If we're going to activate a snippet let's check if errors will be thrown. $this->run_activation_checks(); $post_args['post_status'] = $this->active ? 'publish' : 'draft'; } do_action( 'wpcode_before_snippet_save', $this ); if ( isset( $post_args['ID'] ) ) { $insert_result = wp_update_post( $post_args ); } else { if ( empty( $post_args['post_title'] ) ) { $post_args['post_title'] = $this->get_untitled_title(); } $insert_result = wp_insert_post( $post_args ); } if ( 0 === $insert_result || is_wp_error( $insert_result ) ) { return false; } $this->id = $insert_result; // Reset the last error. $this->reset_last_error(); if ( isset( $this->code_type ) ) { wp_set_post_terms( $this->id, $this->code_type, $this->code_type_taxonomy ); } if ( isset( $this->auto_insert ) ) { // Save this value for reference, but we never query by it. update_post_meta( $this->id, '_wpcode_auto_insert', $this->auto_insert ); } if ( isset( $this->location ) && 1 === $this->auto_insert ) { wp_set_post_terms( $this->id, $this->location, $this->location_taxonomy ); } elseif ( isset( $this->auto_insert ) ) { // If auto insert is disabled we just empty the taxonomy. wp_set_post_terms( $this->id, array(), $this->location_taxonomy ); } if ( isset( $this->tags ) ) { wp_set_post_terms( $this->id, $this->tags, $this->tags_taxonomy ); } if ( isset( $this->insert_number ) ) { update_post_meta( $this->id, '_wpcode_auto_insert_number', $this->insert_number ); } if ( isset( $this->use_rules ) ) { update_post_meta( $this->id, '_wpcode_conditional_logic_enabled', $this->use_rules ); } if ( isset( $this->rules ) ) { update_post_meta( $this->id, '_wpcode_conditional_logic', $this->rules ); } if ( isset( $this->priority ) ) { update_post_meta( $this->id, '_wpcode_priority', $this->priority ); } if ( isset( $this->library_id ) ) { update_post_meta( $this->id, '_wpcode_library_id', $this->library_id ); } if ( isset( $this->library_version ) ) { update_post_meta( $this->id, '_wpcode_library_version', $this->library_version ); } if ( isset( $this->note ) ) { update_post_meta( $this->id, '_wpcode_note', $this->note ); } if ( isset( $this->generator ) ) { update_post_meta( $this->id, '_wpcode_generator', $this->generator ); } if ( isset( $this->generator_data ) ) { update_post_meta( $this->id, '_wpcode_generator_data', $this->generator_data ); } if ( isset( $this->location_extra ) ) { update_post_meta( $this->id, '_wpcode_location_extra', $this->location_extra ); } if ( isset( $this->cloud_id ) ) { $auth_username = wpcode()->library_auth->get_auth_username(); $cloud_ids = get_post_meta( $this->id, '_wpcode_cloud_id', true ); if ( empty( $cloud_ids ) || ! is_array( $cloud_ids ) ) { $cloud_ids = array(); } if ( empty( $this->cloud_id ) && isset( $cloud_ids[ $auth_username ] ) ) { unset( $cloud_ids[ $auth_username ] ); } elseif ( ! empty( $this->cloud_id ) ) { $cloud_ids[ $auth_username ] = $this->cloud_id; } update_post_meta( $this->id, '_wpcode_cloud_id', $cloud_ids ); } if ( isset( $this->custom_shortcode ) ) { if ( empty( $this->custom_shortcode ) ) { // Delete this meta if empty because we query by it. delete_post_meta( $this->id, '_wpcode_custom_shortcode' ); } else { update_post_meta( $this->id, '_wpcode_custom_shortcode', $this->custom_shortcode ); } } if ( isset( $this->device_type ) ) { update_post_meta( $this->id, '_wpcode_device_type', $this->device_type ); } if ( isset( $this->schedule ) ) { update_post_meta( $this->id, '_wpcode_schedule', $this->schedule ); } if ( isset( $this->shortcode_attributes ) ) { update_post_meta( $this->id, '_wpcode_shortcode_attributes', $this->shortcode_attributes ); } if ( isset( $this->load_as_file ) && in_array( $this->get_code_type(), array( 'css', 'js', 'scss' ), true ) ) { update_post_meta( $this->id, '_wpcode_load_as_file', $this->load_as_file ); } if ( isset( $this->compiled_code ) ) { update_post_meta( $this->id, '_wpcode_compiled_code', $this->compiled_code ); } /** * Run extra logic after the snippet is saved. * * @param int $id The id of the updated snippet. * @param WPCode_Snippet $snippet The snippet object. */ do_action( 'wpcode_snippet_after_update', $this->id, $this ); $this->rebuild_cache(); return $this->id; } /** * Method for rebuilding all snippets cache. * * @return void */ public function rebuild_cache() { wpcode()->cache->cache_all_loaded_snippets(); } /** * Check if a snippet can be run without errors before activating it. * * @return void */ public function run_activation_checks() { $executed_types = array( 'php', 'universal', ); if ( ! in_array( $this->get_code_type(), $executed_types, true ) ) { // If the code is not getting executed just skip. return; } if ( false === $this->active ) { // If we're not trying to activate or the snippet is already active, bail. return; } // Make sure no errors are added by something else. wpcode()->error->clear_errors(); // Try running the code. // Grab the executor class specific to the code type. $executor = wpcode()->execute->get_type_execute_class( $this->get_code_type() ); // Mark this as an activation attempt. wpcode()->execute->doing_activation(); /** * Added for convenience. * * @var WPCode_Snippet_Execute_Type $executor */ $execute = new $executor( $this ); // Grab the output that executes the code. $execute->get_output(); // If any errors are caught, prevent the status from being changed. $has_error = wpcode()->error->has_error(); if ( $has_error ) { $this->active = false; } wpcode()->execute->not_doing_activation(); } /** * Return the code type. * * @return string */ public function get_code_type() { if ( ! isset( $this->code_type ) ) { $this->set_code_type(); } return $this->code_type; } /** * Grab the code type from the taxonomy. * * @return void */ public function set_code_type() { // If something below fails, let's not try again. $this->code_type = ''; $this->code_type_term = $this->get_single_term( $this->code_type_taxonomy ); if ( $this->code_type_term ) { $this->code_type = $this->code_type_term->slug; } } /** * Get the default title for snippets with no title set. * * @return string */ public function get_untitled_title() { return __( 'Untitled Snippet', 'insert-headers-and-footers' ); } /** * Shorthand for deactivating a snippet. * * @return void */ public function deactivate() { $this->active = false; $this->get_id(); $this->save(); } /** * This deactivates the snippet without regardless of user permissions. * Should only be used for unattended auto-deactivation when a snippet throws a potentially blocking error. * * @return void */ public function force_deactivate() { global $wpdb; // Add a filter so we can hijack the deactivate logic if needed. $force_deactivate = apply_filters( 'wpcode_force_deactivate_snippet', false, $this ); if ( false !== $force_deactivate ) { return; } // We need to make a direct call as using wp_update_post will load the post content and if the current user // doesn't have the unfiltered_html capability, the code will be changed unexpectedly. $update = $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->posts, array( 'post_status' => 'draft', ), array( 'ID' => $this->get_id(), ) ); if ( $update ) { // Rebuild cache to avoid the snippet being loaded again. $this->rebuild_cache(); wpcode()->error->add_error( array( 'message' => sprintf( /* translators: %s: Snippet title and ID used in the error log for clarity. */ esc_html__( 'Snippet %s was automatically deactivated due to a fatal error.', 'insert-headers-and-footers' ), sprintf( '"%s" (#%d)', $this->get_title(), $this->get_id() ) ), ) ); } } /** * Set the last error for this snippet. * * @param array $error The error details. * * @return void */ public function set_last_error( $error ) { if ( ! isset( $error['message'] ) ) { return; } update_post_meta( $this->get_id(), '_wpcode_last_error', $error ); } /** * Get the last error for this snippet. * * @return array|false */ public function get_last_error() { $error = get_post_meta( $this->get_id(), '_wpcode_last_error', true ); if ( empty( $error ) || ! is_array( $error ) ) { return false; } return $error; } /** * Remove the meta that stores the last error. * * @return void */ public function reset_last_error() { delete_post_meta( $this->get_id(), '_wpcode_last_error' ); wpcode()->error->clear_snippets_errors(); } /** * Get the auto insert number. * * @return int */ public function get_auto_insert_number() { if ( ! isset( $this->insert_number ) ) { $this->insert_number = absint( get_post_meta( $this->get_id(), '_wpcode_auto_insert_number', true ) ); // Default value should be 1. if ( 0 === $this->insert_number ) { $this->insert_number = 1; } } return $this->insert_number; } /** * Get the auto-insert value. * * @return int */ public function get_auto_insert() { if ( ! isset( $this->auto_insert ) ) { $this->auto_insert = absint( get_post_meta( $this->get_id(), '_wpcode_auto_insert', true ) ); } return $this->auto_insert; } /** * Get the array of tag slugs. * * @return string[] */ public function get_tags() { if ( ! isset( $this->tags ) ) { $this->set_tags(); } return $this->tags; } /** * Set the tags for the current snippet. * * @return void */ public function set_tags() { $tags = wp_get_post_terms( $this->get_id(), $this->tags_taxonomy ); $tag_slugs = array(); foreach ( $tags as $tag ) { /** * The tag term object. * * @var WP_Term $tag */ $tag_slugs[] = $tag->slug; } $this->tags = $tag_slugs; $this->tags_terms = $tags; } /** * Get the conditional logic rules from the db. * * @return array */ public function get_conditional_rules() { if ( ! isset( $this->rules ) ) { $rules = get_post_meta( $this->get_id(), '_wpcode_conditional_logic', true ); if ( empty( $rules ) ) { $rules = array(); } $this->rules = $rules; } return $this->rules; } /** * Are the conditional logic rules enabled? * * @return bool */ public function conditional_rules_enabled() { if ( ! isset( $this->use_rules ) ) { $enabled = get_post_meta( $this->get_id(), '_wpcode_conditional_logic_enabled', true ); if ( '' === $enabled ) { $enabled = false; } $this->use_rules = boolval( $enabled ); } return $this->use_rules; } /** * Get the note for this snippet. * * @return string */ public function get_note() { if ( ! isset( $this->note ) ) { $this->note = get_post_meta( $this->get_id(), '_wpcode_note', true ); } return $this->note; } /** * Get the priority number for this snippet. * * @return int */ public function get_priority() { if ( ! isset( $this->priority ) ) { $priority = get_post_meta( $this->get_id(), '_wpcode_priority', true ); if ( '' === $priority ) { $priority = 10; } $this->priority = intval( $priority ); } return $this->priority; } /** * Get essential data for caching. * * @return array */ public function get_data_for_caching() { $modified = 0; $post_data = $this->get_post_data(); if ( ! is_null( $post_data ) ) { $modified = $post_data->post_modified; } return array( 'id' => $this->get_id(), 'title' => $this->get_title(), 'code' => $this->get_code(), 'code_type' => $this->get_code_type(), 'location' => $this->get_location(), 'auto_insert' => $this->get_auto_insert(), 'insert_number' => $this->get_auto_insert_number(), 'use_rules' => $this->conditional_rules_enabled(), 'rules' => $this->get_conditional_rules(), 'priority' => $this->get_priority(), 'location_extra' => $this->get_location_extra(), 'shortcode_attributes' => $this->get_shortcode_attributes(), 'compiled_code' => $this->get_compiled_code(), 'modified' => $modified, ); } /** * Get the cloud id for this snippet. * * @return string */ public function get_cloud_id() { if ( ! isset( $this->cloud_id ) ) { if ( wpcode()->library_auth->has_auth() ) { $cloud_id = get_post_meta( $this->get_id(), '_wpcode_cloud_id', true ); if ( empty( $cloud_id ) || ! is_array( $cloud_id ) ) { $cloud_id = array(); } $auth_username = wpcode()->library_auth->get_auth_username(); $this->cloud_id = isset( $cloud_id[ $auth_username ] ) ? $cloud_id[ $auth_username ] : false; } else { $this->cloud_id = false; } } return $this->cloud_id; } /** * Set the cloud id. * * @param string $cloud_id The cloud id to use. * * @return void */ public function set_cloud_id( $cloud_id ) { $this->cloud_id = $cloud_id; } /** * Get the custom shortcode value. * * @return string */ public function get_custom_shortcode() { if ( ! isset( $this->custom_shortcode ) ) { $this->custom_shortcode = get_post_meta( $this->get_id(), '_wpcode_custom_shortcode', true ); } return $this->custom_shortcode; } /** * Get the device type for this snippet. * * @return string */ public function get_device_type() { if ( ! isset( $this->device_type ) ) { $this->device_type = get_post_meta( $this->get_id(), '_wpcode_device_type', true ); if ( empty( $this->device_type ) ) { $this->device_type = 'any'; } } return $this->device_type; } /** * Get the schedule data for this snippet. * * @return array */ public function get_schedule() { if ( ! isset( $this->schedule ) ) { $this->schedule = wp_parse_args( get_post_meta( $this->get_id(), '_wpcode_schedule', true ), array( 'start' => '', 'end' => '', ) ); } return $this->schedule; } /** * Get the generator data for this snippet, if any. * * @return array|false */ public function get_generator_data() { if ( ! isset( $this->generator_data ) ) { $generator_data = get_post_meta( $this->get_id(), '_wpcode_generator_data', true ); $this->generator_data = empty( $generator_data ) ? false : $generator_data; } return $this->generator_data; } /** * Get the generator name for this snippet. * * @return array|false */ public function get_generator() { if ( ! isset( $this->generator ) ) { $generator_name = get_post_meta( $this->get_id(), '_wpcode_generator', true ); $this->generator = empty( $generator_name ) ? false : $generator_name; } return $this->generator; } /** * Check if the snippet is generated using a WPCode generator.. * * @return bool */ public function is_generated() { return ! empty( $this->get_generator() ); } /** * Is this snippet scheduled? * * @return bool */ public function is_scheduled() { $schedule = $this->get_schedule(); return ! empty( $schedule['start'] ) || ! empty( $schedule['end'] ); } /** * Extra data for the selected auto-insert location. * * @return array */ public function get_location_extra() { if ( ! isset( $this->location_extra ) ) { $this->location_extra = get_post_meta( $this->get_id(), '_wpcode_location_extra', true ); } return $this->location_extra; } /** * Load compiled Code. * * @return string */ public function get_compiled_code() { if ( ! isset( $this->compiled_code ) ) { $this->compiled_code = get_post_meta( $this->get_id(), '_wpcode_compiled_code', true ); } return $this->compiled_code; } /** * Load the shortcode attributes and return. * * @return array */ public function get_shortcode_attributes() { if ( ! isset( $this->shortcode_attributes ) ) { $attributes = get_post_meta( $this->get_id(), '_wpcode_shortcode_attributes', true ); if ( ! is_array( $attributes ) ) { $attributes = array(); } $this->shortcode_attributes = $attributes; } return $this->shortcode_attributes; } /** * Set shortcode attribute value. * * @param string $key The attribute key. * @param string $value The value for the attribute. * * @return void */ public function set_attribute( $key, $value ) { $this->attributes[ $key ] = $value; } /** * Duplicates a snippet with all its data. * * @return void */ public function duplicate() { $this->get_data_for_caching(); $this->get_note(); $this->get_tags(); $this->get_custom_shortcode(); $this->get_device_type(); $this->get_schedule(); // Add a suffix to the title. $this->title = $this->get_title() . ' - Copy'; // Make sure the snippet is not active. $this->post_data->post_status = 'draft'; // Let's make sure the slashes don't get removed from the code. $this->code = wp_slash( $this->code ); /** * Fires before a snippet that is about to be duplicated is saved. * * @param WPCode_Snippet $snippet The snippet object. */ do_action( 'wpcode_before_snippet_duplicated', $this ); // Remove the id to create a new snippet. unset( $this->id ); // Save the new snippet. $this->save(); /** * Fires after a snippet has been duplicated. * * @param WPCode_Snippet $snippet The snippet object. */ do_action( 'wpcode_after_snippet_duplicated', $this ); } /** * Get the edit url for this snippet. * * @return string */ public function get_edit_url() { return admin_url( 'admin.php?page=wpcode-snippet-manager&snippet_id=' . absint( $this->get_id() ) ); } /** * Whether this snippet should be load as a file (for JS or CSS snippets, returns false for other code types). * * @return bool */ public function get_load_as_file() { if ( ! isset( $this->load_as_file ) ) { $this->load_as_file = in_array( $this->get_code_type(), array( 'js', 'css', 'scss' ), true ); if ( $this->load_as_file ) { $this->load_as_file = boolval( get_post_meta( $this->get_id(), '_wpcode_load_as_file', true ) ); } } return $this->load_as_file; } /** * Execute a snippet on demand. * * @param bool $ignore_conditional_logic Whether to ignore the conditional logic rules. * * @return void */ public function execute( $ignore_conditional_logic = false ) { if ( ! $ignore_conditional_logic ) { if ( $this->conditional_rules_enabled() && ! wpcode()->conditional_logic->are_snippet_rules_met( $this ) ) { return; } } wpcode()->execute->get_snippet_output( $this ); } }