File manager - Edit - /home/premiey/www/wp-includes/images/media/core.tar
Back
utils.php 0000666 00000027353 15165257376 0006454 0 ustar 00 <?php namespace ElementorPro\Core; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Utils { public static function get_public_post_types( $args = [] ) { $post_type_args = [ // Default is the value $public. 'show_in_nav_menus' => true, ]; // Keep for backwards compatibility if ( ! empty( $args['post_type'] ) ) { $post_type_args['name'] = $args['post_type']; unset( $args['post_type'] ); } $post_type_args = wp_parse_args( $post_type_args, $args ); $_post_types = get_post_types( $post_type_args, 'objects' ); $post_types = []; foreach ( $_post_types as $post_type => $object ) { $post_types[ $post_type ] = $object->label; } /** * Supported post types. * * Filters the allowed post types Elementor should work on. * * By default Elementor can be applied on publicly available post * types. This hook allows developers to alter those post types to * add new and remove existing types. * * @since 2.3.0 * * @param array $post_types Elementor supported post types. */ $post_types = apply_filters( 'elementor_pro/utils/get_public_post_types', $post_types ); return $post_types; } public static function get_client_ip() { $server_ip_keys = [ 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR', ]; foreach ( $server_ip_keys as $key ) { $value = self::_unstable_get_super_global_value( $_SERVER, $key ); if ( $value && filter_var( $value, FILTER_VALIDATE_IP ) ) { return $value; } } // Fallback local ip. return '127.0.0.1'; } public static function get_site_domain() { return str_ireplace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) ); } public static function get_current_post_id() { if ( isset( Plugin::elementor()->documents ) ) { return Plugin::elementor()->documents->get_current()->get_main_id(); } return get_the_ID(); } public static function get_the_archive_url() { $url = ''; if ( is_category() || is_tag() || is_tax() ) { $url = get_term_link( get_queried_object() ); } elseif ( is_author() ) { $url = get_author_posts_url( get_queried_object_id() ); } elseif ( is_year() ) { $url = get_year_link( get_query_var( 'year' ) ); } elseif ( is_month() ) { $url = get_month_link( get_query_var( 'year' ), get_query_var( 'monthnum' ) ); } elseif ( is_day() ) { $url = get_day_link( get_query_var( 'year' ), get_query_var( 'monthnum' ), get_query_var( 'day' ) ); } elseif ( is_post_type_archive() ) { $url = get_post_type_archive_link( get_post_type() ); } return $url; } public static function get_page_title( $include_context = true ) { $title = ''; if ( is_singular() ) { /* translators: %s: Search term. */ $title = get_the_title(); if ( $include_context ) { $post_type_obj = get_post_type_object( get_post_type() ); $title = sprintf( '%s: %s', $post_type_obj->labels->singular_name, $title ); } } elseif ( is_search() ) { /* translators: %s: Search term. */ $title = sprintf( esc_html__( 'Search Results for: %s', 'elementor-pro' ), get_search_query() ); if ( get_query_var( 'paged' ) ) { /* translators: %s: Page number. */ $title .= sprintf( esc_html__( ' – Page %s', 'elementor-pro' ), get_query_var( 'paged' ) ); } } elseif ( is_category() ) { $title = single_cat_title( '', false ); if ( $include_context ) { /* translators: Category archive title. %s: Category name. */ $title = sprintf( esc_html__( 'Category: %s', 'elementor-pro' ), $title ); } } elseif ( is_tag() ) { $title = single_tag_title( '', false ); if ( $include_context ) { /* translators: Tag archive title. %s: Tag name. */ $title = sprintf( esc_html__( 'Tag: %s', 'elementor-pro' ), $title ); } } elseif ( is_author() ) { $title = '<span class="vcard">' . get_the_author() . '</span>'; if ( $include_context ) { /* translators: Author archive title. %s: Author name. */ $title = sprintf( esc_html__( 'Author: %s', 'elementor-pro' ), $title ); } } elseif ( is_year() ) { $title = get_the_date( _x( 'Y', 'yearly archives date format', 'elementor-pro' ) ); if ( $include_context ) { /* translators: Yearly archive title. %s: Year. */ $title = sprintf( esc_html__( 'Year: %s', 'elementor-pro' ), $title ); } } elseif ( is_month() ) { $title = get_the_date( _x( 'F Y', 'monthly archives date format', 'elementor-pro' ) ); if ( $include_context ) { /* translators: Monthly archive title. %s: Month name and a year. */ $title = sprintf( esc_html__( 'Month: %s', 'elementor-pro' ), $title ); } } elseif ( is_day() ) { $title = get_the_date( _x( 'F j, Y', 'daily archives date format', 'elementor-pro' ) ); if ( $include_context ) { /* translators: Daily archive title. %s: Date. */ $title = sprintf( esc_html__( 'Day: %s', 'elementor-pro' ), $title ); } } elseif ( is_tax( 'post_format' ) ) { if ( is_tax( 'post_format', 'post-format-aside' ) ) { $title = _x( 'Asides', 'post format archive title', 'elementor-pro' ); } elseif ( is_tax( 'post_format', 'post-format-gallery' ) ) { $title = _x( 'Galleries', 'post format archive title', 'elementor-pro' ); } elseif ( is_tax( 'post_format', 'post-format-image' ) ) { $title = _x( 'Images', 'post format archive title', 'elementor-pro' ); } elseif ( is_tax( 'post_format', 'post-format-video' ) ) { $title = _x( 'Videos', 'post format archive title', 'elementor-pro' ); } elseif ( is_tax( 'post_format', 'post-format-quote' ) ) { $title = _x( 'Quotes', 'post format archive title', 'elementor-pro' ); } elseif ( is_tax( 'post_format', 'post-format-link' ) ) { $title = _x( 'Links', 'post format archive title', 'elementor-pro' ); } elseif ( is_tax( 'post_format', 'post-format-status' ) ) { $title = _x( 'Statuses', 'post format archive title', 'elementor-pro' ); } elseif ( is_tax( 'post_format', 'post-format-audio' ) ) { $title = _x( 'Audio', 'post format archive title', 'elementor-pro' ); } elseif ( is_tax( 'post_format', 'post-format-chat' ) ) { $title = _x( 'Chats', 'post format archive title', 'elementor-pro' ); } } elseif ( is_post_type_archive() ) { $title = post_type_archive_title( '', false ); if ( $include_context ) { /* translators: Post type archive title. %s: Post type name. */ $title = sprintf( esc_html__( 'Archives: %s', 'elementor-pro' ), $title ); } } elseif ( is_tax() ) { $title = single_term_title( '', false ); if ( $include_context ) { $tax = get_taxonomy( get_queried_object()->taxonomy ); /* translators: Taxonomy term archive title. 1: Taxonomy singular name, 2: Current taxonomy term. */ $title = sprintf( esc_html__( '%1$s: %2$s', 'elementor-pro' ), $tax->labels->singular_name, $title ); } } elseif ( is_archive() ) { $title = esc_html__( 'Archives', 'elementor-pro' ); } elseif ( is_404() ) { $title = esc_html__( 'Page Not Found', 'elementor-pro' ); } // End if(). /** * Page title. * * Filters the title of the page. * * By default different pages have different titles depending of the page * context (archive, singular, 404 etc.). This hook allows developers to * alter those titles. * * @since 1.0.0 * * @param string $title Page title to be displayed. */ $title = apply_filters( 'elementor/utils/get_the_archive_title', $title ); return $title; } public static function set_global_authordata() { global $authordata; if ( ! isset( $authordata->ID ) ) { $post = get_post(); $authordata = get_userdata( $post->post_author ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited } } /** * Used to overcome core bug when taxonomy is in more then one post type * * @see https://core.trac.wordpress.org/ticket/27918 * * @global array $wp_taxonomies The registered taxonomies. * * * @param array $args * @param string $output * @param string $operator * * @return array */ public static function get_taxonomies( $args = [], $output = 'names', $operator = 'and' ) { global $wp_taxonomies; $field = ( 'names' === $output ) ? 'name' : false; // Handle 'object_type' separately. if ( isset( $args['object_type'] ) ) { $object_type = (array) $args['object_type']; unset( $args['object_type'] ); } $taxonomies = wp_filter_object_list( $wp_taxonomies, $args, $operator ); if ( isset( $object_type ) ) { foreach ( $taxonomies as $tax => $tax_data ) { if ( ! array_intersect( $object_type, $tax_data->object_type ) ) { unset( $taxonomies[ $tax ] ); } } } if ( $field ) { $taxonomies = wp_list_pluck( $taxonomies, $field ); } return $taxonomies; } public static function get_ensure_upload_dir( $path ) { if ( file_exists( $path . '/index.php' ) ) { return $path; } wp_mkdir_p( $path ); $files = [ [ 'file' => 'index.php', 'content' => [ '<?php', '// Silence is golden.', ], ], [ 'file' => '.htaccess', 'content' => [ 'Options -Indexes', '<ifModule mod_headers.c>', ' <Files *.*>', ' Header set Content-Disposition attachment', ' </Files>', '</IfModule>', ], ], ]; foreach ( $files as $file ) { if ( ! file_exists( trailingslashit( $path ) . $file['file'] ) ) { $content = implode( PHP_EOL, $file['content'] ); @ file_put_contents( trailingslashit( $path ) . $file['file'], $content ); } } return $path; } /** * Remove words from a sentence. * * @param string $text * @param integer $length * * @return string */ public static function trim_words( $text, $length ) { if ( $length && str_word_count( $text ) > $length ) { $text = explode( ' ', $text, $length + 1 ); unset( $text[ $length ] ); $text = implode( ' ', $text ); } return $text; } /** * Get a user option with default value as fallback. * TODO: Use `\Elementor\User::get_user_option_with_default()` after this PR is merged: * https://github.com/elementor/elementor/pull/17745 * * @param string $option - Option key. * @param int $user_id - User ID * @param mixed $default - Default fallback value. * * @return mixed */ public static function get_user_option_with_default( $option, $user_id, $default ) { $value = get_user_option( $option, $user_id ); return ( false === $value ) ? $default : $value; } /** * TODO: Use core method instead (after merging PR of the original function in core). * PR URL: https://github.com/elementor/elementor/pull/18670. * * @param $file * @param mixed ...$args * @return false|string */ public static function _unstable_file_get_contents( $file, ...$args ) { if ( ! is_file( $file ) || ! is_readable( $file ) ) { return false; } return file_get_contents( $file, ...$args ); } /** * TODO: Use core method instead (after Pro minimum requirements is updated). * PR URL: https://github.com/elementor/elementor/pull/20392 */ public static function _unstable_get_super_global_value( $super_global, $key ) { if ( ! isset( $super_global[ $key ] ) ) { return null; } if ( $_FILES === $super_global ) { $super_global[ $key ]['name'] = sanitize_file_name( $super_global[ $key ]['name'] ); return $super_global[ $key ]; } return wp_kses_post_deep( wp_unslash( $super_global[ $key ] ) ); } /** * TODO: Use a core method instead (after Pro minimum requirements is updated). * @throws \Exception */ public static function _unstable_get_document_for_edit( $id ) { $document = Plugin::elementor()->documents->get( $id ); if ( ! $document ) { throw new \Exception( 'Not found.' ); } if ( ! $document->is_editable_by_current_user() ) { throw new \Exception( 'Access denied.' ); } return $document; } } utils/collection.php 0000666 00000002740 15165257376 0010600 0 ustar 00 <?php namespace ElementorPro\Core\Utils; use \Elementor\Core\Utils\Collection as Collection_Base; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } // TODO: Move to Core. class Collection extends Collection_Base implements \JsonSerializable { /** * Change the items key by an item field. * * @param string $key * * @return Collection */ public function key_by( $key ) { return $this->map_with_keys( function ( $item ) use ( $key ) { return [ $item->{$key} => $item ]; } ); } /** * Flatten the items recursively. * * @return array */ public function flatten_recursive() { $output = []; $items = $this->all(); array_walk_recursive($items, function( $item ) use ( &$output ) { $output[] = $item; } ); return $output; } /** * Run array_diff between the collection and other array or collection. * * @param $filter * * @return $this */ public function diff( $filter ) { if ( $filter instanceof Collection_Base ) { $filter = $filter->all(); } return new static( array_diff( $this->all(), $filter ) ); } /** * Reverse the array * * @param false $preserve_keys * * @return $this */ public function reverse( $preserve_keys = false ) { return new static( array_reverse( $this->all(), $preserve_keys ) ); } /** * Return a JSON serialized representation of the Collection. * * @return array */ #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->all(); } } utils/registrar.php 0000666 00000002217 15165257376 0010446 0 ustar 00 <?php namespace ElementorPro\Core\Utils; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Basic items registrar. * * TODO: Move to Core. */ class Registrar { /** * @var array */ private $items; /** * Registrar constructor. * * @return void */ public function __construct() { $this->items = []; } /** * Register a new item. * * @param $instance - Item instance. * @param string $id - Optional - For BC - Deprecated. * * @return boolean - Whether the item was registered. */ public function register( $instance, $id = null ) { // TODO: For BC. Remove in the future. if ( ! $id ) { // Get the ID or default to the class name. $id = ( method_exists( $instance, 'get_id' ) ) ? $instance->get_id() : get_class( $instance ); } if ( $this->get( $id ) ) { return false; } $this->items[ $id ] = $instance; return true; } /** * Get an item by ID. * * @param string $id * * @return array|null */ public function get( $id = null ) { if ( ! $id ) { return $this->items; } return isset( $this->items[ $id ] ) ? $this->items[ $id ] : null; } } behaviors/feature-lock.php 0000666 00000003100 15165257376 0011637 0 ustar 00 <?php namespace ElementorPro\Core\Behaviors; use ElementorPro\License\API; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Feature_Lock implements Temp_Lock_Behavior { private $config; public function __construct( $config = [] ) { $this->config = $config; } public function is_locked() { return ! API::is_license_active(); } public function get_config() { $utm_args = [ 'utm_source' => '%%utm_source%%', // Will be replaced in the frontend. 'utm_medium' => '%%utm_medium%%', 'utm_campaign' => API::is_license_expired() ? 'renew-license' : 'connect-and-activate-license', 'utm_term' => $this->config['type'], ]; $connect_url = Plugin::instance()->license_admin->get_connect_url( $utm_args ); $renew_url = add_query_arg( $utm_args, 'https://my.elementor.com/subscriptions/' ); return [ 'is_locked' => $this->is_locked(), 'badge' => [ 'icon' => 'eicon-lock', 'text' => esc_html__( 'Pro', 'elementor-pro' ), ], 'content' => [ 'heading' => esc_html__( 'You need an active Elementor Pro license', 'elementor-pro' ), 'description' => esc_html__( 'Your Elementor Pro license is inactive. To access premium Elementor widgets, templates, support & plugin updates activate your Pro license.', 'elementor-pro' ), ], 'button' => [ 'text' => API::is_license_expired() ? esc_html__( 'Renew now', 'elementor-pro' ) : esc_html__( 'Connect & Activate', 'elementor-pro' ), 'url' => API::is_license_expired() ? $renew_url : $connect_url, ], ]; } } behaviors/temp-lock-behavior.php 0000666 00000001320 15165257376 0012750 0 ustar 00 <?php namespace ElementorPro\Core\Behaviors; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } // TODO: Used here for testing. Should be removed when it'll be available in the Core. interface Temp_Lock_Behavior { /** * @return bool */ public function is_locked(); /** * @return array { * * @type bool $is_locked * * @type array $badge { * @type string $icon * @type string $text * } * * @type array $content { * @type string $heading * @type string $description * } * * @type array $button { * @type string $text * @type string $url * } * * } */ public function get_config(); } integrations/exceptions/action-validation-failed-exception.php 0000666 00000000532 15165257376 0021014 0 ustar 00 <?php namespace ElementorPro\Core\Integrations\Exceptions; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Action_Validation_Failed_Exception extends Exception_Base { protected function format_message( $message ) { return sprintf( 'Action `%s` failed validation: %s', $this->action, $message ); } } integrations/exceptions/action-failed-exception.php 0000666 00000000513 15165257376 0016663 0 ustar 00 <?php namespace ElementorPro\Core\Integrations\Exceptions; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Action_Failed_Exception extends Exception_Base { protected function format_message( $message ) { return sprintf( 'Action `%s` failed to run: %s', $this->action, $message ); } } integrations/exceptions/exception-base.php 0000666 00000002471 15165257376 0015103 0 ustar 00 <?php namespace ElementorPro\Core\Integrations\Exceptions; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } abstract class Exception_Base extends \Exception { /** * @var string */ protected $action; /** * @var array */ protected $meta = []; /** * Get a formatted message specific to the current exception type. * * @param string $message * * @return string */ abstract protected function format_message( $message ); /** * Exception_Base constructor. * * @param string $action - Action name that failed (ideally the class name, e.g. Email::class). * @param string $message - Message to show. * @param array $meta - Exception meta data. Used for logging. * */ public function __construct( $action, $message = '', $meta = [] ) { $this->action = $action; $this->meta = $meta; $message = $this->format_message( $message ); parent::__construct( $message ); } /** * Log the exception to Elementor's log. * * @return void */ public function log() { Plugin::elementor()->logger->get_logger()->error( $this->getMessage(), [ 'meta' => $this->meta ] ); } /** * Get the error format. * * @return string */ public function __toString() { return sprintf( '%s: %s', __CLASS__, $this->getMessage() ); } } integrations/actions/email/email-address.php 0000666 00000001526 15165257376 0015255 0 ustar 00 <?php namespace ElementorPro\Core\Integrations\Actions\Email; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Email_Address { /** * Recipient email address. * * @var array */ public $address; /** * Recipient name. * * @var string */ public $name; /** * Email_Address constructor. * * @param string $address * @param string $name * * @return void */ public function __construct( $address, $name ) { $this->address = (string) $address; $this->name = (string) $name; } /** * Format an email to be ready for header (e.g. `Recipient Name <user@email.com>` or `user@email.com`) * * @return string */ public function format() { if ( ! empty( $this->name ) ) { return sprintf( '%s <%s>', $this->name, $this->address ); } return sprintf( '%s', $this->address ); } } integrations/actions/email/email.php 0000666 00000005321 15165257376 0013627 0 ustar 00 <?php namespace ElementorPro\Core\Integrations\Actions\Email; use ElementorPro\Core\Integrations\Actions\Action_Base; use ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception; use ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Email extends Action_Base { /** * @param Email_Message $payload * * @return void * @throws \Exception */ public function apply( $payload ) { // Set default headers. $headers = [ sprintf( 'Content-Type: %s; charset=UTF-8', $payload->content_type ), sprintf( 'From: %s', $payload->from->format() ), ]; foreach ( $payload->reply_to as $recipient ) { $headers[] = sprintf( 'Reply-To: %s', $recipient->format() ); } // Set CC headers. $cc_headers = []; foreach ( $payload->cc as $recipient ) { $cc_headers[] = sprintf( 'Cc: %s', $recipient->format() ); } // Send email. $this->send_mail( $payload->to->format(), $payload->subject, $payload->body, implode( PHP_EOL, array_merge( $headers, $cc_headers ) ), $payload->attachments ); // Send BCC emails. foreach ( $payload->bcc as $bcc ) { $this->send_mail( $bcc->format(), $payload->subject, $payload->body, implode( PHP_EOL, $headers ), $payload->attachments ); } } /** * @alias `$this->run()` * * @param Email_Message $payload * * @return void *@throws \Exception * */ public function send( Email_Message $payload ) { $this->run( $payload ); } /** * Validate the email message DTO. * * @param Email_Message $payload * * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception * * @return void */ public function validate( $payload ) { $required_fields = [ 'from', 'to', 'subject', 'body', 'content_type', ]; foreach ( $required_fields as $field ) { if ( empty( $payload->{$field} ) ) { throw new Action_Validation_Failed_Exception( static::class, "`Email_Message::\${$field}` is required." ); } } } /** * Calls `wp_mail()`. Used for testing. * * @param mixed ...$args * * @return void */ protected function send_mail( ...$args ) { add_action( 'wp_mail_failed', [ $this, 'on_wp_mail_error' ] ); wp_mail( ...$args ); remove_action( 'wp_mail_failed', [ $this, 'on_wp_mail_error' ] ); } /** * Throw exception on `wp_mail()` error. * * @param \WP_Error $error * * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception * * @return void */ public function on_wp_mail_error( \WP_Error $error ) { throw new Action_Failed_Exception( static::class, '`wp_mail()` cannot send email', $error ); } } integrations/actions/email/email-message.php 0000666 00000010015 15165257376 0015245 0 ustar 00 <?php namespace ElementorPro\Core\Integrations\Actions\Email; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Email_Message { /** * Email sender. * * @var Email_Address */ public $from; /** * Email recipient. * * @var Email_Address */ public $to; /** * Email reply to address. * * @var Email_Address[] */ public $reply_to = []; /** * Email CC recipient. * * @var Email_Address[] */ public $cc = []; /** * Email BCC recipient. * * @var Email_Address[] */ public $bcc = []; /** * Email subject. * * @var string */ public $subject; /** * Email content type. * * @var string */ public $content_type; /** * Email body. * * @var string */ public $body; /** * Email attachments. * * @var array */ public $attachments = []; /** * Email_Message constructor. * * @return void */ public function __construct() { // Set defaults. $this->from( get_bloginfo( 'admin_email' ), get_bloginfo( 'name' ) ); } /** * Set the email sender. * * @param string $email * @param string|null $name * * @return $this */ public function from( $email, $name = null ) { $this->from = new Email_Address( $email, $name ); return $this; } /** * Set the email recipient. * * @param string $email * @param string|null $name * * @return $this */ public function to( $email, $name = null ) { $this->to = new Email_Address( $email, $name ); return $this; } /** * Add a reply to. * * @param string $email * @param string|null $name * * @return $this */ public function reply_to( $email, $name = null ) { $this->reply_to[] = new Email_Address( $email, $name ); return $this; } /** * Add a CC. * * @param string $email * @param string|null $name * * @return $this */ public function cc( $email, $name = null ) { $this->cc[] = new Email_Address( $email, $name ); return $this; } /** * Add a BCC. * * @param string $email * @param string|null $name * * @return $this */ public function bcc( $email, $name = null ) { $this->bcc[] = new Email_Address( $email, $name ); return $this; } /** * Set the email subject. * * @param string $subject * * @return $this */ public function subject( $subject ) { $this->subject = (string) $subject; return $this; } /** * Set the email content type. * * @param string $content_type * * @return $this */ public function content_type( $content_type ) { $this->content_type = (string) $content_type; return $this; } /** * Set the email body using plain text. * * @param string $body * @param string $content_type * * @return $this */ public function body( $body, $content_type = 'text/html' ) { $this->body = (string) $body; return $this->content_type( $content_type ); } /** * Set the email body using a view. * * @param string $path - View path, * @param array $data - Data that will be passes to the view. * * @return $this * @throws \Exception */ public function view( $path, $data = [] ) { if ( ! is_file( $path ) ) { throw new \Exception( "`{$path}` is not a valid view." ); } ob_start(); // Inspired from Laravel's view mechanism: // [1] https://github.dev/illuminate/filesystem/blob/b179f9ea3b3195d1f4b5ae2aee67e42eac6ceb5e/Filesystem.php#L98 // [2] https://github.dev/illuminate/view/blob/6dd315634a44450c5e443fa8735d4a526833fad3/Engines/PhpEngine.php#L48 call_user_func( function( $__view_path, $__view_data ) { extract( $__view_data, EXTR_SKIP ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract unset( $__view_data ); // `$__view_data` keys are available in the file as variables. require $__view_path; }, $path, $data ); $this->body = ob_get_clean(); return $this->content_type( 'text/html' ); } /** * Add an attachment. * * @param string $path - Attachment path on the server. * * @return $this */ public function attach( $path ) { $this->attachments[] = (string) $path; return $this; } } integrations/actions/action-base.php 0000666 00000002012 15165257376 0013630 0 ustar 00 <?php namespace ElementorPro\Core\Integrations\Actions; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } abstract class Action_Base { /** * Validate a payload. * * @param mixed $payload - Payload object instance. * * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception * * @return mixed */ abstract public function validate( $payload ); /** * Apply the action. * * @param mixed $payload - Payload object instance. * * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception * * @return void */ abstract public function apply( $payload ); /** * Run the action. * * @param mixed $payload - Payload object instance. * * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception * * @return void */ public function run( $payload ) { $this->validate( $payload ); $this->apply( $payload ); } } integrations/integrations-manager.php 0000666 00000005343 15165257376 0014133 0 ustar 00 <?php namespace ElementorPro\Core\Integrations; use ElementorPro\Core\Integrations\Actions\Action_Base; use ElementorPro\Core\Integrations\Actions\Email\Email; use ElementorPro\Core\Integrations\Actions\Email\Email_Message; use ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception; use ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception; use ElementorPro\Core\Utils\Registrar; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Integrations_Manager { /** * Registered action types. * * @var Registrar */ protected $actions_registrar; /** * Integrations_Manager constructor. * * @return void */ public function __construct() { $this->actions_registrar = new Registrar(); } /** * Get an action instance. * * @shortcut `Registrar->get()`. * * @return \ElementorPro\Core\Integrations\Actions\Action_Base|null */ public function get_action( $id ) { if ( ! $this->is_initialized() ) { $this->init_actions(); } return $this->actions_registrar->get( $id ); } /** * Run an action for a selected payload. * * @param array|mixed $payloads - Payloads instances to run the actions on. * @param null|string $id - If `$payloads` is not an array, a custom action ID can be provided. * * @return void */ public function run( $payloads, $id = null ) { if ( ! is_array( $payloads ) ) { $payloads = $id ? [ $id => $payloads ] : [ $payloads ]; } foreach ( $payloads as $key => $payload ) { // Get the action ID for the provided payload type. $action_id = is_numeric( $key ) ? get_class( $payload ) : $key; /** * @type Action_Base $action */ $action = $this->get_action( $action_id ); if ( ! $action ) { throw new \Exception( "{$action_id} doesn't have an associated `Action`." ); } if ( ! ( $action instanceof Action_Base ) ) { $action_class = get_class( $action ); throw new \Exception( "{$action_class} is not a valid `Action_Base`." ); } try { $action->run( $payload ); } catch ( Action_Validation_Failed_Exception $e ) { $e->log(); } catch ( Action_Failed_Exception $e ) { $e->log(); } } } /** * Initialize the manager actions. * * @return void */ protected function init_actions() { add_action( 'elementor_pro/core/integrations/actions/register', function ( Registrar $actions_registrar ) { $actions_registrar->register( new Email(), Email_Message::class ); } ); do_action( 'elementor_pro/core/integrations/actions/register', $this->actions_registrar ); } /** * Determine if the manager is initialized. * * @return boolean */ protected function is_initialized() { return ! ! did_action( 'elementor_pro/core/integrations/actions/register' ); } } admin/admin.php 0000666 00000022002 15165257376 0007456 0 ustar 00 <?php namespace ElementorPro\Core\Admin; use Elementor\Core\Base\App; use Elementor\Rollback; use Elementor\Settings; use Elementor\Tools; use Elementor\Utils; use ElementorPro\Core\Utils as ProUtils; use ElementorPro\License\API; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Admin extends App { const USAGE_PARAM_INSTALL_TIME = 'install_time_pro'; /** * Get module name. * * Retrieve the module name. * * @since 2.3.0 * @access public * * @return string Module name. */ public function get_name() { return 'admin'; } /** * Enqueue admin styles. * * @since 1.0.0 * @return void */ public function enqueue_styles() { $suffix = Utils::is_script_debug() ? '' : '.min'; $direction_suffix = is_rtl() ? '-rtl' : ''; wp_register_style( 'elementor-pro-admin', ELEMENTOR_PRO_ASSETS_URL . 'css/admin' . $direction_suffix . $suffix . '.css', [], ELEMENTOR_PRO_VERSION ); wp_enqueue_style( 'elementor-pro-admin' ); } public function enqueue_scripts() { $suffix = Utils::is_script_debug() ? '' : '.min'; wp_enqueue_script( 'elementor-pro-admin', ELEMENTOR_PRO_URL . 'assets/js/admin' . $suffix . '.js', [ 'elementor-admin', ], ELEMENTOR_PRO_VERSION, true ); $locale_settings = []; /** * Localized admin settings. * * Filters the localized settings used in the admin as JavaScript variables. * * By default Elementor Pro passes some admin settings to be consumed as JavaScript * variables. This hook allows developers to add extra settings values to be consumed * using JavaScript in WordPress admin. * * @since 1.0.0 * * @param array $locale_settings Localized settings. */ $locale_settings = apply_filters( 'elementor_pro/admin/localize_settings', $locale_settings ); Utils::print_js_config( 'elementor-pro-admin', 'ElementorProConfig', $locale_settings ); } public function remove_go_pro_menu() { remove_action( 'admin_menu', [ Plugin::elementor()->settings, 'register_pro_menu' ], Settings::MENU_PRIORITY_GO_PRO ); } private function get_rollback_versions() { $rollback_versions = get_transient( 'elementor_pro_rollback_versions_' . ELEMENTOR_PRO_VERSION ); if ( false === $rollback_versions ) { $max_versions = 30; $versions = API::get_previous_versions(); if ( is_wp_error( $versions ) ) { return []; } $rollback_versions = []; $current_index = 0; foreach ( $versions as $version ) { if ( $max_versions <= $current_index ) { break; } $lowercase_version = strtolower( $version ); $is_valid_rollback_version = ! preg_match( '/(trunk|beta|rc|dev)/i', $lowercase_version ); /** * Is valid rollback version. * * Filters whether the version of the rollback is valid or not. * * By default Elementor doesn't allow to rollback for trunk/beta/rc/dev versions. * This hook allows developers to enable a rollback for thise kind of versions by * returning `true`. * * @param bool $is_valid_rollback_version Whether a rollback version is valid. * @param array $lowercase_version A list of previous versions. */ $is_valid_rollback_version = apply_filters( 'elementor-pro/settings/tools/rollback/is_valid_rollback_version', $is_valid_rollback_version, $lowercase_version ); if ( ! $is_valid_rollback_version ) { continue; } if ( version_compare( $version, ELEMENTOR_VERSION, '>=' ) ) { continue; } $current_index++; $rollback_versions[] = $version; } set_transient( 'elementor_pro_rollback_versions_' . ELEMENTOR_PRO_VERSION, $rollback_versions, WEEK_IN_SECONDS ); } return $rollback_versions; } public function register_admin_tools_fields( Tools $tools ) { $rollback_html = '<select class="elementor-rollback-select">'; foreach ( $this->get_rollback_versions() as $version ) { $rollback_html .= "<option value='{$version}'>$version</option>"; } $rollback_html .= '</select>'; // Rollback $tools->add_fields( 'versions', 'rollback', [ 'rollback_pro_separator' => [ 'field_args' => [ 'type' => 'raw_html', 'html' => '<hr>', ], ], 'rollback_pro' => [ 'label' => esc_html__( 'Rollback Pro Version', 'elementor-pro' ), 'field_args' => [ 'type' => 'raw_html', 'html' => sprintf( $rollback_html . '<a data-placeholder-text="' . esc_html__( 'Reinstall', 'elementor-pro' ) . ' v{VERSION}" href="#" data-placeholder-url="%s" class="button elementor-button-spinner elementor-rollback-button">%s</a>', wp_nonce_url( admin_url( 'admin-post.php?action=elementor_pro_rollback&version=VERSION' ), 'elementor_pro_rollback' ), __( 'Reinstall', 'elementor-pro' ) ), 'desc' => '<span style="color: red;">' . esc_html__( 'Warning: Please backup your database before making the rollback.', 'elementor-pro' ) . '</span>', ], ], ] ); } public function post_elementor_pro_rollback() { check_admin_referer( 'elementor_pro_rollback' ); $rollback_versions = $this->get_rollback_versions(); $version = ProUtils::_unstable_get_super_global_value( $_GET, 'version' ); if ( ! $version || ! in_array( $version, $rollback_versions, true ) ) { wp_die( esc_html__( 'Error occurred, The version selected is invalid. Try selecting different version.', 'elementor-pro' ) ); } $package_url = API::get_plugin_package_url( $version ); if ( is_wp_error( $package_url ) ) { wp_die( $package_url ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } $plugin_slug = basename( ELEMENTOR_PRO__FILE__, '.php' ); $rollback = new Rollback( [ 'version' => $version, 'plugin_name' => ELEMENTOR_PRO_PLUGIN_BASE, 'plugin_slug' => $plugin_slug, 'package_url' => $package_url, ] ); $rollback->run(); wp_die( '', esc_html__( 'Rollback to Previous Version', 'elementor-pro' ), [ 'response' => 200 ] ); } public function plugin_action_links( $links ) { unset( $links['go_pro'] ); return $links; } public function plugin_row_meta( $plugin_meta, $plugin_file ) { if ( ELEMENTOR_PRO_PLUGIN_BASE === $plugin_file ) { $plugin_slug = basename( ELEMENTOR_PRO__FILE__, '.php' ); $plugin_name = esc_html__( 'Elementor Pro', 'elementor-pro' ); $row_meta = [ 'view-details' => sprintf( '<a href="%s" class="thickbox open-plugin-details-modal" aria-label="%s" data-title="%s">%s</a>', esc_url( network_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin_slug . '&TB_iframe=true&width=600&height=550' ) ), /* translators: %s: Plugin name - Elementor Pro. */ esc_attr( sprintf( esc_html__( 'More information about %s', 'elementor-pro' ), $plugin_name ) ), esc_attr( $plugin_name ), __( 'View details', 'elementor-pro' ) ), 'changelog' => '<a href="https://go.elementor.com/pro-changelog/" title="' . esc_attr( esc_html__( 'View Elementor Pro Changelog', 'elementor-pro' ) ) . '" target="_blank">' . esc_html__( 'Changelog', 'elementor-pro' ) . '</a>', ]; $plugin_meta = array_merge( $plugin_meta, $row_meta ); } return $plugin_meta; } public function change_tracker_params( $params ) { unset( $params['is_first_time'] ); if ( ! isset( $params['events'] ) ) { $params['events'] = []; } $params['events'] = array_merge( $params['events'], [ self::USAGE_PARAM_INSTALL_TIME => gmdate( 'Y-m-d H:i:s', Plugin::instance()->license_admin->get_installed_time() ), ] ); return $params; } public function add_finder_items( array $categories ) { $settings_url = Settings::get_url(); $categories['settings']['items']['integrations'] = [ 'title' => esc_html__( 'Integrations', 'elementor-pro' ), 'icon' => 'integration', 'url' => $settings_url . '#tab-integrations', 'keywords' => [ 'integrations', 'settings', 'typekit', 'facebook', 'recaptcha', 'mailchimp', 'drip', 'activecampaign', 'getresponse', 'convertkit', 'elementor' ], ]; return $categories; } /** * Admin constructor. */ public function __construct() { $this->add_component( 'canary-deployment', new Canary_Deployment() ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_styles' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); add_action( 'admin_menu', [ $this, 'remove_go_pro_menu' ], 0 ); add_action( 'elementor/admin/after_create_settings/' . Tools::PAGE_ID, [ $this, 'register_admin_tools_fields' ], 50 ); add_filter( 'plugin_action_links_' . ELEMENTOR_PLUGIN_BASE, [ $this, 'plugin_action_links' ], 50 ); add_filter( 'plugin_row_meta', [ $this, 'plugin_row_meta' ], 10, 2 ); add_filter( 'elementor/finder/categories', [ $this, 'add_finder_items' ] ); add_filter( 'elementor/tracker/send_tracking_data_params', [ $this, 'change_tracker_params' ], 200 ); add_action( 'admin_post_elementor_pro_rollback', [ $this, 'post_elementor_pro_rollback' ] ); add_action( 'in_plugin_update_message-' . ELEMENTOR_PRO_PLUGIN_BASE, function( $plugin_data ) { Plugin::elementor()->admin->version_update_warning( ELEMENTOR_PRO_VERSION, $plugin_data['new_version'] ); } ); } } admin/canary-deployment.php 0000666 00000001164 15165257376 0012027 0 ustar 00 <?php namespace ElementorPro\Core\Admin; use ElementorPro\License\API; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Canary_Deployment extends \Elementor\Core\Admin\Canary_Deployment { const CURRENT_VERSION = ELEMENTOR_PRO_VERSION; const PLUGIN_BASE = ELEMENTOR_PRO_PLUGIN_BASE; protected function get_canary_deployment_remote_info( $force ) { $version_info = API::get_version( false ); $canary_info = []; if ( ! is_wp_error( $version_info ) && ! empty( $version_info['canary_deployment'] ) ) { $canary_info = $version_info['canary_deployment']; } return $canary_info; } } database/model-query-builder.php 0000666 00000003613 15165257376 0012740 0 ustar 00 <?php namespace ElementorPro\Core\Database; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Model_Query_Builder extends Query_Builder { /** * The Query Builder associated model. * * @var string */ public $model; /** * Whether the returned value should be hydrated into a model. * * @var bool */ public $return_as_model = true; /** * Model_Query_Builder constructor. * * @param string $model_classname - Model to use inside the builder. * @param \wpdb|null $connection - MySQL connection. */ public function __construct( $model_classname, \wpdb $connection = null ) { $this->set_model( $model_classname ); parent::__construct( $connection ); } /** * Set the model the generated from the query builder. * * @param $model_classname * * @return $this */ public function set_model( $model_classname ) { $this->model = $model_classname; return $this; } /** * Disable model hydration. * * @return $this */ public function disable_model_initiation() { $this->return_as_model = false; return $this; } /** * Disable hydration before calling the original count. * * @param string $column * * @return int */ public function count( $column = '*' ) { $this->disable_model_initiation(); return parent::count( $column ); } /** * Disable hydration before calling the original pluck. * * @inheritDoc */ public function pluck( $column = null ) { $this->disable_model_initiation(); return parent::pluck( $column ); } /** * Override the parent `get()` and make Models from the results. * * @return \ElementorPro\Core\Utils\Collection */ public function get() { $items = parent::get(); if ( ! $this->return_as_model ) { return $items; } // Convert the SQL results to Model instances. return $items->map( function ( $comment ) { return new $this->model( $comment ); } ); } } database/base-database-updater.php 0000666 00000006203 15165257376 0013165 0 ustar 00 <?php namespace ElementorPro\Core\Database; use ElementorPro\Core\Utils\Collection; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } abstract class Base_Database_Updater { /** * Run all the 'up' method of the migrations classes if needed, and update the db version. * * @param bool $force When passing true, it ignores the current version and run all the up migrations. */ public function up( $force = false ) { $installed_version = $this->get_installed_version(); // Up to date. Nothing to do. if ( ! $force && $this->get_db_version() <= $installed_version ) { return; } $migrations = $this->get_collected_migrations(); if ( ! $force ) { $migrations = $migrations->filter( function ( $_, $version ) use ( $installed_version ) { // Filter all the migrations that already done. return $version > $installed_version; } ); } $migrations->map( function ( Base_Migration $migration, $version ) { $migration->up(); // In case some migration failed it updates version every migration. $this->update_db_version_option( $version ); } ); $this->update_db_version_option( $this->get_db_version() ); } /** * Run all the 'down' method of the migrations classes if can, and update the db version. * * @param bool $force When passing true, it ignores the current version and run all the down migrations. */ public function down( $force = false ) { $installed_version = $this->get_installed_version(); $migrations = $this->get_collected_migrations(); if ( ! $force ) { $migrations = $migrations->filter( function ( $_, $version ) use ( $installed_version ) { // Filter all the migrations that was not installed. return $version <= $installed_version; } ); } $migrations->reverse( true ) ->map( function ( Base_Migration $migration, $version ) { $migration->down(); // In case some migration failed it updates version every migration. $this->update_db_version_option( $version ); } ); $this->update_db_version_option( 0 ); } /** * Register hooks to activate the migrations. */ public function register() { add_action( 'admin_init', function () { $this->up(); } ); } /** * Update the version in the users DB. * * @param $version */ protected function update_db_version_option( $version ) { update_option( $this->get_db_version_option_name(), $version ); } /** * Get the version that already installed. * * @return int */ protected function get_installed_version() { return intval( get_option( $this->get_db_version_option_name() ) ); } /** * Get all migrations inside a Collection. * * @return Collection */ protected function get_collected_migrations() { return new Collection( $this->get_migrations() ); } /** * The most updated version of the DB. * * @return numeric */ abstract protected function get_db_version(); /** * The name of the option that saves the current user DB version. * * @return string */ abstract protected function get_db_version_option_name(); /** * Array of migration classes. * * @return Base_Migration[] */ abstract protected function get_migrations(); } database/join-clause.php 0000666 00000003156 15165257376 0011264 0 ustar 00 <?php namespace ElementorPro\Core\Database; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } /** * JOIN clause builder. * * Essentially, it uses the regular Builder's capabilities while wrapping some method * for syntactic sugar and better readability. */ class Join_Clause extends Query_Builder { // JOIN types. const TYPE_INNER = 'inner'; const TYPE_LEFT = 'left'; const TYPE_RIGHT = 'right'; /** * JOIN type. * * @var string */ public $type; /** * Join_Clause constructor. * * @param string $type - JOIN type. * @param \wpdb|null $connection - MySQL connection to use. * * @return void */ public function __construct( $type, \wpdb $connection = null ) { parent::__construct( $connection ); $this->type = $type; } /** * @uses `$this->where()`. * * @return Join_Clause */ public function on( $column, $operator, $value, $and_or = self::RELATION_AND ) { return $this->where( $column, $operator, $value, $and_or ); } /** * @shortcut `$this->on()`. * * @return Join_Clause */ public function or_on( $first, $operator, $second ) { return $this->on( $first, $operator, $second, self::RELATION_OR ); } /** * @uses `$this->where_column()`. * * @return Join_Clause */ public function on_column( $first, $operator, $second, $and_or = self::RELATION_AND ) { return $this->where_column( $first, $operator, $second, $and_or ); } /** * @shortcut `$this->on_column()`. * * @return Join_Clause */ public function or_on_column( $first, $operator, $second ) { return $this->on_column( $first, $operator, $second, self::RELATION_OR ); } } database/query-builder.php 0000666 00000074042 15165257376 0011646 0 ustar 00 <?php namespace ElementorPro\Core\Database; use ElementorPro\Core\Utils\Collection; use InvalidArgumentException; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Query_Builder { // Relation types. const RELATION_AND = 'AND'; const RELATION_OR = 'OR'; // Column types. const COLUMN_BASIC = 'basic'; // Regular column - will be automatically escaped. const COLUMN_RAW = 'raw'; // Raw column - SHOULD BE ESCAPED BY THE DEVELOPER. const COLUMN_SUB_SELECT = 'sub-select'; // Sub select - will be automatically bind & escaped. const COLUMN_COUNT = 'count'; // Count - wrap the column with a COUNT function. // WHERE types. const WHERE_BASIC = 'basic'; const WHERE_NULL = 'null'; const WHERE_COLUMN = 'column'; const WHERE_IN = 'in'; const WHERE_NOT_IN = 'not-in'; const WHERE_SUB = 'sub'; const WHERE_NESTED = 'nested'; const WHERE_EXISTS = 'exists'; const WHERE_NOT_EXISTS = 'not-exists'; // HAVING types. const HAVING_RAW = 'raw'; /** * MySQL connection. * * @var \wpdb */ protected $connection; /** * Current query value binding. * * @var array[] */ protected $bindings = [ 'select' => [], 'join' => [], 'where' => [], ]; /** * Current query columns to return. * * @var array */ protected $columns = [ [ 'type' => self::COLUMN_RAW, 'column' => '*', 'as' => null, ], ]; /** * Table to select from. * * @var array */ protected $from = []; /** * Current query joins. * * @var array */ protected $joins = []; /** * The where constraints for the query. * * @var array */ protected $wheres = []; /** * The having constraints for the query. * * @var array */ protected $havings = []; /** * The groupings for the query. * * @var array */ protected $groups = []; /** * The orderings for the query. * * @var array */ protected $orders = []; /** * The maximum number of records to return. * * @var int */ protected $limit; /** * The number of records to skip. * * @var int */ protected $offset; /** * Aggregations. * * @var array */ protected $with = []; /** * Query_Builder constructor. * * @param \wpdb|null $connection - The Mysql connection instance to use. */ public function __construct( \wpdb $connection = null ) { if ( $connection ) { $this->connection = $connection; return; } global $wpdb; $this->connection = $wpdb; } /** * Add columns to the SELECT clause. * * @param string[] $columns - Array of column names. * @param string $type - Select type. * * @return $this */ public function select( $columns = [ '*' ], $type = self::COLUMN_BASIC ) { $this->columns = []; $this->bindings['select'] = []; foreach ( $columns as $as => $column ) { $this->columns[ $as ] = [ 'type' => $type, 'as' => is_string( $as ) ? $as : null, 'column' => $column, ]; } return $this; } /** * @shortcut `$this->select()`. */ public function select_raw( $raw_columns = [ '*' ] ) { return $this->select( $raw_columns, self::COLUMN_RAW ); } /** * Add a `(SELECT ...) AS alias` statement to the SELECT clause. * * @param callable $callback - Callback that gets a `Query_Builder` and modifies it. * @param string $as - Alias for the sub select. * * @return $this */ public function add_sub_select( callable $callback, $as ) { call_user_func( $callback, $query = $this->new_query() ); $this->add_binding( $query->get_bindings(), 'select' ); $this->columns[] = [ 'type' => self::COLUMN_SUB_SELECT, 'column' => $query->to_sql(), 'as' => $as, ]; return $this; } /** * Add a `COUNT({col}) AS {alias}` statement to the SELECT clause. * * @param $column_name * @param $as * * @return $this */ public function add_count_select( $column_name, $as = null ) { $this->columns[] = [ 'type' => self::COLUMN_COUNT, 'column' => $column_name, 'as' => $as, ]; return $this; } /** * Set the table to select from. * * @param string $table - Table name. * @param string|null $as - Table alias. * * @return $this */ public function from( $table, $as = null ) { // Default the alias to the table name without prefix. $as = $as ? $as : $table; // Get the prefixed table name from the connection. $table = $this->connection->$table; $this->from = [ 'table' => $table, 'as' => $as, ]; return $this; } /** * @shortcut $this->from() * * Used for readability with UPDATE / INSERT / DELETE statements. */ public function table( $table, $as = null ) { return $this->from( $table, $as ); } /** * Execute a query operation only on specific condition. * For example: * * $query->when( 1 === $a, function( Query_Builder $builder ) { * // Runs if $a = 1. * $builder->where( ... ); * }, function( Query_Builder $builder ) { * // Runs if $a != 1. * $builder->where( ... ); * } ) * * @param mixed $condition - Condition to check. * @param callable $true_callback - Callback if the condition is truthy. * @param callable|null $false_callback - Callback if the condition is falsy. Optional. * * @return $this */ public function when( $condition, callable $true_callback, callable $false_callback = null ) { if ( $condition ) { call_user_func( $true_callback, $this, $condition ); } elseif ( $false_callback instanceof \Closure ) { call_user_func( $false_callback, $this, $condition ); } return $this; } /** * Add a `WHERE` statement. * * @param string|callable $column - Column name to check. * @param string $operator - Statement operator. * @param string|callable $value - Value as string or callback. * @param string $and_or - Boolean relation, one of `and` / `or`. * * @return $this */ public function where( $column, $operator = null, $value = null, $and_or = self::RELATION_AND ) { // `$column` is a function, create a nested where. if ( $column instanceof \Closure ) { return $this->where_nested( $column, $and_or ); } // `$value` is a function, create a sub select. if ( $value instanceof \Closure ) { return $this->where_sub( $column, $operator, $value, $and_or ); } // Validate relation. if ( ! in_array( strtoupper( $and_or ), [ self::RELATION_AND, self::RELATION_OR ], true ) ) { throw new InvalidArgumentException( 'Relation must be "and" or "or".' ); } // If it's a `LIKE` statement, escape it using WP's `esc_like`. if ( 'like' === strtolower( $operator ) ) { $value = $this->escape_like( $value ); } // Create an `IS NULL` statement if the `$value` is null. if ( null === $value ) { $type = self::WHERE_NULL; } else { $this->add_binding( $value, 'where' ); $type = self::WHERE_BASIC; } $this->wheres[] = [ 'type' => $type, 'column' => $column, 'operator' => $operator, 'value' => $value, 'and_or' => $and_or, ]; return $this; } /** * Add an `OR WHERE` statement. * * @shortcut $this->where(). */ public function or_where( $column, $operator = null, $value = null ) { return $this->where( $column, $operator, $value, self::RELATION_OR ); } /** * @shortcut `$this->where()`. */ public function where_null( $column, $and_or = self::RELATION_AND ) { return $this->where( $column, '=', null ); } /** * @shortcut `$this->where_null()`. */ public function or_where_null( $column ) { return $this->where_null( $column, self::RELATION_OR ); } /** * Add a `WHERE col1 = col2` statement. * * @param string $first - First column name to check. * @param string $operator - Statement operator. * @param string $second - Second column name to check. * @param string $and_or - Boolean relation, one of `and` / `or`. * * @return $this */ public function where_column( $first, $operator, $second, $and_or = self::RELATION_AND ) { // Validate relation. if ( ! in_array( strtoupper( $and_or ), [ self::RELATION_AND, self::RELATION_OR ], true ) ) { throw new InvalidArgumentException( 'Relation must be "and" or "or".' ); } $this->wheres[] = [ 'type' => self::WHERE_COLUMN, 'first' => $first, 'second' => $second, 'operator' => $operator, 'and_or' => $and_or, ]; return $this; } /** * Add an `OR WHERE col1 = col2` statement. * * @shortcut $this->where_column(). */ public function or_where_column( $first, $operator, $second ) { return $this->where_column( $first, $operator, $second, self::RELATION_OR ); } /** * Add a `WHERE IN()` statement. * * @param string $column - Column name to check. * @param string[]|callable $values - Array of values. * @param string $and_or - Boolean relation, one of `and` / `or`. * @param boolean $in - Whether it's `IN` or `NOT IN`. * * @return $this */ public function where_in( $column, $values, $and_or = self::RELATION_AND, $in = true ) { $type = $in ? self::WHERE_IN : self::WHERE_NOT_IN; // Support `WHERE IN ( SELECT ... FROM )`. if ( $values instanceof \Closure ) { $operator = $in ? 'IN' : 'NOT IN'; return $this->where( $column, $operator, $values ); } $this->wheres[] = [ 'type' => $type, 'column' => $column, 'value' => $values, 'and_or' => $and_or, ]; $this->add_binding( $values, 'where' ); return $this; } /** * Add an `OR WHERE IN()` statement. * * @shortcut $this->where_in(). */ public function or_where_in( $column, $values ) { return $this->where_in( $column, $values, self::RELATION_OR ); } /** * Add a `WHERE NOT IN()` statement. * * @shortcut $this->where_in(). */ public function where_not_in( $column, $values, $and_or = self::RELATION_AND ) { return $this->where_in( $column, $values, $and_or, false ); } /** * Add an `OR WHERE NOT IN()` statement. * * @shortcut $this->where_in(). */ public function or_where_not_in( $column, $values ) { return $this->where_not_in( $column, $values, self::RELATION_OR ); } /** * Add a `WHERE EXISTS()` statement. * * @param callable $callback - Callback that gets a `Query_Builder` and modifies it. * @param string $and_or - Boolean relation, one of `and` / `or`. * @param bool $exists - Whether to use `EXISTS` or `NOT EXISTS` statement. * * @return $this */ public function where_exists( callable $callback, $and_or = self::RELATION_AND, $exists = true ) { call_user_func( $callback, $query = $this->new_query() ); $type = $exists ? self::WHERE_EXISTS : self::WHERE_NOT_EXISTS; $this->wheres[] = [ 'type' => $type, 'query' => $query, 'and_or' => $and_or, ]; $this->add_binding( $query->get_bindings(), 'where' ); return $this; } /** * Add an `OR WHERE EXISTS()` statement. * * @shortcut $this->where_exists(). */ public function or_where_exists( callable $callback, $exists = true ) { return $this->where_exists( $callback, self::RELATION_OR, $exists ); } /** * Add a `WHERE NOT EXISTS()` statement. * * @shortcut $this->where_exists(). */ public function where_not_exists( callable $callback, $and_or = self::RELATION_AND ) { return $this->where_exists( $callback, $and_or, false ); } /** * Add an `OR WHERE NOT EXISTS()` statement. * * @shortcut $this->where_exists(). */ public function or_where_not_exists( callable $callback ) { return $this->or_where_exists( $callback, false ); } /** * Add a sub query. * * @param string $column - Column name to check. * @param string $operator - Statement operator. * @param callable $callback - Callback that gets a `Query_Builder` and modifies it. * @param string $and_or - Boolean relation, one of `and` / `or`. * * @return $this */ public function where_sub( $column, $operator, callable $callback, $and_or = self::RELATION_AND ) { call_user_func( $callback, $query = $this->new_query() ); $this->wheres[] = [ 'type' => self::WHERE_SUB, 'column' => $column, 'operator' => $operator, 'query' => $query, 'and_or' => $and_or, ]; $this->add_binding( $query->get_bindings(), 'where' ); return $this; } /** * Add a nested `WHERE` query. * * @param callable $callback - Callback that gets a `Query_Builder` and modifies it. * @param string $and_or - Boolean relation, one of `and` / `or`. * * @return $this */ public function where_nested( callable $callback, $and_or = self::RELATION_AND ) { call_user_func( $callback, $query = $this->new_query() ); $this->wheres[] = [ 'type' => self::WHERE_NESTED, 'query' => $query, 'and_or' => $and_or, ]; $this->add_binding( $query->get_bindings( 'where' ), 'where' ); return $this; } /** * Add `HAVING` statement. * * @param string $sql - RAW SQL having clause. * @param string $and_or - Boolean relation, one of `and` / `or`. * * @return $this */ public function having_raw( $sql, $and_or = self::RELATION_AND ) { $this->havings[] = [ 'type' => self::HAVING_RAW, 'and_or' => $and_or, 'sql' => $sql, ]; return $this; } /** * Add `OR HAVING` statement. * * @param string $sql - RAW SQL having clause. * * @return $this */ public function or_having_raw( $sql ) { return $this->having_raw( $sql, self::RELATION_OR ); } /** * Add a `JOIN ... ON` statement. * * @param callable $callback - Closure that builds the JOIN clause. * @param string $type - JOIN type. * * @return $this */ public function join( callable $callback, $type = Join_Clause::TYPE_INNER ) { // Validate type. if ( ! in_array( strtolower( $type ), [ Join_Clause::TYPE_INNER, Join_Clause::TYPE_LEFT, Join_Clause::TYPE_RIGHT ], true ) ) { throw new InvalidArgumentException( 'Join type must be "inner", "left" or "right".' ); } call_user_func( $callback, $join = $this->new_join_clause( $type ) ); $this->add_binding( $join->get_bindings(), 'join' ); $this->joins[] = $join; return $this; } /** * @shortcut `$this->join()` */ public function left_join( callable $callback ) { return $this->join( $callback, Join_Clause::TYPE_LEFT ); } /** * @shortcut `$this->join()` */ public function right_join( callable $callback ) { return $this->join( $callback, Join_Clause::TYPE_RIGHT ); } /** * Creates a new Query Builder instance using the same connection as the initiator. * * @return self */ public function new_query() { // Make sure this is `new self` and not `new static`. // When extending the Query Builder, sometimes it comes with default table or queries. // For that reason it should be avoided passing those defaults to `nested` or `sub-queries`. return new self( $this->connection ); } /** * Creates a new Join Clause instance using the same connection as the initiator. * * @param string $type - JOIN type. * * @return Join_Clause */ public function new_join_clause( $type ) { return new Join_Clause( $type, $this->connection ); } /** * Limit the returned results. * Adds a `LIMIT` statement. * * @param int $limit - Max count of results to return. * * @return $this */ public function limit( $limit ) { $this->limit = (int) $limit; return $this; } /** * Add and `OFFSET` statement. * * @param int $offset - Count of results to skip. * * @return $this */ public function offset( $offset ) { $this->offset = (int) $offset; return $this; } /** * Adds an `ORDER BY` statement. * NOTE: `$column` IS NOT ESCAPED & SHOULD BE WHITELISTED! * * @param string $column - Column to order by. * @param string $direction - Direction (`asc` / `desc`). * * @return $this */ public function order_by( $column, $direction = 'asc' ) { if ( ! in_array( strtolower( $direction ), [ 'asc', 'desc' ], true ) ) { throw new InvalidArgumentException( 'Order direction must be "asc" or "desc".' ); } $this->orders[] = [ 'column' => $column, 'direction' => $direction, ]; return $this; } /** * Adds a `GROUP BY` statement. * NOTE: `$column` IS NOT ESCAPED & SHOULD BE WHITELISTED! * * @param string $column - Column to group by. * * @return $this */ public function group_by( $column ) { $this->groups[] = [ 'column' => $column, ]; return $this; } /** * Get the raw bindings array. * * @return array[] */ public function get_raw_bindings() { return $this->bindings; } /** * Get the columns to use inside the SELECT statement. * Defaults to `*` if non are selected. * * @return string */ public function compile_columns() { if ( 0 === count( $this->columns ) ) { return '*'; }; $columns = []; foreach ( $this->columns as $column ) { switch ( $column['type'] ) { case self::COLUMN_BASIC: $column_name = $this->parse_column( $column['column'] ); $as = $this->parse_as( $column['as'] ); $columns[] = "{$column_name}{$as}"; break; case self::COLUMN_SUB_SELECT: $as = $this->parse_as( $column['as'] ); $columns[] = "( {$column['column']} ){$as}"; break; case self::COLUMN_RAW: $columns[] = $column['column']; break; case self::COLUMN_COUNT: $column_name = $this->parse_column( $column['column'] ); $as = $this->parse_as( $column['as'] ); $columns[] = "COUNT({$column_name}){$as}"; break; } } return $this->concatenate( $columns, ', ' ); } /** * Get the raw columns array. * * @return string[] */ public function get_raw_columns() { return $this->columns; } /** * Compile the `columns` & `from` attributes into an actual `SELECT` statement. * * @return string */ public function compile_select() { return $this->concatenate( [ 'SELECT', $this->compile_columns(), 'FROM', $this->compile_from(), ] ); } /** * Compile the table name and alias. * * @return string */ public function compile_from() { $table = $this->wrap_with_backticks( $this->from['table'] ); $as = $this->parse_as( $this->from['as'] ); return "{$table}{$as}"; } /** * Compile the `joins` array into an actual `JOIN` statement. * * @return string */ public function compile_joins() { $joins = []; foreach ( $this->joins as $join ) { /** * @var Join_Clause $join */ $table = $join->compile_from(); $ons = $join->compile_wheres(); switch ( $join->type ) { case Join_Clause::TYPE_INNER: $joins[] = "INNER JOIN {$table} ON {$ons}"; break; case Join_Clause::TYPE_LEFT: $joins[] = "LEFT JOIN {$table} ON {$ons}"; break; case Join_Clause::TYPE_RIGHT: $joins[] = "RIGHT JOIN {$table} ON {$ons}"; break; } } return $this->concatenate( $joins ); } /** * Compile the `wheres` array into an actual `WHERE` statement. * * @return string */ public function compile_wheres() { $wheres = [ '1 = 1', // A default statement for easier `WHERE` concatenation. ]; foreach ( $this->wheres as $where ) { switch ( $where['type'] ) { case self::WHERE_BASIC: $column = $this->parse_column( $where['column'] ); $binding = $this->get_binding_type( $where['value'] ); $wheres[] = "{$where['and_or']} {$column} {$where['operator']} {$binding}"; break; case self::WHERE_NULL: $column = $this->parse_column( $where['column'] ); $wheres[] = "{$where['and_or']} {$column} IS NULL"; break; case self::WHERE_COLUMN: $first = $this->parse_column( $where['first'] ); $second = $this->parse_column( $where['second'] ); $wheres[] = "{$where['and_or']} {$first} {$where['operator']} {$second}"; break; case self::WHERE_IN: // Handle invalid `WHERE IN` - Force the SQL to fail. if ( empty( $where['value'] ) ) { $wheres[] = "{$where['and_or']} 0 = 1"; break; } $column = $this->parse_column( $where['column'] ); $binding = $this->get_binding_type( $where['value'] ); $wheres[] = "{$where['and_or']} {$column} IN( {$binding} )"; break; case self::WHERE_NOT_IN: // Handle invalid `WHERE IN` - Force the SQL to fail. if ( empty( $where['value'] ) ) { $wheres[] = "{$where['and_or']} 1 = 1"; break; } $column = $this->parse_column( $where['column'] ); $binding = $this->get_binding_type( $where['value'] ); $wheres[] = "{$where['and_or']} {$column} NOT IN( {$binding} )"; break; case self::WHERE_SUB: $column = $this->parse_column( $where['column'] ); $sub_query = $where['query']->to_sql(); $wheres[] = "{$where['and_or']} {$column} {$where['operator']} ( {$sub_query} )"; break; case self::WHERE_NESTED: $nested_query = $where['query']->compile_wheres(); $wheres[] = "{$where['and_or']} ( {$nested_query} )"; break; case self::WHERE_EXISTS: $sub_query = $where['query']->to_sql(); $wheres[] = "{$where['and_or']} EXISTS ( {$sub_query} )"; break; case self::WHERE_NOT_EXISTS: $sub_query = $where['query']->to_sql(); $wheres[] = "{$where['and_or']} NOT EXISTS ( {$sub_query} )"; break; } } return $this->concatenate( $wheres ); } /** * Compile the `havings` array into an actual `HAVING` statement. * TODO: Add more types. * * @return string */ public function compile_having() { if ( 0 === count( $this->havings ) ) { return ''; } $havings = [ 'HAVING', '1 = 1', // A default statement for easier `HAVING` concatenation. ]; foreach ( $this->havings as $having ) { switch ( $having['type'] ) { case self::HAVING_RAW: $havings[] = "{$having['and_or']} {$having['sql']}"; break; } } return $this->concatenate( $havings ); } /** * Compile the `groups` array into an actual `GROUP BY` statement. * * @return string */ public function compile_group_by() { if ( 0 === count( $this->groups ) ) { return ''; } $groups = []; foreach ( $this->groups as $group ) { $groups[] = $this->parse_column( $group['column'] ); } return $this->concatenate( [ 'GROUP BY', $this->concatenate( $groups, ', ' ), ] ); } /** * Compile the `orders` array into an actual `ORDER BY` statement. * * @return string */ public function compile_order_by() { if ( 0 === count( $this->orders ) ) { return ''; } $orders = []; foreach ( $this->orders as $order ) { $column = $this->parse_column( $order['column'] ); $orders[] = "{$column} {$order['direction']}"; } return $this->concatenate( [ 'ORDER BY', $this->concatenate( $orders, ', ' ), ] ); } /** * Compile the `limit` attribute into an actual `LIMIT` statement. * * @return string */ public function compile_limit() { return $this->limit ? "LIMIT {$this->limit}" : ''; } /** * Compile the `offset` attribute into an actual `OFFSET` statement. * * @return string */ public function compile_offset() { return $this->offset ? "OFFSET {$this->offset}" : ''; } /** * Get the final SQL of the query, with bindings placeholders. * * @return string */ public function to_sql() { $select = $this->compile_select(); $join = $this->compile_joins(); $where = $this->compile_wheres(); $group_by = $this->compile_group_by(); $having = $this->compile_having(); $order_by = $this->compile_order_by(); $limit = $this->compile_limit(); $offset = $this->compile_offset(); return $this->concatenate( [ $select, $join, 'WHERE', $where, $group_by, $having, $order_by, $limit, $offset, ] ); } /** * Find & get by id. * * @param int $id - ID to search for. * @param string $field - Field name. Defaults to `id`. * * @return array|null */ public function find( $id, $field = 'id' ) { return $this->where( $field, '=', $id )->first(); } /** * Return the first matching row or null otherwise. * * @return array|null */ public function first() { return $this->limit( 1 )->get()->first(); } /** * Pluck a specific column from the query results. * * @param string $column - The column to pluck. * * @return Collection */ public function pluck( $column ) { return $this ->select( [ $column ] ) ->get() ->pluck( $column ); } /** * Return the count of rows based on the query. * * @param string $column * * @return int */ public function count( $column = '*' ) { return (int) ( new Collection( $this->select( [] ) ->add_count_select( $column ) ->first() ) )->first( 0 ); } /** * Get the query result. * * @return Collection */ public function get() { $sql = $this->to_sql(); $bindings = $this->get_bindings(); if ( 0 !== count( $bindings ) ) { $sql = $this->connection->prepare( $sql, $bindings ); } $result = $this->connection->get_results( $sql, ARRAY_A ); $result = new Collection( $result ); // Add aggregations. foreach ( $this->with as $resolver ) { $result = $resolver( $result ); } return $result; } /** * Insert data to a table. * * @param array $values - Array of [ `column` => `value` ] pairs. Non-escaped. * * @return int * @throws \Exception */ public function insert( array $values ) { // Take the raw table name since `wpdb` wraps it with backticks. $table = $this->from['table']; // Data should be escaped since `wpdb` escapes it. // https://developer.wordpress.org/reference/classes/wpdb/insert/ $succeed = $this->connection->insert( $table, $values ); if ( ! $succeed ) { throw new \Exception( $this->connection->last_error ); } return $this->connection->insert_id; } /** * Update data in the table. * * @param array $values - Array of [ `column` => `value` ] pairs. Non-escaped. * * @return bool|int */ public function update( array $values ) { $this->add_binding( array_values( $values ), 'select' ); $columns = []; foreach ( $values as $column => $value ) { $binding_type = $this->get_binding_type( $value ); $column = $this->wrap_with_backticks( $column ); $columns[] = "{$column} = {$binding_type}"; } $table = $this->compile_from(); $columns = $this->concatenate( $columns, ', ' ); $where = $this->compile_wheres(); $sql = $this->concatenate( [ 'UPDATE', $table, 'SET', $columns, 'WHERE', $where, ] ); $prepared = $this->connection->prepare( $sql, $this->get_bindings() ); return $this->connection->query( $prepared ); } /** * Delete data from the table. * * @return bool|int */ public function delete() { $where = $this->compile_wheres(); $table = $this->wrap_with_backticks( $this->from['table'] ); $sql = $this->concatenate( [ 'DELETE FROM', $table, 'WHERE', $where, ] ); $prepared = $this->connection->prepare( $sql, $this->get_bindings() ); return $this->connection->query( $prepared ); } /** * Add an eager loaded relation. * * @param string $key - Array key to store the resolver in. * @param callable $resolver - Resolve function that gets the results and adds the eager loaded relation. * * @return $this */ protected function add_with( $key, callable $resolver ) { $this->with[ $key ] = $resolver; return $this; } /** * Escape a value for `LIKE` statement. * * @param string $value - Value to escape. * * @return string */ protected function escape_like( $value ) { $value = explode( '%', $value ); $value = array_map( function ( $str ) { return $this->connection->esc_like( $str ); }, $value ); return implode( '%', $value ); } /** * Get a flat array of the current bindings. * * @param null|string $type - The binding type to get. * * @return array */ protected function get_bindings( $type = null ) { if ( $type && isset( $this->bindings[ $type ] ) ) { return $this->bindings[ $type ]; } return ( new Collection( $this->bindings ) )->flatten_recursive(); } /** * Add a binding to the bindings array by a sector. * * @param string|array $value - Raw value that needs to be bind. * @param string $type - Bind type (the sector in the SQL query). * * @return $this */ protected function add_binding( $value, $type ) { if ( is_array( $value ) ) { $this->bindings[ $type ] = array_values( array_merge( $this->bindings[ $type ], $value ) ); } else { $this->bindings[ $type ][] = $value; } return $this; } /** * Get the type of the binding type for SQL `prepare` function. * * @param array|string|numeric $value - The value to get the binding for. * * @return string - One of `%d` / `%f` / `%s`. */ protected function get_binding_type( $value ) { if ( is_array( $value ) ) { $bindings = array_map( function( $value ) { return $this->get_binding_type( $value ); }, array_values( $value ) ); return $this->concatenate( $bindings, ', ' ); } return is_float( $value ) ? '%f' : ( is_int( $value ) ? '%d' : '%s' ); } /** * Wrap a value with backticks. * * @param numeric|string|string[] $value - Value to wrap. * * @return string|string[] */ protected function wrap_with_backticks( $value ) { if ( is_array( $value ) ) { return array_map( [ $this, 'wrap_with_backticks' ], $value ); } // It should not wrap '*' with backticks. if ( '*' === $value ) { return $value; } $sanitized_value = is_scalar( $value ) ? preg_replace( '/[^a-zA-Z0-9_\-]/', '', $value ) : ''; return "`{$sanitized_value}`"; } /** * Concatenate an array of segments, removing empties. * * @param array $segments - Segments to concatenate. * @param array $separator - Separator string. Defaults to empty space. * * @return string */ protected function concatenate( array $segments, $separator = ' ' ) { return implode( $separator, array_filter( $segments, function ( $value ) { return '' !== (string) $value; } ) ); } /** * Parse a column by splitting it to table & column names, and wrapping it with backticks. * * @param $column - Column to parse. * * @return string */ protected function parse_column( $column ) { $parsed = explode( '.', $column ); $parsed = $this->wrap_with_backticks( $parsed ); return $this->concatenate( $parsed, '.' ); } protected function parse_as( $as ) { if ( ! $as ) { return ''; } $as = $this->wrap_with_backticks( $as ); return " AS {$as}"; } /** * Determine if a column is already selected. * * @param string $name - Column name to check. * * @return mixed|null */ protected function is_column_selected( $name ) { return ( new Collection( $this->columns ) ) ->find( function ( $column ) use ( $name ) { // Check for aliases. if ( ! empty( $column['as'] ) ) { return $name === $column['as']; } return $name === $column['column']; } ); } } database/model-base.php 0000666 00000006757 15165257376 0011075 0 ustar 00 <?php namespace ElementorPro\Core\Database; use ElementorPro\Core\Utils\Collection; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } abstract class Model_Base implements \JsonSerializable { // Casting types. const TYPE_BOOLEAN = 'boolean'; const TYPE_COLLECTION = 'collection'; const TYPE_INTEGER = 'integer'; const TYPE_STRING = 'string'; const TYPE_JSON = 'json'; const TYPE_DATETIME = 'datetime'; const TYPE_DATETIME_GMT = 'datetime_gmt'; /** * Casts array. * Used to automatically cast values from DB to the appropriate property type. * * @var array */ protected static $casts = []; /** * Model_Base constructor. * * @param array $fields - Fields from the DB to fill. * * @return void */ public function __construct( array $fields ) { foreach ( $fields as $key => $value ) { if ( ! property_exists( $this, $key ) ) { continue; } $this->{$key} = ( empty( static::$casts[ $key ] ) ) ? $value : static::cast( $value, static::$casts[ $key ] ); } } /** * Get the model's table name. * Throws an exception by default in order to require implementation, * since abstract static functions are not allowed. * * @return string */ public static function get_table() { throw new \Exception( 'You must implement `get_table()` inside ' . static::class ); } /** * Create a Query Builder for the model's table. * * @param \wpdb|null $connection - MySQL connection to use. * * @return Query_Builder */ public static function query( \wpdb $connection = null ) { $builder = new Model_Query_Builder( static::class, $connection ); return $builder->from( static::get_table() ); } /** * Cast value into specific type. * * @param $value - Value to cast. * @param $type - Type to cast into. * * @return mixed */ protected static function cast( $value, $type ) { if ( null === $value ) { return null; } switch ( $type ) { case self::TYPE_BOOLEAN: return boolval( $value ); case self::TYPE_COLLECTION: return new Collection( $value ); case self::TYPE_INTEGER: return intval( $value ); case self::TYPE_STRING: return strval( $value ); case self::TYPE_JSON: return json_decode( $value, true ); case self::TYPE_DATETIME: return new \DateTime( $value ); case self::TYPE_DATETIME_GMT: return new \DateTime( $value, new \DateTimeZone( 'GMT' ) ); } return $value; } /** * Cast a model property value into a JSON compatible data type. * * @param $value - Value to cast. * @param $type - Type to cast into. * @param $property_name - The model property name. * * @return mixed */ protected static function json_serialize_property( $value, $type, $property_name ) { switch ( $type ) { case self::TYPE_DATETIME: case self::TYPE_DATETIME_GMT: /** @var \DateTime $value */ return $value->format( 'c' ); } /** @var mixed $value */ return $value; } /** * @return array */ #[\ReturnTypeWillChange] public function jsonSerialize() { return ( new Collection( (array) $this ) ) ->map( function ( $_, $key ) { $value = $this->{$key}; $type = array_key_exists( $key, static::$casts ) ? static::$casts[ $key ] : null; if ( null === $value ) { return $value; } // Can be overridden by child model. $value = static::json_serialize_property( $value, $type, $key ); if ( $value instanceof \JsonSerializable ) { return $value->jsonSerialize(); } return $value; } ) ->all(); } } database/base-migration.php 0000666 00000010705 15165257376 0011752 0 ustar 00 <?php namespace ElementorPro\Core\Database; use Elementor\Core\Utils\Collection; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } abstract class Base_Migration { /* * @see https://github.com/WordPress/WordPress/blob/d2694aa46647af48d1bcaff48a4f6cac7f5cf470/wp-admin/includes/schema.php#L49 */ const MAX_INDEX_LENGTH = 191; /** * @var \wpdb */ protected $wpdb; /** * @param \wpdb|null $wpdb_instance */ public function __construct( \wpdb $wpdb_instance = null ) { if ( ! $wpdb_instance ) { global $wpdb; $this->wpdb = $wpdb; } else { $this->wpdb = $wpdb_instance; } } /** * Runs when upgrading the database * * @return void */ abstract public function up(); /** * Runs when downgrading the database. * * @return void */ abstract public function down(); /** * A util to run SQL for creating tables. * * @param $table_name * @param array $columns */ protected function create_table( $table_name, array $columns ) { $table_name = "{$this->wpdb->prefix}{$table_name}"; $columns_sql = ( new Collection( $columns ) ) ->map( function( $definition, $col_name ) { return "`{$col_name}` {$definition}"; } ) ->implode( ', ' ); $query = "CREATE TABLE `{$table_name}` ({$columns_sql}) {$this->wpdb->get_charset_collate()};"; $this->run_db_delta( $query ); } /** * Add columns. * * @param $table_name * @param array $columns */ protected function add_columns( $table_name, array $columns ) { $table_name = "{$this->wpdb->prefix}{$table_name}"; $add_columns_sql = ( new Collection( $columns ) ) ->map( function ( $definition, $column_name ) { return "ADD COLUMN `{$column_name}` {$definition}"; } ) ->implode( ', ' ); $this->wpdb->query( "ALTER TABLE `{$table_name}` {$add_columns_sql};" ); // phpcs:ignore } /** * Drop columns * * @param $table_name * @param array $columns */ protected function drop_columns( $table_name, array $columns ) { $table_name = "{$this->wpdb->prefix}{$table_name}"; $drop_columns_sql = ( new Collection( $columns ) ) ->map( function ( $column_name ) { return "DROP COLUMN `{$column_name}`"; } ) ->implode( ', ' ); $this->wpdb->query( "ALTER TABLE `{$table_name}` {$drop_columns_sql};" ); // phpcs:ignore } /** * A util to run SQL for dropping tables. * * @param $table_name */ protected function drop_table( $table_name ) { $table_name = "{$this->wpdb->prefix}{$table_name}"; $query = "DROP TABLE IF EXISTS `{$table_name}`;"; // Safe query that shouldn't be escaped. $this->wpdb->query( $query ); // phpcs:ignore } /** * A util to run SQL for creating indexes. * * @param $table_name * @param array $column_names */ protected function create_indexes( $table_name, array $column_names ) { $max_index_length = static::MAX_INDEX_LENGTH; $table_name = "{$this->wpdb->prefix}{$table_name}"; // Safe query that shouldn't be escaped. $column_definition = $this->get_column_definition( $table_name ); if ( ! $column_definition ) { return; } $should_set_max_length_index = ( new Collection( $column_definition ) ) ->filter( function ( $value ) { preg_match( '/\((\d+)\)/', $value['Type'], $match ); return ( isset( $match[1] ) && intval( $match[1] ) > Base_Migration::MAX_INDEX_LENGTH ) || in_array( strtolower( $value['Type'] ), [ 'text', 'longtext' ], true ); } ) ->pluck( 'Field' ) ->values(); $indexes_sql = ( new Collection( $column_names ) ) ->map( function( $col_name ) use ( $should_set_max_length_index, $max_index_length ) { $max_index_length_sql = ''; if ( in_array( $col_name, $should_set_max_length_index, true ) ) { $max_index_length_sql = " ({$max_index_length})"; } return "ADD INDEX `{$col_name}_index` (`{$col_name}`{$max_index_length_sql})"; } ) ->implode( ', ' ); // Safe query that shouldn't be escaped. $this->wpdb->query( "ALTER TABLE `{$table_name}` {$indexes_sql};" ); // phpcs:ignore } /** * @param $table_name * * @return array */ protected function get_column_definition( $table_name ) { return $this->wpdb->get_results( "SHOW COLUMNS FROM `{$table_name}`;", ARRAY_A ); // phpcs:ignore } /** * Runs global dbDelta function (wrapped into method to allowing mock for testing). * * @param $query * * @return array */ protected function run_db_delta( $query ) { require_once ABSPATH . 'wp-admin/includes/upgrade.php'; return dbDelta( $query ); } } app/assets/styles/app-imports.scss 0000666 00000001104 15165257376 0013342 0 ustar 00 @import "../../modules/site-editor/assets/js/molecules/site-template.scss"; @import "../../modules/site-editor/assets/js/pages/add-new.scss"; @import "../../modules/site-editor/assets/js/pages/template-type.scss"; @import "../../modules/site-editor/assets/js/pages/conditions/conditions.scss"; @import "../../modules/site-editor/assets/js/molecules/back-button.scss"; @import "../../modules/site-editor/assets/js/atoms/indicator-bullet.scss"; @import "../../modules/site-editor/assets/js/atoms/preview-iframe.scss"; @import "../../modules/site-editor/assets/js/site-editor.scss"; app/assets/js/hooks/use-feature-lock.js 0000666 00000001155 15165257376 0014125 0 ustar 00 import ConnectButtonUI from '../ui/connect-button'; import { htmlDecodeTextContent, replaceUtmPlaceholders } from '../utils'; export default function useFeatureLock( featureName ) { const appConfig = elementorAppProConfig[ featureName ] ?? {}, isLocked = appConfig.lock?.is_locked ?? false; const buttonText = htmlDecodeTextContent( appConfig.lock?.button.text ); const buttonLink = replaceUtmPlaceholders( appConfig.lock?.button.url ?? '', appConfig.utms ?? {}, ); const ConnectButton = () => ( <ConnectButtonUI text={ buttonText } url={ buttonLink } /> ); return { isLocked, ConnectButton, }; } app/assets/js/utils.js 0000666 00000001452 15165257376 0010767 0 ustar 00 // Copied from Core. export const arrayToClassName = ( array, action ) => { return array .filter( ( item ) => 'object' === typeof ( item ) ? Object.entries( item )[ 0 ][ 1 ] : item ) .map( ( item ) => { const value = 'object' === typeof ( item ) ? Object.entries( item )[ 0 ][ 0 ] : item; return action ? action( value ) : value; } ) .join( ' ' ); }; export const htmlDecodeTextContent = ( input ) => { const doc = new DOMParser().parseFromString( input, 'text/html' ); return doc.documentElement.textContent; }; export const replaceUtmPlaceholders = ( link = '', utms = {} ) => { if ( ! link || ! utms ) { return link; } Object.keys( utms ).forEach( ( key ) => { const match = new RegExp( `%%${ key }%%`, 'g' ); link = link.replace( match, utms[ key ] ); } ); return link; }; app/assets/js/ui/connect-button.js 0000666 00000001643 15165257376 0013210 0 ustar 00 import React, { useRef, useEffect } from 'react'; import { Button } from '@elementor/app-ui'; import { arrayToClassName } from '../utils.js'; const ConnectButton = ( props ) => { const className = arrayToClassName( [ 'e-app-connect-button', props.className, ] ); const buttonRef = useRef( null ); useEffect( () => { if ( ! buttonRef.current ) { return; } jQuery( buttonRef.current ).elementorConnect(); }, [] ); return ( <Button { ...props } elRef={ buttonRef } className={ className } /> ); }; ConnectButton.propTypes = { ...Button.propTypes, text: PropTypes.string.isRequired, url: PropTypes.string.isRequired, className: PropTypes.string, }; ConnectButton.defaultProps = { className: '', variant: 'contained', size: 'sm', color: 'cta', target: '_blank', rel: 'noopener noreferrer', text: __( 'Connect & Activate', 'elementor' ), }; export default React.memo( ConnectButton ); app/assets/js/index.js 0000666 00000000125 15165257376 0010732 0 ustar 00 import Module from '../../modules/site-editor/assets/js/site-editor'; new Module(); app/app.php 0000666 00000004573 15165257376 0006653 0 ustar 00 <?php namespace ElementorPro\Core\App; use Elementor\Core\Base\App as BaseApp; use ElementorPro\Plugin; use ElementorPro\Core\App\Modules\SiteEditor\Module as SiteEditor; use ElementorPro\Core\App\Modules\KitLibrary\Module as KitLibrary; use ElementorPro\Core\App\Modules\Onboarding\Module as Onboarding; use ElementorPro\Core\App\Modules\ImportExport\Module as ImportExport; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class App extends BaseApp { /** * Get module name. * * Retrieve the module name. * * @since 3.0.0 * @access public * * @return string Module name. */ public function get_name() { return 'app-pro'; } public function init() { $this->enqueue_assets(); } public function set_menu_url() { Plugin::elementor()->app->set_settings( 'menu_url', Plugin::elementor()->app->get_base_url() . '#/site-editor' ); } protected function get_init_settings() { return [ 'baseUrl' => $this->get_assets_base_url(), ]; } protected function get_assets_base_url() { return ELEMENTOR_PRO_URL; } private function enqueue_assets() { wp_enqueue_style( 'elementor-pro-app', $this->get_css_assets_url( 'app', null, 'default', true ), [ 'elementor-app', 'select2', ], ELEMENTOR_VERSION ); wp_enqueue_script( 'elementor-pro-app', $this->get_js_assets_url( 'app' ), [ 'wp-i18n', 'elementor-app-packages', 'elementor-common', 'select2', ], ELEMENTOR_PRO_VERSION, true ); wp_set_script_translations( 'elementor-pro-app', 'elementor-pro' ); } private function enqueue_config() { // If script didn't loaded, config is still relevant, enqueue without a file. if ( ! wp_script_is( 'elementor-pro-app' ) ) { wp_register_script( 'elementor-pro-app', false, [], ELEMENTOR_PRO_VERSION ); wp_enqueue_script( 'elementor-pro-app' ); } $this->print_config( 'elementor-pro-app' ); } public function __construct() { $this->add_component( 'site-editor', new SiteEditor() ); $this->add_component( 'kit-library', new KitLibrary() ); $this->add_component( 'onboarding', new Onboarding() ); $this->add_component( 'import-export', new ImportExport() ); add_action( 'elementor/app/init', [ $this, 'init' ] ); add_action( 'elementor/common/after_register_scripts', function () { $this->enqueue_config(); } ); add_action( 'elementor/init', [ $this, 'set_menu_url' ] ); } } app/modules/site-editor/assets/js/atoms/preview-iframe.js 0000666 00000002211 15165257376 0017566 0 ustar 00 import './preview-iframe.scss'; export default function PreviewIFrame( props ) { const ref = React.useRef( null ), previewBreakpoint = 1200, [ scale, setScale ] = React.useState( 1 ), [ height, setHeight ] = React.useState( 0 ); // In order to make sure that the iframe itself show the content in specific viewport, // and it should fit to the size of the card, there is a use of css props `scale` and `height`, // and another element that wraps the iframe to be the guidelines of the iframe sizes. React.useEffect( () => { const currentScale = ref.current.clientWidth / previewBreakpoint; setScale( currentScale ); setHeight( ref.current.clientHeight / currentScale ); }, [] ); return ( <div ref={ ref } className={ `site-editor__preview-iframe site-editor__preview-iframe--${ props.templateType }` } > <iframe title="preview" src={ props.src } className={ `site-editor__preview-iframe__iframe` } style={ { transform: `scale(${ scale })`, height, width: previewBreakpoint } } /> </div> ); } PreviewIFrame.propTypes = { src: PropTypes.string.isRequired, templateType: PropTypes.string.isRequired, }; app/modules/site-editor/assets/js/atoms/indicator-bullet.scss 0000666 00000002110 15165257376 0020442 0 ustar 00 $eps-indicator-bullet-border-color: theme-colors(light); $eps-indicator-bullet-dark-border-color: dark-tints(500); :root { --indicator-bullet-border-color: #{$eps-indicator-bullet-border-color}; } .eps-theme-dark { --indicator-bullet-border-color: #{$eps-indicator-bullet-dark-border-color}; } $eps-indicator-bullet-size: spacing(16) * 0.75; $eps-indicator-bullet-color: tints(300); $eps-indicator-bullet-color-active: theme-colors(success); $eps-indicator-bullet-box-shadow: $eps-box-shadow-1; $eps-indicator-bullet-radius: 100%; $eps-indicator-bullet-border: 2px solid var(--indicator-bullet-border-color); $eps-indicator-bullet-spacing: spacing(10); .eps-indicator-bullet { display: block; flex-shrink: 0; width: $eps-indicator-bullet-size; height: $eps-indicator-bullet-size; box-shadow: $eps-indicator-bullet-box-shadow; background-color: $eps-indicator-bullet-color; border: $eps-indicator-bullet-border; border-radius: $eps-indicator-bullet-radius; margin-inline-end: $eps-indicator-bullet-spacing; &--active { background-color: $eps-indicator-bullet-color-active; } } app/modules/site-editor/assets/js/atoms/preview-iframe.scss 0000666 00000000345 15165257376 0020133 0 ustar 00 .site-editor__preview-iframe { height: 50vh; position: relative; &__iframe { top: 0; left: 0; position: absolute; border: none; transform-origin: 0 0; height: 100%; } &--header, &--footer{ height: 15vh; } } app/modules/site-editor/assets/js/atoms/indicator-bullet.js 0000666 00000000431 15165257376 0020107 0 ustar 00 import './indicator-bullet.scss'; export const Indicator = ( props ) => { let className = 'eps-indicator-bullet'; if ( props.active ) { className += ` ${ className }--active`; } return <i className={ className } />; }; Indicator.propTypes = { active: PropTypes.bool, }; app/modules/site-editor/assets/js/site-editor.js 0000666 00000005750 15165257376 0015764 0 ustar 00 import { Router, LocationProvider, Redirect } from '@reach/router'; import Templates from './pages/templates'; import TemplateType from './pages/template-type'; import AddNew from './pages/add-new'; import Conditions from './pages/conditions/conditions'; import Import from './pages/import'; import TemplatesProvider, { Context as TemplatesContext } from './context/templates'; import { Layout, AllPartsButton, NotFound } from '@elementor/site-editor'; import { ErrorBoundary, Grid, Button } from '@elementor/app-ui'; import router from '@elementor/router'; import Component from './data/component'; import useFeatureLock from 'elementor-pro-app/hooks/use-feature-lock'; import './site-editor.scss'; function SiteEditor() { const { isLocked } = useFeatureLock( 'site-editor' ); const basePath = 'site-editor'; const headerButtons = [ { id: 'import', text: __( 'import', 'elementor-pro' ), hideText: true, icon: 'eicon-upload-circle-o', onClick: () => router.appHistory.navigate( basePath + '/import' ), }, ]; // Remove Core cache. elementorCommon.ajax.invalidateCache( { unique_id: 'app_site_editor_template_types', } ); const SiteEditorDefault = () => { const { templates } = React.useContext( TemplatesContext ); if ( Object.keys( templates ).length ) { return <Redirect from={ '/' } to={ '/' + basePath + '/templates' } noThrow={ true } />; } return <Redirect from={ '/' } to={ '/' + basePath + '/add-new' } noThrow={ true } />; }; return ( <ErrorBoundary title={ __( 'Theme Builder could not be loaded', 'elementor-pro' ) } learnMoreUrl="https://go.elementor.com/app-theme-builder-load-issue" > <Layout allPartsButton={ <AllPartsButton url={ '/' + basePath } /> } headerButtons={ headerButtons } titleRedirectRoute={ '/' + basePath } promotion={ isLocked }> <Grid container className="e-site-editor__content_container"> <Grid item className="e-site-editor__content_container_main"> <TemplatesProvider> <LocationProvider history={ router.appHistory }> <Router> <SiteEditorDefault path={ basePath } /> <Templates path={ basePath + '/templates' } /> <TemplateType path={ basePath + '/templates/:type/*id' } /> <AddNew path={ basePath + '/add-new' } /> <Conditions path={ basePath + '/conditions/:id' } /> <Import path={ basePath + '/import' } /> <NotFound default /> </Router> </LocationProvider> </TemplatesProvider> </Grid> <Grid item className="e-site-editor__content_container_secondary"> <Button text={ __( 'Switch to table view', 'elementor-pro' ) } url={ elementorAppProConfig[ 'site-editor' ]?.urls?.legacy_view } /> </Grid> </Grid> </Layout> </ErrorBoundary> ); } export default class Module { constructor() { elementorCommon.debug.addURLToWatch( 'elementor-pro/assets' ); $e.components.register( new Component() ); router.addRoute( { path: '/site-editor/*', component: SiteEditor, } ); } } app/modules/site-editor/assets/js/editor.js 0000666 00000000652 15165257376 0015016 0 ustar 00 import Component from './data/component'; import { Templates } from './data/commands'; export default class Module extends elementorModules.editor.utils.Module { onElementorInit() { const config = elementor.documents.getCurrent().config; if ( config.support_site_editor ) { $e.components.register( new Component() ); $e.data.deleteCache( $e.components.get( Component.namespace ), Templates.signature ); } } } app/modules/site-editor/assets/js/part-actions/dialogs-and-buttons.js 0000666 00000003671 15165257376 0022016 0 ustar 00 import DialogRename from './dialog-rename'; import DialogDelete from './dialog-delete'; import { Button, Popover } from '@elementor/app-ui'; export const handlers = { rename: null, delete: null, }; // TODO: Think about refactor to portals: https://reactjs.org/docs/portals.html export function PartActionsDialogs() { const [ DialogRenameId, setDialogRenameId ] = React.useState( null ); const [ DialogDeleteId, setDialogDeleteId ] = React.useState( null ); handlers.rename = setDialogRenameId; handlers.delete = setDialogDeleteId; return ( <> <DialogRename id={ DialogRenameId } setId={ setDialogRenameId } /> <DialogDelete id={ DialogDeleteId } setId={ setDialogDeleteId } /> </> ); } export default function PartActionsButtons( props ) { const [ showMenu, setShowMenu ] = React.useState( false ); let SiteTemplatePopover = ''; if ( showMenu ) { SiteTemplatePopover = ( <Popover closeFunction={ () => setShowMenu( ! showMenu ) }> <li> <Button className="eps-popover__item" icon="eicon-sign-out" text={ __( 'Export', 'elementor-pro' ) } url={ props.exportLink } /> </li> <li> <Button className="eps-popover__item eps-popover__item--danger" icon="eicon-trash-o" text={ __( 'Trash', 'elementor-pro' ) } onClick={ () => handlers.delete( props.id ) } /> </li> <li> <Button className="eps-popover__item" icon="eicon-edit" text={ __( 'Rename', 'elementor-pro' ) } onClick={ () => handlers.rename( props.id ) } /> </li> </Popover> ); } return ( <div className="eps-popover__container"> <Button text={ __( 'Toggle', 'elementor-pro' ) } hideText={ true } icon="eicon-ellipsis-h" size="lg" onClick={ () => setShowMenu( ! showMenu ) } /> { SiteTemplatePopover } </div> ); } PartActionsButtons.propTypes = { id: PropTypes.number.isRequired, exportLink: PropTypes.string.isRequired, }; app/modules/site-editor/assets/js/part-actions/dialog-delete.js 0000666 00000002113 15165257376 0020625 0 ustar 00 import { Dialog } from '@elementor/app-ui'; import { Context as TemplatesContext } from '../context/templates'; export default function DialogDelete( props ) { const { deleteTemplate, findTemplateItemInState } = React.useContext( TemplatesContext ); const closeDialog = ( shouldUpdate ) => { props.setId( null ); if ( shouldUpdate ) { deleteTemplate( props.id ); } }; if ( ! props.id ) { return ''; } const template = findTemplateItemInState( props.id ); return ( <Dialog title={ __( 'Move Item To Trash', 'elementor-pro' ) } text={ __( 'Are you sure you want to move this item to trash:', 'elementor-pro' ) + ` "${ template.title }"` } onSubmit={ () => closeDialog( true ) } approveButtonText={ __( 'Move to Trash', 'elementor-pro' ) } approveButtonOnClick={ () => closeDialog( true ) } approveButtonColor="danger" dismissButtonText={ __( 'Cancel', 'elementor-pro' ) } dismissButtonOnClick={ () => closeDialog() } onClose={ () => closeDialog() } /> ); } DialogDelete.propTypes = { id: PropTypes.number, setId: PropTypes.func.isRequired, }; app/modules/site-editor/assets/js/part-actions/dialog-rename.js 0000666 00000002724 15165257376 0020642 0 ustar 00 import { useEffect } from 'react'; import { Dialog } from '@elementor/app-ui'; import { Context as TemplatesContext } from '../context/templates'; export default function DialogRename( props ) { const { findTemplateItemInState, updateTemplate } = React.useContext( TemplatesContext ), template = findTemplateItemInState( props.id ); const [ title, setTitle ] = React.useState( '' ); useEffect( () => { // The "title" state should be updated if the template title changed. if ( template ) { setTitle( template.title ); } }, [ template ] ); const closeDialog = ( shouldUpdate ) => { props.setId( null ); if ( shouldUpdate ) { updateTemplate( props.id, { post_title: title } ); } }; if ( ! props.id ) { return ''; } return ( <Dialog title={ __( 'Rename Site Part', 'elementor-pro' ) } approveButtonText={ __( 'Change', 'elementor-pro' ) } onSubmit={ () => closeDialog( true ) } approveButtonOnClick={ () => closeDialog( true ) } approveButtonColor="primary" dismissButtonText={ __( 'Cancel', 'elementor-pro' ) } dismissButtonOnClick={ () => closeDialog() } onClose={ () => closeDialog() } > <input type="text" className="eps-input eps-input-text eps-input--block" // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus value={ title } onChange={ ( e ) => setTitle( e.target.value ) } /> </Dialog> ); } DialogRename.propTypes = { id: PropTypes.number, setId: PropTypes.func.isRequired, }; app/modules/site-editor/assets/js/site-editor.scss 0000666 00000000371 15165257376 0016315 0 ustar 00 .e-site-editor__content_container { flex-direction: column; min-height: 100%; flex-wrap: nowrap; } .e-site-editor__content_container_main { flex: 1; } .e-site-editor__content_container_secondary { margin: 0 auto; padding-top: spacing(44); } app/modules/site-editor/assets/js/context/templates.js 0000666 00000006744 15165257376 0017222 0 ustar 00 import BaseContext from './base-context'; import { Templates } from '../data/commands'; import Component from '../data/component'; export const Context = React.createContext(); export class TemplatesProvider extends BaseContext { static propTypes = { children: PropTypes.object.isRequired, }; static actions = { FETCH: 'fetch', DELETE: 'delete', UPDATE: 'update', IMPORT: 'import', }; constructor( props ) { super( props ); this.state = { ...this.state, action: { ...this.state.action, current: TemplatesProvider.actions.FETCH, loading: true, }, templates: {}, updateTemplateItemState: this.updateTemplateItemState.bind( this ), findTemplateItemInState: this.findTemplateItemInState.bind( this ), fetchTemplates: this.fetchTemplates.bind( this ), deleteTemplate: this.deleteTemplate.bind( this ), updateTemplate: this.updateTemplate.bind( this ), importTemplates: this.importTemplates.bind( this ), }; } componentDidMount() { this.fetchTemplates(); } importTemplates( { fileName, fileData } ) { return this.executeAction( TemplatesProvider.actions.IMPORT, () => $e.data.create( Templates.signature, { fileName, fileData } ), ).then( ( response ) => { this.updateTemplatesState( ( prev ) => ( { ...prev, ...Object.values( response.data ).reduce( ( current, template ) => { if ( ! template.supportsSiteEditor ) { return current; } return { ...current, [ template.id ]: template }; }, {}, ), } ) ); return response; } ); } deleteTemplate( id ) { return this.executeAction( TemplatesProvider.actions.DELETE, () => $e.data.delete( Templates.signature, { id } ), ).then( () => { this.updateTemplatesState( ( prev ) => { const newTemplates = { ...prev }; delete newTemplates[ id ]; return newTemplates; } ); } ); } updateTemplate( id, args ) { return this.executeAction( TemplatesProvider.actions.UPDATE, () => $e.data.update( Templates.signature, args, { id } ), ).then( ( response ) => { this.updateTemplateItemState( id, response.data ); } ); } fetchTemplates() { return this.executeAction( TemplatesProvider.actions.FETCH, () => $e.data.get( Templates.signature, {}, { refresh: true } ), ).then( ( response ) => { this.updateTemplatesState( () => Object.values( response.data ).reduce( ( current, template ) => ( { ...current, [ template.id ]: template } ), {}, ), false ); } ); } updateTemplateItemState( id, args ) { return this.updateTemplatesState( ( prev ) => { const template = { ...prev[ id ], ...args, }; return { ...prev, [ id ]: template, }; } ); } updateTemplatesState( callback, clearCache = true ) { if ( clearCache ) { $e.data.deleteCache( $e.components.get( Component.namespace ), Templates.signature ); } return this.setState( ( prev ) => { return { templates: callback( prev.templates ) }; } ); } findTemplateItemInState( id ) { return this.state.templates[ id ]; } render() { if ( this.state.action.current === TemplatesProvider.actions.FETCH ) { if ( this.state.action.error ) { return <h3>{ __( 'Error:', 'elementor-pro' ) } { this.state.action.error }</h3>; } if ( this.state.action.loading ) { return <h3>{ __( 'Loading', 'elementor-pro' ) }...</h3>; } } return ( <Context.Provider value={ this.state }> { this.props.children } </Context.Provider> ); } } export default TemplatesProvider; app/modules/site-editor/assets/js/context/services/conditions-config.js 0000666 00000005145 15165257376 0022455 0 ustar 00 import { ConditionsConfig as ConditionsConfigCommand } from '../../data/commands'; export class ConditionsConfig { static instance; config = null; constructor( config ) { this.config = config; } /** * @return {Promise<ConditionsConfig>} - */ static create() { if ( ConditionsConfig.instance ) { return Promise.resolve( ConditionsConfig.instance ); } return $e.data.get( ConditionsConfigCommand.signature, {}, { refresh: true } ) .then( ( response ) => { ConditionsConfig.instance = new ConditionsConfig( response.data ); return ConditionsConfig.instance; } ); } /** * Get main options for condition name. * * @return {Array} - */ getOptions() { return this.getSubOptions( 'general', true ) .map( ( { label, value } ) => { return { label, value, }; } ); } /** * Get the sub options for the select. * * @param {string} itemName * @param {boolean} isSubItem * @return {Array} - */ getSubOptions( itemName, isSubItem = false ) { const config = this.config[ itemName ]; if ( ! config ) { return []; } return [ { label: config.all_label, value: isSubItem ? itemName : '' }, ...config.sub_conditions.map( ( subName ) => { const subConfig = this.config[ subName ]; return { label: subConfig.label, value: subName, children: subConfig.sub_conditions.length ? this.getSubOptions( subName, true ) : null, }; } ), ]; } /** * Get the autocomplete property from the conditions config * * @param {string} sub * @return {{}|any} - */ getSubIdAutocomplete( sub ) { const config = this.config[ sub ]; if ( ! config || ! ( 'object' === typeof ( config.controls ) ) ) { return {}; } const controls = Object.values( config.controls ); if ( ! controls?.[ 0 ]?.autocomplete ) { return {}; } return controls[ 0 ].autocomplete; } /** * Calculate instances from the conditions. * * @param {Array} conditions * @return {Object} - */ calculateInstances( conditions ) { let instances = conditions.reduce( ( current, condition ) => { if ( 'exclude' === condition.type ) { return current; } const key = condition.sub || condition.name, config = this.config[ key ]; if ( ! config ) { return current; } const instanceLabel = condition.subId ? `${ config.label } #${ condition.subId }` : config.all_label; return { ...current, [ key ]: instanceLabel, }; }, {} ); if ( 0 === Object.keys( instances ).length ) { instances = [ __( 'No instances', 'elementor-pro' ) ]; } return instances; } } export default ConditionsConfig; app/modules/site-editor/assets/js/context/models/condition.js 0000666 00000002162 15165257376 0020463 0 ustar 00 export default class Condition { id = elementorCommon.helpers.getUniqueId(); default = ''; type = 'include'; name = ''; sub = ''; subId = ''; options = []; subOptions = []; subIdAutocomplete = []; subIdOptions = []; conflictErrors = []; constructor( args ) { this.set( args ); } set( args ) { Object.assign( this, args ); return this; } clone() { return Object.assign( new Condition(), this ); } remove( keys ) { if ( ! Array.isArray( keys ) ) { keys = [ keys ]; } keys.forEach( ( key ) => { delete this[ key ]; } ); return this; } only( keys ) { if ( ! Array.isArray( keys ) ) { keys = [ keys ]; } const keysToRemove = Object.keys( this ) .filter( ( conditionKey ) => ! keys.includes( conditionKey ) ); this.remove( keysToRemove ); return this; } toJson() { return JSON.stringify( this ); } toString() { return this.forDb().filter( ( item ) => item ).join( '/' ); } forDb() { return [ this.type, this.name, this.sub, this.subId ]; } forContext() { return { type: this.type, name: this.name, sub: this.sub, subId: this.subId, }; } } app/modules/site-editor/assets/js/context/conditions.js 0000666 00000017646 15165257376 0017400 0 ustar 00 import Condition from './models/condition'; import ConditionsConfig from './services/conditions-config'; import BaseContext from './base-context'; import { TemplatesConditions, TemplatesConditionsConflicts } from '../data/commands'; export const Context = React.createContext(); export class ConditionsProvider extends BaseContext { static propTypes = { children: PropTypes.any.isRequired, currentTemplate: PropTypes.object.isRequired, onConditionsSaved: PropTypes.func.isRequired, validateConflicts: PropTypes.bool, }; static defaultProps = { validateConflicts: true, }; static actions = { FETCH_CONFIG: 'fetch-config', SAVE: 'save', CHECK_CONFLICTS: 'check-conflicts', }; /** * Holds the conditions config object. * * @type {ConditionsConfig} */ conditionsConfig = null; /** * ConditionsProvider constructor. * * @param {any} props */ constructor( props ) { super( props ); this.state = { ...this.state, conditions: {}, updateConditionItemState: this.updateConditionItemState.bind( this ), removeConditionItemInState: this.removeConditionItemInState.bind( this ), createConditionItemInState: this.createConditionItemInState.bind( this ), findConditionItemInState: this.findConditionItemInState.bind( this ), saveConditions: this.saveConditions.bind( this ), }; } /** * Fetch the conditions config, then normalize the conditions and then setup titles for * the subIds. */ componentDidMount() { this.executeAction( ConditionsProvider.actions.FETCH_CONFIG, () => ConditionsConfig.create() ) .then( ( conditionsConfig ) => this.conditionsConfig = conditionsConfig ) .then( this.normalizeConditionsState.bind( this ) ) .then( this.setSubIdTitles.bind( this ) ); } /** * Execute a request to save the template conditions. * * @return {any} - */ saveConditions() { const conditions = Object.values( this.state.conditions ) .map( ( condition ) => condition.forDb() ); return this.executeAction( ConditionsProvider.actions.SAVE, () => $e.data.update( TemplatesConditions.signature, { conditions }, { id: this.props.currentTemplate.id } ), ).then( () => { const contextConditions = Object.values( this.state.conditions ) .map( ( condition ) => condition.forContext() ); this.props.onConditionsSaved( this.props.currentTemplate.id, { conditions: contextConditions, instances: this.conditionsConfig.calculateInstances( Object.values( this.state.conditions ) ), isActive: !! ( Object.keys( this.state.conditions ).length && 'publish' === this.props.currentTemplate.status ), } ); } ); } /** * Check for conflicts in the server and mark the condition if there * is a conflict. * * @param {any} condition */ checkConflicts( condition ) { return this.executeAction( ConditionsProvider.actions.CHECK_CONFLICTS, () => $e.data.get( TemplatesConditionsConflicts.signature, { post_id: this.props.currentTemplate.id, condition: condition.clone().toString(), } ), ).then( ( response ) => this.updateConditionItemState( condition.id, { conflictErrors: Object.values( response.data ) }, false ), ); } /** * Fetching subId titles. * * @param {any} condition * @return {Promise<unknown>} - */ fetchSubIdsTitles( condition ) { return new Promise( ( resolve ) => { return elementorCommon.ajax.loadObjects( { action: 'query_control_value_titles', ids: _.isArray( condition.subId ) ? condition.subId : [ condition.subId ], data: { get_titles: condition.subIdAutocomplete, unique_id: elementorCommon.helpers.getUniqueId(), }, success( response ) { resolve( response ); }, } ); } ); } /** * Get the conditions from the template and normalize it to data structure * that the components can work with. */ normalizeConditionsState() { this.updateConditionsState( () => { return this.props.currentTemplate.conditions.reduce( ( current, condition ) => { const conditionObj = new Condition( { ...condition, default: this.props.currentTemplate.defaultCondition, options: this.conditionsConfig.getOptions(), subOptions: this.conditionsConfig.getSubOptions( condition.name ), subIdAutocomplete: this.conditionsConfig.getSubIdAutocomplete( condition.sub ), supIdOptions: condition.subId ? [ { value: condition.subId, label: condition.subId } ] : [], } ); return { ...current, [ conditionObj.id ]: conditionObj, }; }, {} ); } ).then( () => { Object.values( this.state.conditions ).forEach( ( condition ) => this.checkConflicts( condition ) ); } ); } /** * Set titles to the subIds, * for the first render of the component. */ setSubIdTitles() { return Object.values( this.state.conditions ).forEach( ( condition ) => { if ( ! condition.subId ) { return; } return this.fetchSubIdsTitles( condition ) .then( ( response ) => this.updateConditionItemState( condition.id, { subIdOptions: [ { label: Object.values( response )[ 0 ], value: condition.subId, } ], }, false ), ); } ); } /** * Update state of specific condition item. * * @param {any} id * @param {any} args * @param {boolean} shouldCheckConflicts */ updateConditionItemState( id, args, shouldCheckConflicts = true ) { if ( args.name ) { args.subOptions = this.conditionsConfig.getSubOptions( args.name ); } if ( args.sub || args.name ) { args.subIdAutocomplete = this.conditionsConfig.getSubIdAutocomplete( args.sub ); // In case that the condition has been changed, it will set the options of the subId // to empty array to let select2 autocomplete handle the options. args.subIdOptions = []; } this.updateConditionsState( ( prev ) => { const condition = prev[ id ]; return { ...prev, [ id ]: condition.clone().set( args ), }; } ).then( () => { if ( shouldCheckConflicts ) { this.checkConflicts( this.findConditionItemInState( id ) ); } } ); } /** * Remove a condition item from the state. * * @param {any} id */ removeConditionItemInState( id ) { this.updateConditionsState( ( prev ) => { const newConditions = { ...prev }; delete newConditions[ id ]; return newConditions; } ); } /** * Add a new condition item into the state. * * @param {boolean} shouldCheckConflicts */ createConditionItemInState( shouldCheckConflicts = true ) { const defaultCondition = this.props.currentTemplate.defaultCondition, newCondition = new Condition( { name: defaultCondition, default: defaultCondition, options: this.conditionsConfig.getOptions(), subOptions: this.conditionsConfig.getSubOptions( defaultCondition ), subIdAutocomplete: this.conditionsConfig.getSubIdAutocomplete( '' ), } ); this.updateConditionsState( ( prev ) => ( { ...prev, [ newCondition.id ]: newCondition } ) ) .then( () => { if ( shouldCheckConflicts ) { this.checkConflicts( newCondition ); } } ); } /** * Find a condition item from the conditions state. * * @param {any} id * @return {Condition|null} - */ findConditionItemInState( id ) { return Object.values( this.state.conditions ).find( ( c ) => c.id === id ); } /** * Update the whole conditions state. * * @param {Function} callback * @return {Promise<any>} - */ updateConditionsState( callback ) { return new Promise( ( resolve ) => this.setState( ( prev ) => ( { conditions: callback( prev.conditions ) } ), resolve ), ); } /** * Renders the provider. * * @return {any} - */ render() { if ( this.state.action.current === ConditionsProvider.actions.FETCH_CONFIG ) { if ( this.state.error ) { return <h3>{ __( 'Error:', 'elementor-pro' ) } { this.state.error }</h3>; } if ( this.state.loading ) { return <h3>{ __( 'Loading', 'elementor-pro' ) }...</h3>; } } return ( <Context.Provider value={ this.state }> { this.props.children } </Context.Provider> ); } } export default ConditionsProvider; app/modules/site-editor/assets/js/context/base-context.js 0000666 00000002021 15165257376 0017600 0 ustar 00 export class BaseContext extends React.Component { constructor( props ) { super( props ); this.state = { action: { current: null, loading: false, error: null, errorMeta: {}, }, updateActionState: this.updateActionState.bind( this ), resetActionState: this.resetActionState.bind( this ), }; } executeAction( name, handler ) { this.updateActionState( { current: name, loading: true, error: null, errorMeta: {} } ); return handler() .then( ( response ) => { this.resetActionState(); return Promise.resolve( response ); } ) .catch( ( error ) => { this.updateActionState( { current: name, loading: false, error: error.message, errorMeta: error } ); return Promise.reject( error ); } ); } updateActionState( data ) { return this.setState( ( prev ) => ( { action: { ...prev.action, ...data, }, } ) ); } resetActionState() { this.updateActionState( { current: null, loading: false, error: null, errorMeta: {} } ); } } export default BaseContext; app/modules/site-editor/assets/js/molecules/back-button.js 0000666 00000000711 15165257376 0017725 0 ustar 00 import { Button } from '@elementor/app-ui'; import './back-button.scss'; export default function BackButton( props ) { return ( <div className="back-button-wrapper"> <Button className="eps-back-button" text={ __( 'Back', 'elementor-pro' ) } icon="eicon-chevron-left" onClick={ props.onClick } /> </div> ); } BackButton.propTypes = { onClick: PropTypes.func, }; BackButton.defaultProps = { onClick: () => history.back(), }; app/modules/site-editor/assets/js/molecules/site-template-thumbnail.js 0000666 00000001436 15165257376 0022257 0 ustar 00 import { Button, CardImage, CardOverlay } from '@elementor/app-ui'; export default function SiteTemplateThumbnail( props ) { return ( <CardImage alt={ props.title } src={ props.thumbnail || props.placeholder } className={ ! props.thumbnail ? 'e-site-template__placeholder' : '' } > <CardOverlay className="e-site-template__overlay-preview"> <Button className="e-site-template__overlay-preview-button" text={ __( 'Preview', 'elementor-pro' ) } icon="eicon-preview-medium" url={ `/site-editor/templates/${ props.type }/${ props.id }` } /> </CardOverlay> </CardImage> ); } SiteTemplateThumbnail.propTypes = { id: PropTypes.number, title: PropTypes.string, type: PropTypes.string, thumbnail: PropTypes.string, placeholder: PropTypes.string, }; app/modules/site-editor/assets/js/molecules/site-template.scss 0000666 00000004301 15165257376 0020627 0 ustar 00 $eps-meta-icon-color: tints(400); $eps-meta-icon-dark-color: dark-tints(200); $preview-button-height: spacing(30); $image-aspect-ratio: var(--card-image-aspect-ratio, #{$ratio-portrait}); $overlay-preview-padding-top: calc(#{$image-aspect-ratio} - #{$preview-button-height}); $aspect-ratio-wide: calc(100% * 0.1235); :root { --eps-meta-icon-color: #{$eps-meta-icon-color}; } .eps-theme-dark { --eps-meta-icon-color: #{$eps-meta-icon-dark-color}; } .e-site-template { &__meta-data { margin-inline-start: spacing(10); @include text-truncate(); font-size: type(text, xxs); &:last-of-type { margin-inline-end: auto; } &:first-of-type { margin-inline-start: spacing(16); } .#{$eps-prefix}icon { margin-inline-end: spacing(5); color: var(--eps-meta-icon-color); font-size: type(text, sm); } } &__placeholder { .#{$eps-prefix}card__image { filter: var(--placeholder-filter, none); } } &__overlay-preview { padding-top: $overlay-preview-padding-top; position: relative; &-button { font-weight: bold; display: flex; flex-wrap: wrap; height: $preview-button-height; width: 100%; background-color: var(--card-background-color-hover); justify-content: center; align-items: center; padding-top: spacing(10); line-height: spacing(20); --button-background-color: var(--card-headline-color); &::before { content: ''; position: absolute; display: block; width: 100%; top: 0; left: 0; padding-top: $overlay-preview-padding-top; } & > :not(:first-child) { margin-inline-start: spacing(5); } } } &__edit-btn { margin-inline-end: spacing(20); .#{$eps-prefix}icon { margin-inline-end: spacing(5); } } &__instances { .#{$eps-prefix}icon { margin-inline-end: spacing(5); color: var(--eps-meta-icon-color); font-size: type(text, sm) } &-list { @include text-truncate(); } } &__edit-conditions { margin-inline-start: spacing(16); text-decoration: underline; font-style: italic; } &--extended { .#{$eps-prefix}card { &__figure { overflow: auto; } &__headline { flex-grow: 0; } } } &--wide { --card-image-aspect-ratio: #{$aspect-ratio-wide}; } } app/modules/site-editor/assets/js/molecules/site-template-body.js 0000666 00000001427 15165257376 0021231 0 ustar 00 import { CardBody } from '@elementor/app-ui'; import SiteTemplateThumbnail from './site-template-thumbnail'; import PreviewIFrame from '../atoms/preview-iframe'; export const SiteTemplateBody = ( props ) => { return ( <CardBody> { props.extended ? <PreviewIFrame src={ props.previewUrl } templateType={ props.type } /> : <SiteTemplateThumbnail id={ props.id } title={ props.title } type={ props.type } thumbnail={ props.thumbnail } placeholder={ props.placeholderUrl } /> } </CardBody> ); }; SiteTemplateBody.propTypes = { extended: PropTypes.bool, id: PropTypes.number, title: PropTypes.string, thumbnail: PropTypes.string, placeholderUrl: PropTypes.string, type: PropTypes.string, previewUrl: PropTypes.string, }; app/modules/site-editor/assets/js/molecules/site-template-header.js 0000666 00000003046 15165257376 0021523 0 ustar 00 import { Button, CardHeader, Heading, Icon, Text } from '@elementor/app-ui'; import PartActionsButtons from '../part-actions/dialogs-and-buttons'; import { Indicator } from '../atoms/indicator-bullet'; export const SiteTemplateHeader = ( props ) => { const status = props.status && 'publish' !== props.status ? ` (${ props.status })` : '', title = props.title + status, ActionButtons = () => ( <> <Button text={ __( 'Edit', 'elementor-pro' ) } icon="eicon-edit" className="e-site-template__edit-btn" size="sm" url={ props.editURL } /> <PartActionsButtons { ... props } /> </> ), MetaDataIcon = ( innerProps ) => ( <Text tag="span" className="e-site-template__meta-data"> <Icon className={ innerProps.icon } /> { innerProps.content } </Text> ), MetaData = () => ( <> <MetaDataIcon icon="eicon-user-circle-o" content={ props.author } /> <MetaDataIcon icon="eicon-clock-o" content={ props.modifiedDate } /> </> ), IndicatorDot = props.showInstances ? <Indicator active={ props.isActive } /> : ''; return ( <CardHeader> { IndicatorDot } <Heading tag="h1" title={ title } variant="text-sm" className="eps-card__headline">{ title }</Heading> { props.extended && <MetaData /> } { props.extended && <ActionButtons /> } </CardHeader> ); }; SiteTemplateHeader.propTypes = { isActive: PropTypes.bool, author: PropTypes.string, editURL: PropTypes.string, extended: PropTypes.bool, modifiedDate: PropTypes.string, status: PropTypes.string, title: PropTypes.string, showInstances: PropTypes.bool, }; app/modules/site-editor/assets/js/molecules/back-button.scss 0000666 00000000251 15165257376 0020263 0 ustar 00 .#{$eps-prefix}back-button { font-size: 14px; margin-bottom: spacing(24); .#{$eps-prefix}icon { transform: getValueByDirection(rotate(0deg), rotate(180deg)); } } app/modules/site-editor/assets/js/molecules/site-template.js 0000666 00000002723 15165257376 0020276 0 ustar 00 import { Card } from '@elementor/app-ui'; import { SiteTemplateHeader } from './site-template-header'; import { SiteTemplateBody } from './site-template-body'; import { SiteTemplateFooter } from './site-template-footer'; import './site-template.scss'; export default function SiteTemplate( props ) { const baseClassName = 'e-site-template', classes = [ baseClassName ], ref = React.useRef( null ); React.useEffect( () => { if ( ! props.isSelected ) { return; } ref.current.scrollIntoView( { behavior: 'smooth', block: 'start', } ); }, [ props.isSelected ] ); if ( props.extended ) { classes.push( `${ baseClassName }--extended` ); } if ( props.aspectRatio ) { classes.push( `${ baseClassName }--${ props.aspectRatio }` ); } const CardFooter = props.extended && props.showInstances ? <SiteTemplateFooter { ...props } /> : ''; return ( <Card className={ classes.join( ' ' ) } ref={ ref }> <SiteTemplateHeader { ... props } /> <SiteTemplateBody { ... props } /> { CardFooter } </Card> ); } SiteTemplate.propTypes = { aspectRatio: PropTypes.string, className: PropTypes.string, extended: PropTypes.bool, id: PropTypes.number.isRequired, isActive: PropTypes.bool.isRequired, status: PropTypes.string, thumbnail: PropTypes.string.isRequired, title: PropTypes.string.isRequired, isSelected: PropTypes.bool, type: PropTypes.string.isRequired, showInstances: PropTypes.bool, }; SiteTemplate.defaultProps = { isSelected: false, }; app/modules/site-editor/assets/js/molecules/site-template-footer.js 0000666 00000001416 15165257376 0021570 0 ustar 00 import { Button, CardFooter, Icon, Text } from '@elementor/app-ui'; export const SiteTemplateFooter = ( props ) => { const instances = Object.values( props.instances ).join( ', ' ); return ( <CardFooter> <div className="e-site-template__instances"> <Icon className="eicon-flow" /> <Text tag="span" variant="sm"><b>{ __( 'Instances', 'elementor-pro' ) }:</b></Text> <Text className="e-site-template__instances-list" tag="span" variant="xxs"> { instances }</Text> <Button text={ __( 'Edit Conditions', 'elementor-pro' ) } className="e-site-template__edit-conditions" url={ `/site-editor/conditions/${ props.id }` } /> </div> </CardFooter> ); }; SiteTemplateFooter.propTypes = { id: PropTypes.number.isRequired, instances: PropTypes.any, }; app/modules/site-editor/assets/js/pages/add-new.js 0000666 00000003216 15165257376 0016145 0 ustar 00 import { AddNewButton, Heading, Grid, CardOverlay } from '@elementor/app-ui'; import { SiteParts } from '@elementor/site-editor'; import './add-new.scss'; import { Context as TemplatesContext } from '../context/templates'; import BackButton from '../molecules/back-button'; import useFeatureLock from 'elementor-pro-app/hooks/use-feature-lock'; export default function AddNew() { const { templates } = React.useContext( TemplatesContext ), hasTemplates = 1 <= Object.keys( templates ).length; const { isLocked, ConnectButton } = useFeatureLock( 'site-editor' ); /** * An hover element for each site part. * * @param {any} props */ const HoverElement = ( props ) => { if ( isLocked ) { return ( <CardOverlay className="e-site-editor__promotion-overlay"> <div className="e-site-editor__promotion-overlay__link"> <i className="e-site-editor__promotion-overlay__icon eicon-lock" /> </div> </CardOverlay> ); } return ( <a href={ props.urls.create } className="eps-card__image-overlay eps-add-new__overlay"> <AddNewButton hideText={ true } /> </a> ); }; HoverElement.propTypes = { urls: PropTypes.object.isRequired, }; return ( <section className="e-site-editor__add-new"> <Grid container direction="column" className="e-site-editor__header"> { hasTemplates && <Grid item><BackButton /></Grid> } <Grid item container justify="space-between" alignItems="start"> <Heading variant="h1">{ __( 'Start customizing every part of your site', 'elementor-pro' ) }</Heading> { isLocked && <ConnectButton /> } </Grid> </Grid> <SiteParts hoverElement={ HoverElement } /> </section> ); } app/modules/site-editor/assets/js/pages/templates.js 0000666 00000001300 15165257376 0016614 0 ustar 00 import SiteTemplates from '../organisms/site-templates'; import { AddNewButton, Grid } from '@elementor/app-ui'; import useFeatureLock from 'elementor-pro-app/hooks/use-feature-lock'; export default function Templates() { const { isLocked, ConnectButton } = useFeatureLock( 'site-editor' ); return ( <section className="e-site-editor__site-templates"> <Grid container justify="space-between" alignItems="start" className="page-header"> <h1>{ __( 'Your Site\'s Global Parts', 'elementor-pro' ) }</h1> { isLocked ? <ConnectButton /> : <AddNewButton url="/site-editor/add-new" /> } </Grid> <hr className="eps-separator" /> <SiteTemplates /> </section> ); } app/modules/site-editor/assets/js/pages/import.js 0000666 00000005203 15165257376 0016136 0 ustar 00 import { DropZone, Dialog } from '@elementor/app-ui'; import { Context as TemplatesContext, TemplatesProvider } from '../context/templates'; import BackButton from '../molecules/back-button'; export default function Import() { const { importTemplates, action, resetActionState } = React.useContext( TemplatesContext ), [ importedTemplate, setImportedTemplate ] = React.useState( null ), isImport = React.useMemo( () => action.current === TemplatesProvider.actions.IMPORT, [ action ] ), isUploading = React.useMemo( () => isImport && action.loading, [ action ] ), hasError = React.useMemo( () => isImport && action.error, [ action ] ); const upload = React.useCallback( ( file ) => { if ( isUploading ) { return; } readFile( file ) .then( ( fileData ) => importTemplates( { fileName: file.name, fileData } ) ) .then( ( response ) => { // For now it show a dialog for the first template ONLY! setImportedTemplate( response.data[ 0 ] ); } ); }, [] ); return ( <section className="site-editor__import"> { importedTemplate && <Dialog title={ __( 'Your template was imported', 'elementor-pro' ) } approveButtonText={ __( 'Preview', 'elementor-pro' ) } approveButtonUrl={ importedTemplate.url } approveButtonTarget="_blank" dismissButtonText={ __( 'Edit', 'elementor-pro' ) } dismissButtonUrl={ importedTemplate.editURL } dismissButtonTarget="_top" onClose={ () => setImportedTemplate( null ) } /> } { hasError && <Dialog title={ action.error } approveButtonText={ __( 'Learn More', 'elementor-pro' ) } approveButtonUrl="https://go.elementor.com/app-theme-builder-import-issue" approveButtonTarget="_blank" approveButtonColor="link" dismissButtonText={ __( 'Go Back', 'elementor-pro' ) } dismissButtonOnClick={ resetActionState } onClose={ resetActionState } /> } <BackButton /> <DropZone heading={ __( 'Import Template To Your Library', 'elementor-pro' ) } text={ __( 'Drag & Drop your .JSON or .zip template file', 'elementor-pro' ) } secondaryText={ __( 'or', 'elementor-pro' ) } onFileSelect={ upload } isLoading={ isUploading } filetypes={ [ 'zip', 'json' ] } /> </section> ); } function readFile( file ) { return new Promise( ( ( resolve ) => { const fileReader = new FileReader(); fileReader.readAsDataURL( file ); fileReader.onload = ( event ) => { // Replace the mime type that prepended to the base64 with empty string and return a // resolved promise only with the base64 string. resolve( event.target.result.replace( /^[^,]+,/, '' ) ); }; } ) ); } app/modules/site-editor/assets/js/pages/template-type.scss 0000666 00000000251 15165257376 0017753 0 ustar 00 .e-site-editor__templates { .page-header { margin-bottom: spacing(10); > a { align-self: baseline; } } .eps-separator { margin-bottom: spacing(44); } } app/modules/site-editor/assets/js/pages/template-type.js 0000666 00000002330 15165257376 0017414 0 ustar 00 import { TemplateTypesContext } from '@elementor/site-editor'; import { AddNewButton, Grid, Heading, NotFound } from '@elementor/app-ui'; import SiteTemplates from '../organisms/site-templates'; import useFeatureLock from 'elementor-pro-app/hooks/use-feature-lock'; import './template-type.scss'; export default function TemplateType( props ) { const { templateTypes } = React.useContext( TemplateTypesContext ), currentType = templateTypes.find( ( item ) => item.type === props.type ), { isLocked, ConnectButton } = useFeatureLock( 'site-editor' ); if ( ! currentType ) { return <NotFound />; } return ( <section className={ `e-site-editor__templates e-site-editor__templates--type-${ props.type }` }> <Grid className="page-header" container justify="space-between"> <Heading variant="h1">{ currentType.page_title }</Heading> { isLocked ? <ConnectButton /> : <AddNewButton url={ currentType.urls.create } text={ __( 'Add New', 'elementor-pro' ) } /> } </Grid> <hr className="eps-separator" /> <SiteTemplates type={ currentType.type } id={ props.id } /> </section> ); } TemplateType.propTypes = { type: PropTypes.string, page_title: PropTypes.string, id: PropTypes.string, }; app/modules/site-editor/assets/js/pages/add-new.scss 0000666 00000000234 15165257376 0016501 0 ustar 00 .eps-add-new__overlay { display: flex; align-items: center; justify-content: center; opacity: 1; --card-image-overlay-background-color: transparent; } app/modules/site-editor/assets/js/pages/conditions/conditions-api.scss 0000666 00000013361 15165257376 0022260 0 ustar 00 $e-site-editor-conditions-header-image-button-spacing: spacing(44); $e-site-editor-conditions-header-image-width: px-to-rem(70); $e-site-editor-conditions-rows-max-width: px-to-rem(700); $e-site-editor-conditions-rows-y-spacing: spacing(44); $e-site-editor-conditions-row-top-spacing: spacing(12); $e-site-editor-conditions-remove-condition-color: tints(400); $e-site-editor-conditions-remove-condition-font-size: type(size, '18'); $e-site-editor-conditions-row-controls-spacing-end: spacing(10); $e-site-editor-conditions-row-controls-background: theme-colors(light); $e-site-editor-conditions-row-controls-dark-background: dark-tints(600); $e-site-editor-conditions-row-controls-radius: $eps-radius; $e-site-editor-conditions-row-controls-border: $eps-border-width $eps-border-style tints(100); $e-site-editor-conditions-row-controls-dark-border: $eps-border-width $eps-border-style tints(700); //$e-site-editor-conditions-row-controls-dark-border: $eps-border-width $eps-border-style tints(725); //merge after 3.12 is out $e-site-editor-conditions-row-controls-error-border: $eps-border-width $eps-border-style theme-colors(danger); $e-site-editor-conditions-conflict-top-spacing: spacing(5); $e-site-editor-conditions-conflict-color: theme-colors(danger); $e-site-editor-add-button-margin-top: spacing(44); $e-site-editor-add-button-background-color: tints(500); $e-site-editor-add-button-background-dark-color: dark-tints(500); $e-site-editor-add-button-color: theme-colors(light); $e-site-editor-add-button-color-hover-background-color: tints(600); $e-site-editor-add-button-color-hover-dark-background-color: dark-tints(600); $e-site-editor-add-button-color-hover-color: theme-colors(light); $e-site-editor-save-button-container-spacing: spacing(8); $e-site-editor-input-wrapper-border-width: $eps-border-width; $e-site-editor-input-wrapper-border-style: $eps-border-style; $e-site-editor-input-wrapper-border-color: tints(100); $e-site-editor-input-wrapper-border-dark-color: dark-tints(700); //$e-site-editor-input-wrapper-border-dark-color: dark-tints(725); //merge after 3.12 is out $e-site-editor-input-wrapper-select-font-size: type(size, "12"); $e-site-editor-input-wrapper-select-height: px-to-rem(40); $e-site-editor-input-wrapper-select-y-padding: spacing(10); $e-site-editor-input-wrapper-select-color: tints(700); //$e-site-editor-input-wrapper-select-color: tints(725); //merge after 3.12 is out $e-site-editor-input-wrapper-select-dark-color: dark-tints(200); $e-site-editor-input-wrapper-select-arrow-font-size: type(size, "12"); $e-site-editor-input-wrapper-select-arrow-margin-end: spacing(10); $e-site-editor-input-wrapper-condition-type-icon-start-spacing: spacing(12); $e-site-editor-input-wrapper-condition-type-icon-font-size: type(size, "15"); $e-site-editor-input-wrapper-condition-type-icon-color: theme-colors(light); $e-site-editor-input-wrapper-condition-type-icon-z-index: z-index(dropdown); $e-site-editor-input-wrapper-condition-type-arrow-color: theme-colors(light); $e-site-editor-input-wrapper-condition-type-start-padding: px-to-rem(34); $e-site-editor-input-wrapper-condition-type-width: px-to-rem(120); $e-site-editor-input-wrapper-condition-type-font-size: type(size, '12'); $e-site-editor-input-wrapper-condition-type-color: theme-colors(light); $e-site-editor-input-wrapper-condition-include-background-color: tints(500); $e-site-editor-input-wrapper-condition-include-background-dark-color: dark-tints(600); $e-site-editor-input-wrapper-condition-exclude-background-color: tints(400); $e-site-editor-input-wrapper-condition-exclude-background-dark-color: dark-tints(600); $e-site-editor-input-select2-search-field-color: theme-elements-colors(text-base-color); $e-site-editor-input-select2-search-field-dark-color: theme-colors(light); :root { --e-site-editor-conditions-row-controls-background: #{$e-site-editor-conditions-row-controls-background}; --e-site-editor-input-wrapper-border-color: #{$e-site-editor-input-wrapper-border-color}; --e-site-editor-input-wrapper-select-color: #{$e-site-editor-input-wrapper-select-color}; --e-site-editor-conditions-row-controls-border: #{$e-site-editor-conditions-row-controls-border}; --e-site-editor-add-button-background-color: #{$e-site-editor-add-button-background-color}; --e-site-editor-add-button-color-hover-background-color: #{$e-site-editor-add-button-color-hover-background-color}; --e-site-editor-input-wrapper-condition-include-background-color: #{$e-site-editor-input-wrapper-condition-include-background-color}; --e-site-editor-input-wrapper-condition-exclude-background-color: #{$e-site-editor-input-wrapper-condition-exclude-background-color}; --e-site-editor-input-select2-search-field-color: #{$e-site-editor-input-select2-search-field-color} } .eps-theme-dark { --select2-selection-background-color: tints(600); --e-site-editor-conditions-row-controls-background: #{$e-site-editor-conditions-row-controls-dark-background}; --e-site-editor-input-wrapper-border-color: #{$e-site-editor-input-wrapper-border-dark-color}; --e-site-editor-input-wrapper-select-color: #{$e-site-editor-input-wrapper-select-dark-color}; --e-site-editor-conditions-row-controls-border: #{$e-site-editor-conditions-row-controls-dark-border}; --e-site-editor-add-button-background-color: #{$e-site-editor-add-button-background-dark-color}; --e-site-editor-add-button-color-hover-background-color: #{$e-site-editor-add-button-color-hover-dark-background-color}; --e-site-editor-input-wrapper-condition-include-background-color: #{$e-site-editor-input-wrapper-condition-include-background-dark-color}; --e-site-editor-input-wrapper-condition-exclude-background-color: #{$e-site-editor-input-wrapper-condition-exclude-background-dark-color}; --e-site-editor-input-select2-search-field-color: #{$e-site-editor-input-select2-search-field-dark-color} } app/modules/site-editor/assets/js/pages/conditions/condition-type.js 0000666 00000001666 15165257376 0021753 0 ustar 00 import { Select } from '@elementor/app-ui'; export default function ConditionType( props ) { const wrapperRef = React.createRef(); const options = [ { label: __( 'Include', 'elementor-pro' ), value: 'include', }, { label: __( 'Exclude', 'elementor-pro' ), value: 'exclude', }, ]; const onChange = ( e ) => { props.updateConditions( props.id, { type: e.target.value } ); }; React.useEffect( () => { wrapperRef.current.setAttribute( 'data-elementor-condition-type', props.type ); } ); return ( <div className="e-site-editor-conditions__input-wrapper e-site-editor-conditions__input-wrapper--condition-type" ref={ wrapperRef }> <Select options={ options } value={ props.type } onChange={ onChange } /> </div> ); } ConditionType.propTypes = { updateConditions: PropTypes.func.isRequired, id: PropTypes.string.isRequired, type: PropTypes.string.isRequired, }; ConditionType.defaultProps = { type: '', }; app/modules/site-editor/assets/js/pages/conditions/condition-name.js 0000666 00000001407 15165257376 0021703 0 ustar 00 import { Select } from '@elementor/app-ui'; export default function ConditionName( props ) { // Hide for template types that has another default, like single & archive. if ( 'general' !== props.default ) { return ''; } const onChange = ( e ) => props.updateConditions( props.id, { name: e.target.value, sub: '', subId: '' } ); return ( <div className="e-site-editor-conditions__input-wrapper"> <Select options={ props.options } value={ props.name } onChange={ onChange } /> </div> ); } ConditionName.propTypes = { updateConditions: PropTypes.func.isRequired, id: PropTypes.string.isRequired, name: PropTypes.string.isRequired, options: PropTypes.array.isRequired, default: PropTypes.string.isRequired, }; ConditionName.defaultProps = { name: '', }; app/modules/site-editor/assets/js/pages/conditions/condition-sub-id.js 0000666 00000003366 15165257376 0022154 0 ustar 00 import { Select2 } from '@elementor/app-ui'; /** * Main component. * * @param {any} props * @return {any} - * @class */ export default function ConditionSubId( props ) { const settings = React.useMemo( () => ( Object.keys( props.subIdAutocomplete ).length ? getSettings( props.subIdAutocomplete ) : null ), [ props.subIdAutocomplete ] ); if ( ! props.sub || ! settings ) { return ''; } const onChange = ( e ) => props.updateConditions( props.id, { subId: e.target.value } ); return ( <div className="e-site-editor-conditions__input-wrapper"> <Select2 onChange={ onChange } value={ props.subId } settings={ settings } options={ props.subIdOptions } /> </div> ); } /** * Get settings for the select2 base on the autocomplete settings, * that passes as a prop * * @param {any} autocomplete * @return {Object} - */ function getSettings( autocomplete ) { return { allowClear: false, placeholder: __( 'All', 'elementor-pro' ), dir: elementorCommon.config.isRTL ? 'rtl' : 'ltr', ajax: { transport( params, success, failure ) { return elementorCommon.ajax.addRequest( 'pro_panel_posts_control_filter_autocomplete', { data: { q: params.data.q, autocomplete, }, success, error: failure, } ); }, data( params ) { return { q: params.term, page: params.page, }; }, cache: true, }, escapeMarkup( markup ) { return markup; }, minimumInputLength: 1, }; } ConditionSubId.propTypes = { subIdAutocomplete: PropTypes.object, id: PropTypes.string.isRequired, sub: PropTypes.string, subId: PropTypes.string, updateConditions: PropTypes.func, subIdOptions: PropTypes.array, }; ConditionSubId.defaultProps = { subId: '', subIdOptions: [], }; app/modules/site-editor/assets/js/pages/conditions/conditions.js 0000666 00000003150 15165257376 0021145 0 ustar 00 import { Heading, Text } from '@elementor/app-ui'; import ConditionsProvider from '../../context/conditions'; import { Context as TemplatesContext } from '../../context/templates'; import ConditionsRows from './conditions-rows'; import './conditions.scss'; import BackButton from '../../molecules/back-button'; export default function Conditions( props ) { const { findTemplateItemInState, updateTemplateItemState } = React.useContext( TemplatesContext ), template = findTemplateItemInState( parseInt( props.id ) ); if ( ! template ) { return <div>{ __( 'Not Found', 'elementor-pro' ) }</div>; } return ( <section className="e-site-editor-conditions"> <BackButton /> <div className="e-site-editor-conditions__header"> <img className="e-site-editor-conditions__header-image" src={ `${ elementorAppProConfig.baseUrl }/modules/theme-builder/assets/images/conditions-tab.svg` } alt={ __( 'Import template', 'elementor-pro' ) } /> <Heading variant="h1" tag="h1"> { __( 'Where Do You Want to Display Your Template?', 'elementor-pro' ) } </Heading> <Text variant="p"> { __( 'Set the conditions that determine where your template is used throughout your site.', 'elementor-pro' ) } <br /> { __( 'For example, choose \'Entire Site\' to display the template across your site.', 'elementor-pro' ) } </Text> </div> <ConditionsProvider currentTemplate={ template } onConditionsSaved={ updateTemplateItemState }> <ConditionsRows onAfterSave={ () => history.back() } /> </ConditionsProvider> </section> ); } Conditions.propTypes = { id: PropTypes.string, }; app/modules/site-editor/assets/js/pages/conditions/condition-conflicts.js 0000666 00000001244 15165257376 0022746 0 ustar 00 import { Button, Text } from '@elementor/app-ui'; export default function ConditionConflicts( props ) { if ( ! props.conflicts.length ) { return ''; } const conflictLinks = props.conflicts.map( ( conflict ) => { return ( <Button key={ conflict.template_id } target="_blank" url={ conflict.edit_url } text={ conflict.template_title } /> ); } ); return ( <Text className="e-site-editor-conditions__conflict" variant="sm"> { __( 'Elementor recognized that you have set this location for other templates: ', 'elementor-pro' ) } { conflictLinks } </Text> ); } ConditionConflicts.propTypes = { conflicts: PropTypes.array.isRequired, }; app/modules/site-editor/assets/js/pages/conditions/conditions-rows.js 0000666 00000005544 15165257376 0022146 0 ustar 00 import { Context as ConditionsContext, ConditionsProvider } from '../../context/conditions'; import { Button, Dialog } from '@elementor/app-ui'; import ConditionType from './condition-type'; import ConditionName from './condition-name'; import ConditionSub from './condition-sub'; import ConditionSubId from './condition-sub-id'; import ConditionConflicts from './condition-conflicts'; export default function ConditionsRows( props ) { const { conditions, createConditionItemInState: create, updateConditionItemState: update, removeConditionItemInState: remove, saveConditions: save, action, resetActionState, } = React.useContext( ConditionsContext ); const rows = Object.values( conditions ).map( ( condition ) => <div key={ condition.id }> <div className="e-site-editor-conditions__row"> <div className={ `e-site-editor-conditions__row-controls ${ condition.conflictErrors.length && 'e-site-editor-conditions__row-controls--error' }` }> <ConditionType { ...condition } updateConditions={ update } /> <div className="e-site-editor-conditions__row-controls-inner"> <ConditionName { ...condition } updateConditions={ update } /> <ConditionSub { ...condition } updateConditions={ update } /> <ConditionSubId { ...condition } updateConditions={ update } /> </div> </div> <Button className="e-site-editor-conditions__remove-condition" text={ __( 'Delete', 'elementor-pro' ) } icon="eicon-close" hideText={ true } onClick={ () => remove( condition.id ) } /> </div> <ConditionConflicts conflicts={ condition.conflictErrors } /> </div>, ); const isSaving = action.current === ConditionsProvider.actions.SAVE && action.loading; return ( <> { action.error && <Dialog text={ action.error } dismissButtonText={ __( 'Go Back', 'elementor-pro' ) } dismissButtonOnClick={ resetActionState } approveButtonText={ __( 'Learn More', 'elementor-pro' ) } approveButtonColor="link" approveButtonUrl="https://go.elementor.com/app-theme-builder-conditions-load-issue" approveButtonTarget="_target" /> } <div className="e-site-editor-conditions__rows"> { rows } </div> <div className="e-site-editor-conditions__add-button-container"> <Button className="e-site-editor-conditions__add-button" variant="contained" size="lg" text={ __( 'Add Condition', 'elementor-pro' ) } onClick={ create } /> </div> <div className="e-site-editor-conditions__footer"> <Button variant="contained" color="primary" size="lg" hideText={ isSaving } icon={ isSaving ? 'eicon-loading eicon-animation-spin' : '' } text={ __( 'Save & Close', 'elementor-pro' ) } onClick={ () => save().then( props.onAfterSave ) } /> </div> </> ); } ConditionsRows.propTypes = { onAfterSave: PropTypes.func, }; app/modules/site-editor/assets/js/pages/conditions/conditions.scss 0000666 00000011436 15165257376 0021512 0 ustar 00 @import "conditions-api"; .e-site-editor-conditions { &__header { text-align: center; } &__header-image { display: block; margin: 0 auto $e-site-editor-conditions-header-image-button-spacing; width: $e-site-editor-conditions-header-image-width; } &__rows { margin: $e-site-editor-conditions-rows-y-spacing auto; max-width: $e-site-editor-conditions-rows-max-width; } &__row { display: flex; flex-grow: 1; margin-top: $e-site-editor-conditions-row-top-spacing; } &__remove-condition { color: $e-site-editor-conditions-remove-condition-color; font-size: $e-site-editor-conditions-remove-condition-font-size; display: flex; align-items: center; justify-content: center; } &__row-controls { overflow: hidden; @include margin-end($e-site-editor-conditions-row-controls-spacing-end); background-color: var(--e-site-editor-conditions-row-controls-background); display: flex; width: 100%; border: var(--e-site-editor-conditions-row-controls-border); border-radius: $e-site-editor-conditions-row-controls-radius; &--error { border: $e-site-editor-conditions-row-controls-error-border; } } &__conflict { text-align: center; margin-top: $e-site-editor-conditions-conflict-top-spacing; color: $e-site-editor-conditions-conflict-color; } &__row-controls-inner { width: 100%; display: flex; div { flex: 1; } } &__add-button-container { text-align: center; } &__add-button { margin-top: $e-site-editor-add-button-margin-top; background-color: var(--e-site-editor-add-button-background-color); color: $e-site-editor-add-button-color; text-transform: uppercase; &:hover { background-color: var(--e-site-editor-add-button-color-hover-background-color); color: $e-site-editor-add-button-color-hover-color; } } &__footer { display: flex; justify-content: flex-end; position: absolute; bottom: 0; right: 0; left: 0; padding: $e-site-editor-save-button-container-spacing; border-top: 1px solid var(--hr-color); } &__input-wrapper { position: relative; @include border-start($e-site-editor-input-wrapper-border-width $e-site-editor-input-wrapper-border-style); border-color: var(--e-site-editor-input-wrapper-border-color); &:first-child { border: none; } select { appearance: none; -webkit-appearance: none; font-size: $e-site-editor-input-wrapper-select-font-size; height: $e-site-editor-input-wrapper-select-height; border-width: 0; padding: 0 $e-site-editor-input-wrapper-select-y-padding; width: 100%; position: relative; color: var(--e-site-editor-input-wrapper-select-color); outline: none; background: transparent; } &:after { font-family: eicons; content: '\e8ad'; font-size: $e-site-editor-input-wrapper-select-arrow-font-size; pointer-events: none; position: absolute; top: 50%; transform: translateY(-50%); @include end($e-site-editor-input-wrapper-select-arrow-margin-end); } .select2-container--default .select2-selection--single { border: none; line-height: $e-site-editor-input-wrapper-select-height; } .select2-container--default .select2-selection--single .select2-selection__rendered { line-height: $e-site-editor-input-wrapper-select-height; font-size: $e-site-editor-input-wrapper-select-font-size; } .select2-selection { outline: none; background: transparent; height: $e-site-editor-input-wrapper-select-height; } .select2-selection__arrow { display: none; } } &__input-wrapper--condition-type { position: relative; &:before { font-family: eicons; position: absolute; top: 50%; transform: translateY(-50%); @include start($e-site-editor-input-wrapper-condition-type-icon-start-spacing); font-size: $e-site-editor-input-wrapper-condition-type-icon-font-size; //color: $e-site-editor-input-wrapper-condition-type-icon-color; pointer-events: none; z-index: $e-site-editor-input-wrapper-condition-type-icon-z-index; } select { text-transform: uppercase; @include padding-start($e-site-editor-input-wrapper-condition-type-start-padding); width: $e-site-editor-input-wrapper-condition-type-width; font-size: $e-site-editor-input-wrapper-condition-type-font-size; @include border-end($e-site-editor-input-wrapper-border-width $e-site-editor-input-wrapper-border-style); border-color: var(--e-site-editor-input-wrapper-border-color); } &[data-elementor-condition-type="include"] { &:before { content: '\e8cc'; } } &[data-elementor-condition-type="exclude"] { &:before { content: '\e8cd'; } } } } // This is a temporary fix that handles dark mode in select2. // TODO: Remove it if already handled by the Select2 component. .select2-search__field { background-color: transparent; color: var(--e-site-editor-input-select2-search-field-color); } app/modules/site-editor/assets/js/pages/conditions/condition-sub.js 0000666 00000001330 15165257376 0021547 0 ustar 00 import { Select } from '@elementor/app-ui'; export default function ConditionSub( props ) { if ( 'general' === props.name || ! props.subOptions.length ) { return ''; } const onChange = ( e ) => props.updateConditions( props.id, { sub: e.target.value, subId: '' } ); return ( <div className="e-site-editor-conditions__input-wrapper"> <Select options={ props.subOptions } value={ props.sub } onChange={ onChange } /> </div> ); } ConditionSub.propTypes = { updateConditions: PropTypes.func.isRequired, id: PropTypes.string.isRequired, name: PropTypes.string.isRequired, sub: PropTypes.string.isRequired, subOptions: PropTypes.array.isRequired, }; ConditionSub.defaultProps = { sub: '', subOptions: {}, }; app/modules/site-editor/assets/js/organisms/site-templates.js 0000666 00000005454 15165257376 0020477 0 ustar 00 import { CssGrid, Dialog } from '@elementor/app-ui'; import SiteTemplate from '../molecules/site-template'; import { PartActionsDialogs } from '../part-actions/dialogs-and-buttons'; import { Context as TemplatesContext } from '../context/templates'; import useTemplatesScreenshot from '../hooks/use-templates-screenshot'; export default function SiteTemplates( props ) { const { templates: contextTemplates, action, resetActionState } = React.useContext( TemplatesContext ); let gridColumns, templates; // Make the templates object a memorize value, will re run again only if // templates has been changed, also sort the templates by `isActive`. templates = React.useMemo( () => { return Object.values( contextTemplates ) .sort( ( a, b ) => { // This sort make sure to show first the active templates, second the // inactive templates that are not draft, and then the drafts, // in each category it sorts it inside by date. if ( ! b.isActive && ! a.isActive ) { if ( ( 'draft' === b.status && 'draft' === a.status ) || ( 'draft' !== b.status && 'draft' !== a.status ) ) { return b.date < a.date ? 1 : -1; } return 'draft' === a.status ? 1 : -1; } if ( b.isActive && a.isActive ) { return b.date < a.date ? 1 : -1; } return b.isActive ? 1 : -1; } ); }, [ contextTemplates ] ); // Start to capture screenshots. useTemplatesScreenshot( props.type ); const siteTemplateConfig = {}; if ( props.type ) { templates = templates.filter( ( item ) => item.type === props.type ); siteTemplateConfig.extended = true; siteTemplateConfig.type = props.type; switch ( props.type ) { case 'header': case 'footer': gridColumns = 1; siteTemplateConfig.aspectRatio = 'wide'; break; default: gridColumns = 2; } } if ( ! templates || ! templates.length ) { return <h3>{ __( 'No Templates found. Want to create one?', 'elementor-pro' ) }...</h3>; } return ( <section className="e-site-editor__site-templates"> <PartActionsDialogs /> { action.error && <Dialog text={ action.error } dismissButtonText={ __( 'Go Back', 'elementor-pro' ) } dismissButtonOnClick={ resetActionState } approveButtonText={ __( 'Learn More', 'elementor-pro' ) } approveButtonColor="link" approveButtonUrl="https://go.elementor.com/app-theme-builder-template-load-issue" approveButtonTarget="_target" /> } <CssGrid columns={ gridColumns } spacing={ 24 } colMinWidth={ 200 }> { templates.map( ( item ) => <SiteTemplate key={ item.id } { ... item } { ... siteTemplateConfig } isSelected={ parseInt( props.id ) === item.id } />, ) } </CssGrid> </section> ); } SiteTemplates.propTypes = { type: PropTypes.string, id: PropTypes.string, }; app/modules/site-editor/assets/js/data/component.js 0000666 00000000422 15165257376 0016436 0 ustar 00 import * as dataCommands from './commands'; export default class Component extends $e.modules.ComponentBase { static namespace = 'site-editor'; getNamespace() { return this.constructor.namespace; } defaultData() { return this.importCommands( dataCommands ); } } app/modules/site-editor/assets/js/data/commands/templates-conditions.js 0000666 00000000365 15165257376 0022410 0 ustar 00 export class TemplatesConditions extends $e.modules.CommandData { static signature = 'site-editor/templates-conditions'; static getEndpointFormat() { return 'site-editor/templates-conditions/{id}'; } } export default TemplatesConditions; app/modules/site-editor/assets/js/data/commands/templates.js 0000666 00000000313 15165257376 0020232 0 ustar 00 export class Templates extends $e.modules.CommandData { static signature = 'site-editor/templates'; static getEndpointFormat() { return 'site-editor/templates/{id}'; } } export default Templates; app/modules/site-editor/assets/js/data/commands/templates-conditions-conflicts.js 0000666 00000000434 15165257376 0024367 0 ustar 00 export class TemplatesConditionsConflicts extends $e.modules.CommandData { static signature = 'site-editor/templates-conditions-conflicts'; static getEndpointFormat() { return `${ TemplatesConditionsConflicts.signature }/{id}`; } } export default TemplatesConditionsConflicts; app/modules/site-editor/assets/js/data/commands/conditions-config.js 0000666 00000000351 15165257376 0021652 0 ustar 00 export class ConditionsConfig extends $e.modules.CommandData { static signature = 'site-editor/conditions-config'; static getEndpointFormat() { return 'site-editor/conditions-config/{id}'; } } export default ConditionsConfig; app/modules/site-editor/assets/js/data/commands/index.js 0000666 00000000360 15165257376 0017345 0 ustar 00 export { Templates } from './templates'; export { ConditionsConfig } from './conditions-config'; export { TemplatesConditions } from './templates-conditions'; export { TemplatesConditionsConflicts } from './templates-conditions-conflicts'; app/modules/site-editor/assets/js/hooks/use-templates-screenshot.js 0000666 00000003575 15165257376 0021625 0 ustar 00 import { Context as TemplatesContext } from '../context/templates'; import useScreenshot, { SCREENSHOT_STATUS_SUCCEED, SCREENSHOT_STATUS_FAILED } from 'modules/screenshots/app/assets/js/hooks/use-screenshot'; /** * Wrapper function that was made to take screenshots specific for template. * it will capture a screenshot and update the templates context with the new screenshot. * * @param {any} templateType */ export default function useTemplatesScreenshot( templateType = null ) { const { updateTemplateItemState, templates } = React.useContext( TemplatesContext ); const templatesForScreenshot = Object.values( templates ).filter( ( template ) => shouldScreenshotTemplate( template, templateType ), ); // Start to capture screenshots. const screenshot = useScreenshot( templatesForScreenshot ); // Update the thumbnail url when screenshot created. React.useEffect( () => { screenshot.posts .filter( ( post ) => post.status === SCREENSHOT_STATUS_SUCCEED ) .forEach( ( post ) => updateTemplateItemState( post.id, { thumbnail: post.imageUrl } ) ); }, [ screenshot.succeed ] ); // Update the screenshot url that was failed. // When the user will hit the route on the second time it will avoid trying to take another screenshot. React.useEffect( () => { screenshot.posts .filter( ( post ) => post.status === SCREENSHOT_STATUS_FAILED ) .forEach( ( post ) => updateTemplateItemState( post.id, { screenshot_url: null } ) ); }, [ screenshot.failed ] ); return screenshot; } /** * Filter handler. * will remove all the drafts and private and also will filter by template type if exists. * * @param {any} template * @param {any} templateType * @return {boolean} - */ function shouldScreenshotTemplate( template, templateType = null ) { if ( templateType ) { return false; } return 'publish' === template.status && ! template.thumbnail && template.screenshot_url; } app/modules/site-editor/data/endpoints/base-endpoint.php 0000666 00000000726 15165257376 0017433 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\SiteEditor\Data\Endpoints; use Elementor\Data\Base\Endpoint; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } abstract class Base_Endpoint extends Endpoint { /** * Check if post is lock. * * @param $post_id * * @return bool|false|int */ protected function is_post_lock( $post_id ) { require_once ABSPATH . 'wp-admin/includes/post.php'; return wp_check_post_lock( $post_id ); } } app/modules/site-editor/data/endpoints/template-types.php 0000666 00000001105 15165257376 0017650 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\SiteEditor\Data\Endpoints; use ElementorPro\Plugin; use ElementorPro\Core\App\Modules\SiteEditor\Module; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Template_Types extends Base_Endpoint { /** * @return string */ public function get_name() { return 'template-types'; } public function get_items( $request ) { /** @var Module $site_editor_module */ $site_editor_module = Plugin::instance()->app->get_component( 'site-editor' ); return $site_editor_module->get_template_types(); } } app/modules/site-editor/data/endpoints/templates.php 0000666 00000015223 15165257376 0016677 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\SiteEditor\Data\Endpoints; use ElementorPro\Plugin; use Elementor\Core\Utils\Exceptions; use Elementor\TemplateLibrary\Manager as TemplateManager; use ElementorPro\Modules\ThemeBuilder\Documents\Theme_Document; use ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Manager; use ElementorPro\Modules\ThemeBuilder\Module as ThemeBuilderModule; use ElementorPro\Modules\ThemeBuilder\Classes\Templates_Types_Manager; use ElementorPro\Core\App\Modules\SiteEditor\Render_Mode_Template_Preview; use ElementorPro\Core\App\Modules\SiteEditor\Data\Responses\Lock_Error_Response; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Templates extends Base_Endpoint { /** * @var TemplateManager */ private $templates_manager; /** * @var array */ private $document_types; public function __construct( $controller ) { parent::__construct( $controller ); $this->templates_manager = Plugin::elementor()->templates_manager; } /** * @return string */ public function get_name() { return 'templates'; } protected function register() { parent::register(); $this->register_item_route( \WP_REST_Server::DELETABLE ); $this->register_item_route( \WP_REST_Server::EDITABLE ); $this->register_items_route( \WP_REST_Server::CREATABLE ); } public function get_items( $request ) { $templates = $this->templates_manager->get_source( 'local' )->get_items( [ 'type' => array_keys( $this->get_documents_types() ), 'post_status' => 'any', 'orderby' => 'post_date', 'order' => 'DESC', ] ); return $this->normalize_templates_json( $templates ); } public function create_items( $request ) { $response = $this->templates_manager->import_template( $request->get_body_params() ); if ( is_wp_error( $response ) ) { return new \WP_Error( 'file', $response->get_error_message(), [ 'status' => Exceptions::BAD_REQUEST ] ); } return $this->normalize_templates_json( $response ); } public function update_item( $id, $request ) { $lock_by_user_id = $this->is_post_lock( $id ); if ( $lock_by_user_id ) { return new Lock_Error_Response( $lock_by_user_id ); } wp_update_post( array_merge( [ 'ID' => $id, ], $request->get_body_params() ) ); return $this->normalize_template_json_item( $this->templates_manager->get_source( 'local' )->get_item( $id ) ); } public function delete_item( $id, $request ) { $lock_by_user_id = $this->is_post_lock( $id ); if ( $lock_by_user_id ) { return new Lock_Error_Response( $lock_by_user_id ); } return ! ! wp_trash_post( $id ); } /** * @return array */ private function get_documents_types() { if ( ! $this->document_types ) { /** @var Templates_Types_Manager $types_manager */ $types_manager = ThemeBuilderModule::instance()->get_types_manager(); $this->document_types = $types_manager->get_types_config( [ 'support_site_editor' => true, ] ); } return $this->document_types; } /** * @param $templates * * @return array */ private function normalize_templates_json( $templates ) { return array_map( [ $this, 'normalize_template_json_item' ], $templates ); } /** * @param $template * * @return array */ private function normalize_template_json_item( $template ) { /** @var Conditions_Manager $conditions_manager */ $conditions_manager = Plugin::instance()->modules_manager->get_modules( 'theme-builder' )->get_conditions_manager(); /** @var Theme_Document $document */ $document = Plugin::elementor()->documents->get( $template['template_id'] ); $supports_site_editor = $document::get_property( 'support_site_editor' ); // Supports also a non site editor parts. if ( ! $supports_site_editor ) { return [ 'id' => $template['template_id'], 'url' => $template['url'], 'editURL' => $document->get_edit_url(), 'supportsSiteEditor' => false, ]; } $types = $this->get_documents_types(); $template['instances'] = $conditions_manager->get_document_instances( $template['template_id'] ); $template['defaultCondition'] = $types[ $template['type'] ]['condition_type']; $has_instances = ! empty( $template['instances'] ); $is_active = false; if ( ! $has_instances ) { $template['instances'] = [ 'no_instances' => esc_html__( 'No instances', 'elementor-pro' ) ]; } else { $is_active = 'publish' === $template['status']; } if ( ! $template['thumbnail'] ) { $template['thumbnail'] = ''; } $site_editor_config = $document->get_site_editor_config(); $data = array_merge( $template, [ 'id' => $template['template_id'], 'exportLink' => $template['export_link'], 'modifiedDate' => $template['human_modified_date'], 'editURL' => $document->get_edit_url(), 'conditions' => array_map( function ( $condition ) { return array_merge( $condition, [ 'sub' => $condition['sub_name'], 'subId' => $condition['sub_id'], ] ); }, $conditions_manager->get_document_conditions( $document ) ), 'isActive' => $is_active, 'type' => $this->calculate_template_type( $template['type'], $template['instances'] ), 'previewUrl' => $this->get_preview_url( $template['template_id'] ), 'placeholderUrl' => $site_editor_config['urls']['thumbnail'], 'pageLayout' => $site_editor_config['page_layout'], 'supportsSiteEditor' => true, 'showInstances' => $site_editor_config['show_instances'], ] ); /** * Template data. * * Filters the data returned by Elementor API as JSON. * * By default Elementor API returns data in a JSON format that enables the * builder to work properly. This hook allows developers to alter the data * returned by the API to add new elements. * * @param array $data Template data. */ $data = apply_filters( 'elementor-pro/site-editor/data/template', $data ); return $data; } /** * @param $type * @param $instances * * @return string */ private function calculate_template_type( $type, $instances ) { $condition_to_type_map = [ 'front_page' => 'single-page', 'child_of' => 'single-page', 'page' => 'single-page', 'not_found404' => 'error-404', 'search' => 'search-results', ]; // "single" type was split into "single-page", "single-post" and "404". // this section supports all the old templates that was created as "single". if ( 'single' === $type ) { // By default show it under single-post. $type = 'single-post'; foreach ( $instances as $condition_name => $condition_label ) { if ( isset( $condition_to_type_map[ $condition_name ] ) ) { $type = $condition_to_type_map[ $condition_name ]; break; } } } return $type; } private function get_preview_url( $post_id ) { return Render_Mode_Template_Preview::get_url( $post_id ); } } app/modules/site-editor/data/endpoints/conditions-config.php 0000666 00000001034 15165257376 0020310 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\SiteEditor\Data\Endpoints; use ElementorPro\Modules\ThemeBuilder\Module as ThemeBuilderModule; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Conditions_Config extends Base_Endpoint { /** * @return string */ public function get_name() { return 'conditions-config'; } public function get_items( $request ) { $conditions_manager = ThemeBuilderModule::instance()->get_conditions_manager(); return $conditions_manager->get_conditions_config(); } } app/modules/site-editor/data/endpoints/templates-conditions.php 0000666 00000004207 15165257376 0021046 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\SiteEditor\Data\Endpoints; use ElementorPro\Plugin; use Elementor\Core\Utils\Exceptions; use ElementorPro\Modules\ThemeBuilder\Module; use ElementorPro\Core\App\Modules\SiteEditor\Data\Responses\Lock_Error_Response; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Templates_Conditions extends Base_Endpoint { /** * @return string */ public function get_name() { return 'templates-conditions'; } protected function register() { $this->register_item_route(); $this->register_item_route( \WP_REST_Server::EDITABLE ); } public function get_item( $template_id, $request ) { return $this->get_conditions( $template_id ); } public function update_item( $template_id, $request ) { $lock_by_user_id = $this->is_post_lock( $template_id ); if ( $lock_by_user_id ) { return new Lock_Error_Response( $lock_by_user_id ); } $data = $request->get_body_params(); if ( ! isset( $data['conditions'] ) ) { $data['conditions'] = []; } $is_saved = $this->save_conditions( $template_id, $data['conditions'] ); if ( ! $is_saved ) { return new \WP_Error( 'conditions', __( 'Error while saving conditions.', 'elementor-pro' ), [ 'status' => Exceptions::INTERNAL_SERVER_ERROR ] ); } return true; } protected function get_conditions( $post_id ) { $document = \Elementor\Plugin::$instance->documents->get( $post_id ); /** @var Module $theme_builder */ $theme_builder = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); return $theme_builder ->get_conditions_manager() ->get_document_conditions( $document ); } protected function save_conditions( $post_id, $conditions ) { /** @var Module $theme_builder */ $theme_builder = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); $is_saved = $theme_builder ->get_conditions_manager() ->save_conditions( $post_id, $conditions ); if ( ! $is_saved ) { return new \WP_Error( 'conditions_save', __( 'Cannot save those conditions.', 'elementor-pro' ), [ 'status' => Exceptions::INTERNAL_SERVER_ERROR ] ); } return true; } } app/modules/site-editor/data/endpoints/templates-conditions-conflicts.php 0000666 00000001271 15165257376 0023026 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\SiteEditor\Data\Endpoints; use ElementorPro\Plugin; use ElementorPro\Modules\ThemeBuilder\Module; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Templates_Conditions_Conflicts extends Base_Endpoint { /** * @return string */ public function get_name() { return 'templates-conditions-conflicts'; } public function get_items( $request ) { /** @var Module $theme_builder */ $theme_builder = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); return $theme_builder ->get_conditions_manager() ->get_conditions_conflicts( intval( $request['post_id'] ), $request['condition'] ); } } app/modules/site-editor/data/controller.php 0000666 00000001423 15165257376 0015056 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\SiteEditor\Data; use ElementorPro\Plugin; use Elementor\Data\Base\Controller as Controller_Base; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Controller extends Controller_Base { public function get_name() { return 'site-editor'; } public function register_endpoints() { $this->register_endpoint( Endpoints\Templates::class ); $this->register_endpoint( Endpoints\Conditions_Config::class ); $this->register_endpoint( Endpoints\Templates_Conditions::class ); $this->register_endpoint( Endpoints\Templates_Conditions_Conflicts::class ); } public function get_permission_callback( $request ) { return Plugin::elementor()->kits_manager->get_active_kit()->is_editable_by_current_user(); } } app/modules/site-editor/data/responses/lock-error-response.php 0000666 00000001170 15165257376 0020626 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\SiteEditor\Data\Responses; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Lock_Error_Response extends \WP_Error { public function __construct( $user_id ) { $user = get_user_by( 'ID', $user_id ); parent::__construct( 'post_lock', sprintf( /* translators: %s: User display name. */ esc_html__( '%s is currently editing this template, please try again later', 'elementor-pro' ), $user->display_name ), [ 'status' => 403, 'locked_by_user_id' => $user_id, 'locked_by_user_name' => $user->display_name, ] ); } } app/modules/site-editor/module.php 0000666 00000015076 15165257376 0013260 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\SiteEditor; use Elementor\Core\Admin\Menu\Admin_Menu_Manager; use Elementor\Core\Experiments\Manager as ExperimentsManager; use Elementor\Core\Frontend\Render_Mode_Manager; use Elementor\Core\Base\Module as BaseModule; use Elementor\Core\Common\Modules\Ajax\Module as Ajax; use Elementor\TemplateLibrary\Source_Local; use ElementorPro\Core\App\Modules\SiteEditor\Data\Controller; use ElementorPro\Core\Behaviors\Feature_Lock; use ElementorPro\Modules\ThemeBuilder\AdminMenuItems\Theme_Builder_Menu_Item; use ElementorPro\Modules\ThemeBuilder\Module as Theme_Builder_Table_View; use ElementorPro\Modules\ThemeBuilder\Module as ThemeBuilderModule; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } /** * Site Editor Module * * Responsible for initializing Elementor Pro App functionality */ class Module extends BaseModule { /** * @var Feature_Lock */ private $lock; /** * Get name. * * @access public * * @return string */ public function get_name() { return 'site-editor'; } /** * @throws \Exception */ public function get_template_types() { // Same as admin menu capabilities. if ( ! current_user_can( 'publish_posts' ) ) { throw new \Exception( 'Access denied' ); } $document_types = Plugin::elementor()->documents->get_document_types( [ 'support_site_editor' => true, ] ); // Keep 404 at end of array. $error_404 = $document_types['error-404']; unset( $document_types['error-404'] ); $document_types['error-404'] = $error_404; // Currently the `single` itself is not supported in site editor. // Don't use `support_site_editor=false` in order to support documents that extend it. unset( $document_types['single'] ); $types = []; foreach ( $document_types as $type => $class ) { $types[] = $class::get_site_editor_config(); } return $types; } /** * Register ajax actions. * * @access public * * @param Ajax $ajax */ public function register_ajax_actions( Ajax $ajax ) { $ajax->register_ajax_action( 'app_site_editor_template_types', [ $this, 'get_template_types' ] ); } /** * @param Render_Mode_Manager $manager * * @throws \Exception */ public function register_render_mode( Render_Mode_Manager $manager ) { $manager->register_render_mode( Render_Mode_Template_Preview::class ); } protected function get_init_settings() { $settings = [ 'urls' => [ 'legacy_view' => add_query_arg( 'tabs_group', ThemeBuilderModule::ADMIN_LIBRARY_TAB_GROUP, admin_url( Source_Local::ADMIN_MENU_SLUG ) ), ], 'utms' => [ 'utm_source' => 'theme-builder', 'utm_medium' => 'wp-dash', ], ]; if ( $this->lock->is_locked() ) { $settings['lock'] = $this->lock->get_config(); } return $settings; } private function add_default_new_site_editor_experiments( ExperimentsManager $manager ) { $manager->add_feature( [ 'name' => 'theme_builder_v2', 'title' => __( 'Default to New Theme Builder', 'elementor-pro' ), 'description' => __( 'Entering the Theme Builder through WP Dashboard > Templates > Theme Builder opens the New theme builder by default. But don’t worry, you can always view the WP styled version of the screen with a simple click of a button.', 'elementor-pro' ), 'release_status' => ExperimentsManager::RELEASE_STATUS_STABLE, 'default' => ExperimentsManager::STATE_ACTIVE, ] ); } /** * Get site editor url. * * @return string */ private function get_site_editor_url() : string { return Plugin::elementor()->app->get_base_url() . '#/site-editor'; } private function register_site_editor_menu() { $experiments_manager = Plugin::elementor()->experiments; // Unique case when the experiments manager is not initialized yet. if ( ! $experiments_manager || ! $experiments_manager->is_feature_active( 'theme_builder_v2' ) ) { return; } // Remove the old theme builder link and add the new one. remove_submenu_page( Source_Local::ADMIN_MENU_SLUG, add_query_arg( 'tabs_group', ThemeBuilderModule::ADMIN_LIBRARY_TAB_GROUP, Source_Local::ADMIN_MENU_SLUG ) ); add_submenu_page( Source_Local::ADMIN_MENU_SLUG, '', __( 'Theme Builder', 'elementor-pro' ), 'publish_posts', $this->get_site_editor_url() ); } private function register_admin_menu( Admin_Menu_Manager $admin_menu_manager ) { $experiments_manager = Plugin::elementor()->experiments; // Unique case when the experiments manager is not initialized yet. if ( ! $experiments_manager || ! $experiments_manager->is_feature_active( 'theme_builder_v2' ) ) { return; } $admin_menu_manager->unregister( add_query_arg( 'tabs_group', ThemeBuilderModule::ADMIN_LIBRARY_TAB_GROUP, Source_Local::ADMIN_MENU_SLUG ) ); $admin_menu_manager->register( $this->get_site_editor_url(), new Theme_Builder_Menu_Item() ); } private function add_finder_item( array $categories ) { if ( ! Plugin::elementor()->experiments->is_feature_active( 'theme_builder_v2' ) ) { return $categories; } // Replace the old theme builder "create-new" link with the new site-editor. $categories['create']['items']['theme-template'] = [ 'title' => __( 'Add New Theme Template', 'elementor-pro' ), 'icon' => 'plus-circle-o', 'url' => $this->get_site_editor_url() . '/add-new', 'keywords' => [ 'template', 'theme', 'new', 'create' ], ]; return $categories; } /** * Module constructor. * * @access public */ public function __construct() { $this->lock = new Feature_Lock( [ 'type' => 'theme-builder' ] ); Plugin::elementor()->data_manager->register_controller( Controller::class ); add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ], 11 /* Override core actions */ ); add_action( 'elementor/frontend/render_mode/register', [ $this, 'register_render_mode' ] ); add_action( 'elementor/experiments/default-features-registered', function ( ExperimentsManager $manager ) { $this->add_default_new_site_editor_experiments( $manager ); } ); add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu ) { $this->register_admin_menu( $admin_menu ); }, Theme_Builder_Table_View::ADMIN_MENU_PRIORITY + 1 ); // TODO: BC - Remove after `Admin_Menu_Manager` will be the standard. add_action( 'admin_menu', function () { if ( did_action( 'elementor/admin/menu/register' ) ) { return; } $this->register_site_editor_menu(); }, 23 /* After old theme builder */ ); add_filter( 'elementor/finder/categories', function ( array $categories ) { return $this->add_finder_item( $categories ); }, 11 /* After old theme builder */ ); } } app/modules/site-editor/render-mode-template-preview.php 0000666 00000002077 15165257376 0017461 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\SiteEditor; use Elementor\Core\Frontend\RenderModes\Render_Mode_Base; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Render_Mode_Template_Preview extends Render_Mode_Base { /** * @return string */ public static function get_name() { return 'template-preview'; } public function filter_template() { return ELEMENTOR_PATH . 'modules/page-templates/templates/canvas.php'; } public function prepare_render() { parent::prepare_render(); show_admin_bar( false ); remove_filter( 'the_content', [ \ElementorPro\Modules\ThemeBuilder\Module::instance()->get_locations_manager(), 'builder_wrapper' ], 9999999 ); add_filter( 'template_include', [ $this, 'filter_template' ] ); add_action( 'wp_head', [ $this, 'render_pointer_event_style' ] ); } /** * disable all the interactions in the preview render mode. */ public function render_pointer_event_style() { echo '<style> html { pointer-events: none; } </style>'; } public function is_static() { return true; } } app/modules/import-export/runners/export/templates.php 0000666 00000002733 15165257376 0017403 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\ImportExport\Runners\Export; use Elementor\App\Modules\ImportExport\Runners\Export\Export_Runner_Base; use Elementor\Core\Base\Document; use Elementor\Plugin; use Elementor\TemplateLibrary\Source_Local; class Templates extends Export_Runner_Base { public static function get_name() : string { return 'templates'; } public function should_export( array $data ) { return ( isset( $data['include'] ) && in_array( 'templates', $data['include'], true ) ); } public function export( array $data ) { $template_types = array_values( Source_Local::get_template_types() ); $query_args = [ 'post_type' => Source_Local::CPT, 'post_status' => 'publish', 'posts_per_page' => -1, 'meta_query' => [ [ 'key' => Document::TYPE_META_KEY, 'value' => $template_types, ], ], ]; $templates_query = new \WP_Query( $query_args ); $templates_manifest_data = []; $files = []; foreach ( $templates_query->posts as $template_post ) { $template_id = $template_post->ID; $template_document = Plugin::$instance->documents->get( $template_id ); $templates_manifest_data[ $template_id ] = $template_document->get_export_summary(); $files[] = [ 'path' => 'templates/' . $template_id, 'data' => $template_document->get_export_data(), ]; } $manifest_data['templates'] = $templates_manifest_data; return [ 'files' => $files, 'manifest' => [ $manifest_data, ], ]; } } app/modules/import-export/runners/import/templates.php 0000666 00000007445 15165257376 0017401 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\ImportExport\Runners\Import; use Elementor\App\Modules\ImportExport\Runners\Import\Import_Runner_Base; use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils; use Elementor\Core\Base\Document; use Elementor\Plugin; use ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Manager; use ElementorPro\Modules\ThemeBuilder\Module as ThemeBuilderModule; use ElementorPro\Plugin as ProPlugin; use Elementor\TemplateLibrary\Source_Local; class Templates extends Import_Runner_Base { private $import_session_id; private $templates_conditions = []; public static function get_name() : string { return 'templates'; } public function should_import( array $data ) { return ( isset( $data['include'] ) && in_array( 'templates', $data['include'], true ) && ! empty( $data['extracted_directory_path'] ) && ! empty( $data['manifest']['templates'] ) ); } public function import( array $data, array $imported_data ) { $this->import_session_id = $data['session_id']; $path = $data['extracted_directory_path'] . 'templates/'; $templates = $data['manifest']['templates']; $result['templates'] = [ 'succeed' => [], 'failed' => [], ]; foreach ( $templates as $id => $template_settings ) { try { $template_data = ImportExportUtils::read_json_file( $path . $id ); $import = $this->import_template( $id, $template_settings, $template_data ); $result['templates']['succeed'][ $id ] = $import; } catch ( \Exception $error ) { $result['templates']['failed'][ $id ] = $error->getMessage(); } } return $result; } private function import_template( $id, array $template_settings, array $template_data ) { $doc_type = $template_settings['doc_type']; $new_document = Plugin::$instance->documents->create( $doc_type, [ 'post_title' => $template_settings['title'], 'post_type' => Source_Local::CPT, 'post_status' => 'publish', ] ); if ( is_wp_error( $new_document ) ) { throw new \Exception( $new_document->get_error_message() ); } $template_data['import_settings'] = $template_settings; $template_data['id'] = $id; $this->set_templates_conditions( $template_data ); $new_attachment_callback = function( $attachment_id ) { $this->set_session_post_meta( $attachment_id, $this->import_session_id ); }; add_filter( 'elementor/template_library/import_images/new_attachment', $new_attachment_callback ); $new_document->import( $template_data ); remove_filter( 'elementor/template_library/import_images/new_attachment', $new_attachment_callback ); $document_id = $new_document->get_main_id(); $this->set_session_post_meta( $document_id, $this->import_session_id ); return $document_id; } public function get_import_session_metadata() : array { return [ 'template_conditions' => $this->templates_conditions, ]; } private function set_templates_conditions( $template_data ) { $conditions = $template_data['import_settings']['conditions']; if ( empty( $conditions ) ) { return; } $condition = $conditions[0]; $condition = rtrim( implode( '/', $condition ), '/' ); /** @var ThemeBuilderModule $theme_builder_module */ $theme_builder_module = ProPlugin::instance()->modules_manager->get_modules( 'theme-builder' ); $conditions_manager = $theme_builder_module->get_conditions_manager(); $conflicts = $conditions_manager->get_conditions_conflicts_by_location( $condition, $template_data['import_settings']['location'] ); foreach ( $conflicts as $template ) { $template_document = Plugin::$instance->documents->get( $template['template_id'] ); $template_conditions = $theme_builder_module->get_conditions_manager()->get_document_conditions( $template_document ); $this->templates_conditions[ $template['template_id'] ] = $template_conditions; } } } app/modules/import-export/runners/revert/templates.php 0000666 00000003403 15165257376 0017364 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\ImportExport\Runners\Revert; use Elementor\App\Modules\ImportExport\Runners\Revert\Revert_Runner_Base; use Elementor\Core\Base\Document; use Elementor\Plugin; use ElementorPro\Modules\ThemeBuilder\Module as ThemeBuilderModule; use ElementorPro\Plugin as ProPlugin; use Elementor\TemplateLibrary\Source_Local; class Templates extends Revert_Runner_Base { public static function get_name() : string { return 'templates'; } public function should_revert( array $data ) : bool { return ( isset( $data['runners'] ) && array_key_exists( static::get_name(), $data['runners'] ) ); } public function revert( array $data ) { $template_types = array_values( Source_Local::get_template_types() ); $query_args = [ 'post_type' => Source_Local::CPT, 'post_status' => 'any', 'posts_per_page' => -1, 'meta_query' => [ [ 'key' => Document::TYPE_META_KEY, 'value' => $template_types, ], [ 'key' => static::META_KEY_ELEMENTOR_IMPORT_SESSION_ID, 'value' => $data['session_id'], ], ], ]; $templates_query = new \WP_Query( $query_args ); foreach ( $templates_query->posts as $template_post ) { $template_document = Plugin::$instance->documents->get( $template_post->ID ); $template_document->delete(); } /** @var ThemeBuilderModule $theme_builder_module */ $theme_builder_module = ProPlugin::instance()->modules_manager->get_modules( 'theme-builder' ); $theme_builder_module->get_conditions_manager()->clear_cache(); $old_conditions = $data['runners']['templates']['template_conditions'] ?? []; foreach ( $old_conditions as $template_id => $conditions ) { $theme_builder_module->get_conditions_manager()->save_conditions( $template_id, $conditions ); } } } app/modules/import-export/module.php 0000666 00000004350 15165257376 0013652 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\ImportExport; use Elementor\Core\Base\Module as BaseModule; use ElementorPro\Plugin; use ElementorPro\Modules\ThemeBuilder\Module as ThemeBuilderModule; use Elementor\App\Modules\ImportExport\Processes\Export; use Elementor\App\Modules\ImportExport\Processes\Import; use Elementor\App\Modules\ImportExport\Processes\Revert; use ElementorPro\Core\App\Modules\ImportExport\Runners\Import\Templates as ImportTemplates; use ElementorPro\Core\App\Modules\ImportExport\Runners\Export\Templates as ExportTemplates; use ElementorPro\Core\App\Modules\ImportExport\Runners\Revert\Templates as RevertTemplates; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Module extends BaseModule { public function get_name() { return 'import-export'; } public function __construct() { parent::__construct(); $this->add_actions(); } private function add_actions() { add_filter( 'elementor/import/get_default_settings_conflicts', function( array $conflicts, array $templates ) { return $this->apply_conditions_conflicts( $conflicts, $templates ); }, 10, 2 ); add_action( 'elementor/import-export/import-kit', function( Import $import ) { $this->register_import_kit_runners( $import ); } ); add_action( 'elementor/import-export/export-kit', function( Export $export ) { $this->register_export_kit_runners( $export ); } ); add_action( 'elementor/import-export/revert-kit', function( Revert $revert ) { $this->register_revert_kit_runners( $revert ); } ); } private function apply_conditions_conflicts( $conflicts, $templates ) { /** @var ThemeBuilderModule $theme_builder_module */ $theme_builder_module = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); if ( ! $theme_builder_module ) { return $conflicts; } return $conflicts + $theme_builder_module->get_conditions_conflicts( $templates ); } private function register_import_kit_runners( Import $import ) { $import->register( new ImportTemplates() ); } private function register_export_kit_runners( Export $export ) { $export->register( new ExportTemplates() ); } private function register_revert_kit_runners( Revert $revert ) { $revert->register( new RevertTemplates() ); } } app/modules/kit-library/module.php 0000666 00000004640 15165257376 0013254 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\KitLibrary; use ElementorPro\Plugin; use ElementorPro\License\API; use ElementorPro\License\Admin; use Elementor\Core\Base\Module as BaseModule; use ElementorPro\Core\Connect\Apps\Activate; use Elementor\Core\App\Modules\KitLibrary\Connect\Kit_Library; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Module extends BaseModule { /** * Get name. * * @access public * * @return string */ public function get_name() { return 'kit-library'; } private function set_kit_library_settings() { $common = Plugin::elementor()->common; $app = Plugin::elementor()->app; $prev_settings = $app->get_settings( 'kit-library' ); // BC Support. if ( ! $prev_settings || ! $common ) { return; } /** @var Activate $activate */ $activate = $common->get_component( 'connect' )->get_app( 'activate' ); /** @var Kit_Library $kit_library */ $kit_library = $common->get_component( 'connect' )->get_app( 'kit-library' ); $app->set_settings( 'kit-library', array_merge( $prev_settings, [ 'is_pro' => true, 'is_library_connected' => API::is_license_active() && $kit_library && $kit_library->is_connected(), 'library_connect_url' => $activate->get_admin_url( 'authorize', [ 'utm_source' => 'kit-library', 'utm_medium' => 'wp-dash', 'utm_campaign' => 'connect-and-activate-license', 'utm_term' => '%%page%%', // Will be replaced in the frontend. ] ), 'access_level' => API::get_library_access_level( 'kit' ), ] ) ); } /** * @param array $connect_info * @param $app * * @return array */ private function add_license_to_connect_info( array $connect_info, $app ) { $license_key = Admin::get_license_key(); // In elementor 3.3.0-beta it does not send the $app parameter and it should add the license. $bc_support = ! $app; $is_kit_library_request = $app && Kit_Library::class === get_class( $app ); if ( ! empty( $license_key ) && ( $bc_support || $is_kit_library_request ) ) { $connect_info['license'] = $license_key; } return $connect_info; } public function __construct() { add_action( 'elementor/init', function () { $this->set_kit_library_settings(); }, 13 /** after elementor core */ ); add_filter( 'elementor/connect/additional-connect-info', function ( array $connect_info, $app = null ) { return $this->add_license_to_connect_info( $connect_info, $app ); }, 10, 2 ); } } app/modules/onboarding/module.php 0000666 00000003605 15165257376 0013145 0 ustar 00 <?php namespace ElementorPro\Core\App\Modules\Onboarding; use Elementor\Core\App\Modules\Onboarding\Module as Core_Onboarding_Module; use ElementorPro\Plugin; use Elementor\Core\Base\Module as BaseModule; use ElementorPro\Core\Connect\Apps\Activate; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Module extends BaseModule { /** * Get name * * @since 3.6.0 * @access public * * @return string */ public function get_name() { return 'onboarding'; } /** * Set Onboarding Settings * * Overrides the Onboarding App's Core settings with updated settings to accommodate for Elementor Pro. * * @since 3.6.0 * @access private */ private function set_onboarding_settings() { $common = Plugin::elementor()->common; $app = Plugin::elementor()->app; $onboarding_settings = $app->get_settings( 'onboarding' ); // If the installed Elementor Core version does not include the Onboarding module, exit here. if ( ! $onboarding_settings ) { return; } /** @var Activate $activate */ $activate = $common->get_component( 'connect' )->get_app( 'activate' ); $onboarding_settings['urls']['connect'] = $activate->get_admin_url( 'authorize', [ 'utm_source' => 'editor-app', 'utm_campaign' => 'connect-account', 'utm_medium' => 'wp-dash', 'utm_term' => Core_Onboarding_Module::VERSION, 'source' => 'generic', ] ); $onboarding_settings['urls']['signUp'] = $activate->get_admin_url( 'authorize', [ 'utm_source' => 'editor-app', 'utm_campaign' => 'connect-account', 'utm_medium' => 'wp-dash', 'utm_term' => Core_Onboarding_Module::VERSION, 'source' => 'generic', 'screen_hint' => 'signup', ] ); $app->set_settings( 'onboarding', $onboarding_settings ); } public function __construct() { add_action( 'elementor/init', function () { $this->set_onboarding_settings(); }, 13 /** after elementor core */ ); } } modules-manager.php 0000666 00000004216 15165257376 0010365 0 ustar 00 <?php namespace ElementorPro\Core; use ElementorPro\Plugin; use ElementorPro\Base\Module_Base; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } final class Modules_Manager { /** * @var Module_Base[] */ private $modules = []; public function __construct() { $modules = [ 'query-control', 'custom-attributes', 'custom-css', 'page-transitions', // role-manager Must be before Global Widget 'role-manager', 'global-widget', 'assets-manager', 'popup', 'motion-fx', 'usage', 'screenshots', 'compatibility-tag', 'admin-top-bar', 'notes', // Modules with Widgets. 'theme-builder', 'loop-builder', 'posts', 'gallery', 'forms', 'slides', 'nav-menu', 'animated-headline', 'hotspot', 'pricing', 'flip-box', 'call-to-action', 'carousel', 'table-of-contents', 'countdown', 'share-buttons', 'theme-elements', 'blockquote', 'custom-code', 'woocommerce', 'social', 'library', 'dynamic-tags', 'scroll-snap', 'sticky', 'wp-cli', 'lottie', 'code-highlight', 'video-playlist', 'payments', 'progress-tracker', 'mega-menu', 'nested-carousel', ]; foreach ( $modules as $module_name ) { $class_name = str_replace( '-', ' ', $module_name ); $class_name = str_replace( ' ', '', ucwords( $class_name ) ); $class_name = '\ElementorPro\Modules\\' . $class_name . '\Module'; /** @var Module_Base $class_name */ $experimental_data = $class_name::get_experimental_data(); if ( $experimental_data ) { Plugin::elementor()->experiments->add_feature( $experimental_data ); if ( ! Plugin::elementor()->experiments->is_feature_active( $experimental_data['name'] ) ) { continue; } } if ( $class_name::is_active() ) { $this->modules[ $module_name ] = $class_name::instance(); } } } /** * @param string $module_name * * @return Module_Base|Module_Base[] */ public function get_modules( $module_name ) { if ( $module_name ) { if ( isset( $this->modules[ $module_name ] ) ) { return $this->modules[ $module_name ]; } return null; } return $this->modules; } } compatibility/compatibility.php 0000666 00000000771 15165257376 0013031 0 ustar 00 <?php namespace ElementorPro\Core\Compatibility; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Compatibility { public static function register_actions() { add_action( 'init', [ __CLASS__, 'on_init' ] ); } public static function on_init() { static::translate_press(); } private static function translate_press() { if ( ! class_exists( 'TRP_Translate_Press' ) ) { return; } add_filter( 'elementor_pro/license/api/use_home_url', '__return_false' ); } } connect/manager.php 0000666 00000001113 15165257376 0010341 0 ustar 00 <?php namespace ElementorPro\Core\Connect; use ElementorPro\Core\Connect\Apps\Activate; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Manager { /** * @param \Elementor\Core\Common\Modules\Connect\Module $apps_manager */ public function register_apps( $apps_manager ) { $apps = [ 'activate' => Activate::get_class_name(), ]; foreach ( $apps as $slug => $class ) { $apps_manager->register_app( $slug, $class ); } } public function __construct() { add_action( 'elementor/connect/apps/register', [ $this, 'register_apps' ] ); } } connect/apps/activate.php 0000666 00000007275 15165257376 0011511 0 ustar 00 <?php namespace ElementorPro\Core\Connect\Apps; use Elementor\Core\Common\Modules\Connect\Apps\Common_App; use ElementorPro\License; use ElementorPro\License\API; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Activate extends Common_App { public function get_title() { return esc_html__( 'Activate', 'elementor-pro' ); } public function get_slug() { return 'activate'; } protected function after_connect() { $this->action_activate_license(); } /** * @since 2.3.0 * @access public */ public function action_authorize() { // In case the first connect was not from Activate App - require a new authorization. if ( $this->is_connected() && ! License\Admin::get_license_key() ) { $this->disconnect(); } parent::action_authorize(); } public function action_activate_pro() { $this->action_activate_license(); } public function action_switch_license() { $this->disconnect(); $this->action_authorize(); } public function action_deactivate() { License\Admin::deactivate(); $this->disconnect(); wp_safe_redirect( License\Admin::get_url() ); die; } public function action_activate_license() { if ( ! $this->is_connected() ) { $this->add_notice( esc_html__( 'Please connect to Elementor in order to activate license.', 'elementor-pro' ), 'error' ); $this->redirect_to_admin_page(); } $license = $this->request( 'get_connected_license' ); if ( empty( $license ) ) { // TODO: add suggestions how to check/resolve. wp_die( 'License not found for user ' . esc_attr( $this->get( 'user' )->email ), esc_html__( 'Elementor Pro', 'elementor-pro' ), [ 'back_link' => true, ] ); } if ( is_wp_error( $license ) ) { wp_die( $license, esc_html__( 'Elementor Pro', 'elementor-pro' ), [ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 'back_link' => true, ] ); } $license_key = trim( $license->key ); if ( empty( $license_key ) ) { wp_die( esc_html__( 'License key is missing.', 'elementor-pro' ), esc_html__( 'Elementor Pro', 'elementor-pro' ), [ 'back_link' => true, ] ); } $data = License\API::activate_license( $license_key ); if ( is_wp_error( $data ) ) { wp_die( sprintf( '%s (%s) ', $data->get_error_message(), $data->get_error_code() ), esc_html__( 'Elementor Pro', 'elementor-pro' ), [ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 'back_link' => true, ] ); } if ( empty( $data['success'] ) ) { $error_msg = License\API::get_error_message( $data['error'] ); // get_error_message() escapes html wp_die( $error_msg, esc_html__( 'Elementor Pro', 'elementor-pro' ), [ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 'back_link' => true, ] ); } License\Admin::set_license_key( $license_key ); License\API::set_license_data( $data ); $this->add_notice( esc_html__( 'License has been activated successfully.', 'elementor-pro' ) ); $this->redirect_to_admin_page( License\Admin::get_url() ); die; } public function action_reset() { if ( current_user_can( 'manage_options' ) ) { delete_option( 'elementor_pro_license_key' ); delete_transient( 'elementor_pro_license_data' ); } $this->redirect_to_admin_page(); } protected function get_popup_success_event_data() { return [ 'templates_access_level' => API::get_library_access_level( 'template' ), 'kits_access_level' => API::get_library_access_level( 'kit' ), ]; } protected function get_app_info() { return [ 'license_data' => [ 'label' => 'License Data', 'value' => get_option( '_elementor_pro_license_data' ), ], 'license_key' => [ 'label' => 'License Key', 'value' => get_option( 'elementor_pro_license_key' ), ], ]; } } editor/editor.php 0000666 00000007036 15165257376 0010064 0 ustar 00 <?php namespace ElementorPro\Core\Editor; use Elementor\Core\Base\App; use ElementorPro\License\Admin as License_Admin; use ElementorPro\License\API as License_API; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Editor extends App { /** * Get app name. * * Retrieve the app name. * * @return string app name. * @since 2.6.0 * @access public * */ public function get_name() { return 'pro-editor'; } public function __construct() { add_action( 'elementor/init', [ $this, 'on_elementor_init' ] ); add_action( 'elementor/editor/init', [ $this, 'on_elementor_editor_init' ] ); add_action( 'elementor/editor/after_enqueue_styles', [ $this, 'enqueue_editor_styles' ] ); add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'enqueue_editor_scripts' ] ); add_filter( 'elementor/editor/localize_settings', [ $this, 'localize_settings' ] ); // Loading elementor packages. $loader = ELEMENTOR_PRO_ASSETS_PATH . 'js/packages/loader.php'; if ( file_exists( $loader ) ) { require_once $loader; } } public function get_init_settings() { $settings = [ 'isActive' => License_API::is_license_active(), 'urls' => [ 'modules' => ELEMENTOR_PRO_MODULES_URL, 'connect' => License_Admin::get_url(), ], ]; /** * Localized editor settings. * * Filters the localized settings used in the editor as JavaScript variables. * * By default Elementor Pro passes some editor settings to be consumed as JavaScript * variables. This hook allows developers to add extra settings values to be consumed * using JavaScript in the editor. * * @since 1.0.0 * * @param array $settings Localized editor settings. */ $settings = apply_filters( 'elementor_pro/editor/localize_settings', $settings ); return $settings; } public function enqueue_editor_styles() { wp_enqueue_style( 'elementor-pro', $this->get_css_assets_url( 'editor', null, 'default', true ), [ 'elementor-editor', ], ELEMENTOR_PRO_VERSION ); } public function enqueue_editor_scripts() { wp_enqueue_script( 'elementor-pro', $this->get_js_assets_url( 'editor' ), [ 'backbone-marionette', 'elementor-common', 'elementor-editor-modules', 'elementor-editor-document', ], ELEMENTOR_PRO_VERSION, true ); wp_set_script_translations( 'elementor-pro', 'elementor-pro' ); $this->print_config( 'elementor-pro' ); } public function localize_settings( array $settings ) { $settings['elementPromotionURL'] = Plugin::instance()->license_admin->get_connect_url([ 'utm_source' => '%s', // Will be replaced in the frontend to the widget name 'utm_medium' => 'wp-dash', 'utm_campaign' => 'connect-and-activate-license', 'utm_content' => 'editor-widget-promotion', ]); $settings['dynamicPromotionURL'] = Plugin::instance()->license_admin->get_connect_url( [ 'utm_source' => '%s', // Will be replaced in the frontend to the control name 'utm_medium' => 'wp-dash', 'utm_campaign' => 'connect-and-activate-license', 'utm_content' => 'editor-dynamic-promotion', ] ); return $settings; } public function on_elementor_init() { Plugin::elementor()->editor->notice_bar = new Notice_Bar(); if ( isset( Plugin::elementor()->editor->promotion ) ) { Plugin::elementor()->editor->promotion = new Promotion(); } } public function on_elementor_editor_init() { Plugin::elementor()->common->add_template( __DIR__ . '/template.php' ); } protected function get_assets_base_url() { return ELEMENTOR_PRO_URL; } } editor/promotion.php 0000666 00000002547 15165257376 0010626 0 ustar 00 <?php namespace ElementorPro\Core\Editor; use ElementorPro\License\API; use ElementorPro\License\Admin; use Elementor\Core\Editor\Promotion as Base_Promotion; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Promotion extends Base_Promotion { public function get_elements_promotion() { if ( API::is_license_active() ) { return parent::get_elements_promotion(); } $is_license_expired = API::is_license_expired(); return [ /* translators: %s: Widget title. */ 'title' => __( '%s Widget', 'elementor-pro' ), 'content' => $is_license_expired /* translators: %s: Widget title. */ ? __( 'Renew your Elementor Pro subscription to get %s and dozens more Pro widgets to expand your web-creation toolbox.', 'elementor-pro' ) /* translators: %s: Widget title. */ : __( 'Use %s widget and dozens more pro features to extend your toolbox and build sites faster and better.', 'elementor-pro' ), 'action_button' => $is_license_expired ? [ 'text' => __( 'Renew now', 'elementor-pro' ), 'url' => 'https://my.elementor.com/subscriptions/?utm_source=%s-pro-widget&utm_medium=wp-dash&utm_campaign=renew-license', 'classes' => [ 'elementor-button', 'elementor-button-brand' ], ] : [ 'text' => __( 'Connect & Activate', 'elementor-pro' ), 'url' => Admin::get_url(), ], ]; } } editor/notice-bar.php 0000666 00000011407 15165257376 0010616 0 ustar 00 <?php namespace ElementorPro\Core\Editor; use Elementor\Core\Editor\Notice_Bar as Base_Notice_Bar; use ElementorPro\License\Admin; use ElementorPro\License\API as License_API; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Notice_Bar extends Base_Notice_Bar { const ELEMENTOR_PRO_EDITOR_GO_PRO_TRIAL_ABOUT_TO_EXPIRE_LICENSE_NOTICE_DISMISSED = '_elementor_pro_editor_go_pro_trial_about_to_expire_license_notice_dismissed'; const ELEMENTOR_PRO_EDITOR_GO_PRO_TRIAL_EXPIRED_LICENSE_NOTICE_DISMISSED = '_elementor_pro_editor_go_pro_trial_expired_license_notice_dismissed'; const ELEMENTOR_PRO_EDITOR_RENEW_LICENSE_NOTICE_DISMISSED = '_elementor_pro_editor_renew_license_notice_dismissed'; const ELEMENTOR_PRO_EDITOR_ACTIVATE_LICENSE_NOTICE_DISMISSED = '_elementor_pro_editor_activate_license_notice_dismissed'; const ELEMENTOR_PRO_EDITOR_RENEW_ABOUT_TO_EXPIRE_LICENSE_NOTICE_DISMISSED = '_elementor_pro_editor_renew_about_to_expire_license_notice_dismissed'; protected function get_init_settings() { $license_data = License_API::get_license_data(); $license_admin = Plugin::instance()->license_admin; if ( License_API::is_license_active() && License_API::is_licence_pro_trial() ) { return [ 'option_key' => self::ELEMENTOR_PRO_EDITOR_GO_PRO_TRIAL_ABOUT_TO_EXPIRE_LICENSE_NOTICE_DISMISSED, 'message' => esc_html__( 'Heads up! You are using a free trial. Want to enjoy Pro widgets & templates for a whole year?', 'elementor-pro' ) . sprintf( ' <a href="https://my.elementor.com/upgrade-subscription/?utm_source=editor-notice-bar&utm_medium=wp-dash&utm_campaign=pro-trial&utm_content=trial-period" target="_blank">%s</a>', esc_html__( 'Go Pro now', 'elementor-pro' ) ), 'action_title' => '', 'action_url' => '', 'muted_period' => 0, ]; } if ( License_API::is_license_expired() && License_API::is_licence_pro_trial() ) { return [ 'option_key' => self::ELEMENTOR_PRO_EDITOR_GO_PRO_TRIAL_EXPIRED_LICENSE_NOTICE_DISMISSED, 'message' => esc_html__( 'Your trial has expired. Miss your favorite Elementor Pro features?', 'elementor-pro' ) . sprintf( ' <a href="https://my.elementor.com/upgrade-subscription/?utm_source=editor-notice-bar&utm_medium=wp-dash&utm_campaign=pro-trial&utm_content=trial-expired" target="_blank">%s</a>', esc_html__( 'Upgrade now', 'elementor-pro' ) ), 'action_title' => '', 'action_url' => '', 'muted_period' => 0, ]; } if ( License_API::is_license_expired() ) { return [ 'option_key' => self::ELEMENTOR_PRO_EDITOR_RENEW_LICENSE_NOTICE_DISMISSED, 'icon' => 'eicon-lock', 'message' => esc_html__( 'Renew to unlock all Elementor Pro features', 'elementor-pro' ), 'action_title' => esc_html__( 'Renew now', 'elementor-pro' ), 'action_url' => 'https://go.elementor.com/editor-notice-bar-renew/', 'secondary_message' => esc_html__( 'Already renewed?', 'elementor-pro' ), 'secondary_action_title' => esc_html__( 'Reload Editor', 'elementor-pro' ), 'secondary_action_url' => Admin::get_url() . '&redirect-to-document=' . Plugin::elementor()->documents->get_current()->get_id(), 'secondary_action_target' => '_self', 'muted_period' => 0, ]; } if ( ! License_API::is_license_active() ) { return [ 'option_key' => self::ELEMENTOR_PRO_EDITOR_ACTIVATE_LICENSE_NOTICE_DISMISSED, 'message' => esc_html__( 'Activate Your License and Get Access to Premium Elementor Templates, Support & Plugin Updates.', 'elementor-pro' ), 'action_title' => esc_html__( 'Connect & Activate', 'elementor-pro' ), 'action_url' => $license_admin->get_connect_url( [ 'mode' => 'popup', 'callback_id' => 'editor-pro-activate', // UTM 'utm_source' => 'editor-notice-bar', 'utm_medium' => 'wp-dash', 'utm_campaign' => 'connect-and-activate-license', ] ), 'muted_period' => 0, ]; } if ( ! License_API::is_license_about_to_expire() ) { return []; } if ( isset( $license_data['renewal_discount'] ) && 0 < $license_data['renewal_discount'] ) { $message = sprintf( /* translators: %s: Renewal discount. */ esc_html__( 'Your Elementor Pro license is about to expire. Renew now and get an exclusive, time-limited %s discount.', 'elementor-pro' ), $license_data['renewal_discount'] . '%' ); } else { $message = esc_html__( 'Your Elementor Pro license is about to expire. Renew now and get updates, support, Pro widgets & templates for another year.', 'elementor-pro' ); } return [ 'option_key' => self::ELEMENTOR_PRO_EDITOR_RENEW_ABOUT_TO_EXPIRE_LICENSE_NOTICE_DISMISSED, 'message' => $message, 'action_title' => esc_html__( 'Renew now', 'elementor-pro' ), 'action_url' => 'https://go.elementor.com/editor-notice-bar-renew/', 'muted_period' => 1, ]; } } editor/template.php 0000666 00000001226 15165257376 0010404 0 ustar 00 <?php use ElementorPro\License\Admin as LicenseAdmin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } ?> <script type="text/template" id="tmpl-elementor-pro-template-library-activate-license-button"> <a class="elementor-template-library-template-action elementor-button go-pro" href="<?php // PHPCS - the function LicenseAdmin::get_url() is safe. echo LicenseAdmin::get_url(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" target="_blank"> <i class="eicon-external-link-square"></i> <span class="elementor-button-title"><?php echo esc_html__( 'Activate License', 'elementor-pro' ); ?></span> </a> </script> preview/preview.php 0000666 00000001147 15165257376 0010447 0 ustar 00 <?php namespace ElementorPro\Core\Preview; use Elementor\Core\Base\App; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Preview extends App { public function __construct() { add_action( 'elementor/preview/enqueue_styles', [ $this, 'enqueue_styles' ] ); } public function get_name() { return 'pro-preview'; } public function enqueue_styles() { wp_enqueue_style( 'pro-editor-preview', $this->get_css_assets_url( 'preview', null, 'default', true ), [], ELEMENTOR_PRO_VERSION ); } protected function get_assets_base_url() { return ELEMENTOR_PRO_URL; } } notifications/notifications-manager.php 0000666 00000001063 15165257376 0014434 0 ustar 00 <?php namespace ElementorPro\Core\Notifications; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Notifications_Manager { /** * Send a notification. * * @param \ElementorPro\Core\Notifications\Notification $notification * @param $notifiable * * @throws \Exception * * @return $this */ public function send( Notification $notification, $notifiable ) { $payloads = $notification->get_payloads( $notifiable ); Plugin::instance()->integrations->run( $payloads ); return $this; } } notifications/notification.php 0000666 00000001403 15165257376 0012637 0 ustar 00 <?php namespace ElementorPro\Core\Notifications; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } abstract class Notification { /** * Get the payloads of the notification data shape (e.g. `Email_Message`, `Database_Message`). Those will automatically * be sent over to the appropriate `Actions` under the `Integration_Manager` (using the `notify()` method). * This method is also used to determine notification channels based on user ($notifiable) preferences. * * Returned shape: * [ * $payload1_instance, * $payload2_instance, * ] * * @param \ElementorPro\Core\Notifications\Traits\Notifiable $notifiable - The notified model. * * @return array */ public function get_payloads( $notifiable ) { return []; } } notifications/traits/notifiable.php 0000666 00000001246 15165257376 0013600 0 ustar 00 <?php namespace ElementorPro\Core\Notifications\Traits; use ElementorPro\Core\Notifications\Notification; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } trait Notifiable { /** * Notify a Model with a notification. * Syntactic sugar for sending notifications via the `Notifications_Manager`. * * Usage: * $model->notify( new User_Created_Notification( $new_user ) ); * * @param Notification $notification - Notification to send. * * @throws \Exception * * @return void */ public function notify( Notification $notification ) { Plugin::instance()->notifications->send( $notification, $this ); } } upgrade/upgrades.php 0000666 00000102330 15165257376 0010542 0 ustar 00 <?php namespace ElementorPro\Core\Upgrade; use Elementor\Core\Base\Document; use Elementor\Core\Upgrade\Updater; use Elementor\Icons_Manager; use Elementor\Core\Upgrade\Upgrades as Core_Upgrades; use ElementorPro\License\API; use ElementorPro\Plugin; use Elementor\Modules\History\Revisions_Manager; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Upgrades { public static $typography_control_names = [ 'typography', // The popover toggle ('starter_name'). 'font_family', 'font_size', 'font_weight', 'text_transform', 'font_style', 'text_decoration', 'line_height', 'letter_spacing', ]; public static function _on_each_version( $updater ) { self::_remove_remote_info_api_data(); } public static function _v_1_3_0() { global $wpdb; // Fix Button widget to new sizes options $post_ids = $wpdb->get_col( 'SELECT `post_id` FROM `' . $wpdb->postmeta . '` WHERE `meta_key` = "_elementor_data" AND `meta_value` LIKE \'%"widgetType":"form"%\';' ); if ( empty( $post_ids ) ) { return; } foreach ( $post_ids as $post_id ) { $document = Plugin::elementor()->documents->get( $post_id ); if ( $document ) { $data = $document->get_elements_data(); } if ( empty( $data ) ) { continue; } $data = Plugin::elementor()->db->iterate_data( $data, function( $element ) { if ( empty( $element['widgetType'] ) || 'form' !== $element['widgetType'] ) { return $element; } if ( ! isset( $element['settings']['submit_actions'] ) ) { $element['settings']['submit_actions'] = [ 'email' ]; } if ( ! empty( $element['settings']['redirect_to'] ) ) { if ( ! in_array( 'redirect', $element['settings']['submit_actions'] ) ) { $element['settings']['submit_actions'][] = 'redirect'; } } if ( ! empty( $element['settings']['webhooks'] ) ) { if ( ! in_array( 'webhook', $element['settings']['submit_actions'] ) ) { $element['settings']['submit_actions'][] = 'webhook'; } } return $element; } ); self::save_editor( $post_id, $data ); } } public static function _v_1_4_0() { global $wpdb; // Move all posts columns to classic skin (Just add prefix) $post_ids = $wpdb->get_col( 'SELECT `post_id` FROM `' . $wpdb->postmeta . '` WHERE `meta_key` = "_elementor_data" AND `meta_value` LIKE \'%"widgetType":"posts"%\';' ); if ( empty( $post_ids ) ) { return; } foreach ( $post_ids as $post_id ) { $document = Plugin::elementor()->documents->get( $post_id ); if ( $document ) { $data = $document->get_elements_data(); } if ( empty( $data ) ) { continue; } $data = Plugin::elementor()->db->iterate_data( $data, function( $element ) { if ( empty( $element['widgetType'] ) || 'posts' !== $element['widgetType'] ) { return $element; } $fields_to_change = [ 'columns', 'columns_mobile', 'columns_tablet', ]; foreach ( $fields_to_change as $field ) { // TODO: Remove old value later $new_field_key = 'classic_' . $field; if ( isset( $element['settings'][ $field ] ) && ! isset( $element['settings'][ $new_field_key ] ) ) { $element['settings'][ $new_field_key ] = $element['settings'][ $field ]; } } return $element; } ); $document = Plugin::elementor()->documents->get( $post_id ); $document->save( [ 'elements' => $data, ] ); } } public static function _v_1_12_0() { global $wpdb; // Set `mailchimp_api_key_source` to `custom`. $post_ids = $wpdb->get_col( 'SELECT `post_id` FROM `' . $wpdb->postmeta . '` WHERE `meta_key` = "_elementor_data" AND `meta_value` LIKE \'%"widgetType":"form"%\';' ); if ( empty( $post_ids ) ) { return; } foreach ( $post_ids as $post_id ) { $do_update = false; $document = Plugin::elementor()->documents->get( $post_id ); if ( $document ) { $data = $document->get_elements_data(); } if ( empty( $data ) ) { continue; } $data = Plugin::elementor()->db->iterate_data( $data, function( $element ) use ( &$do_update ) { if ( empty( $element['widgetType'] ) || 'form' !== $element['widgetType'] ) { return $element; } if ( ! empty( $element['settings']['mailchimp_api_key'] ) && ! isset( $element['settings']['mailchimp_api_key_source'] ) ) { $element['settings']['mailchimp_api_key_source'] = 'custom'; $do_update = true; } return $element; } ); // Only update if form has mailchimp if ( ! $do_update ) { continue; } // We need the `wp_slash` in order to avoid the unslashing during the `update_post_meta` $json_value = wp_slash( wp_json_encode( $data ) ); update_metadata( 'post', $post_id, '_elementor_data', $json_value ); } } /** * Replace 'sticky' => 'yes' with 'sticky' => 'top' in sections. */ public static function _v_2_0_3() { global $wpdb; $post_ids = $wpdb->get_col( 'SELECT `post_id` FROM `' . $wpdb->postmeta . '` WHERE `meta_key` = "_elementor_data" AND `meta_value` LIKE \'%"sticky":"yes"%\';' ); if ( empty( $post_ids ) ) { return; } foreach ( $post_ids as $post_id ) { $do_update = false; $document = Plugin::elementor()->documents->get( $post_id ); if ( ! $document ) { continue; } $data = $document->get_elements_data(); if ( empty( $data ) ) { continue; } $data = Plugin::elementor()->db->iterate_data( $data, function( $element ) use ( &$do_update ) { if ( empty( $element['elType'] ) || 'section' !== $element['elType'] ) { return $element; } if ( ! empty( $element['settings']['sticky'] ) && 'yes' === $element['settings']['sticky'] ) { $element['settings']['sticky'] = 'top'; $do_update = true; } return $element; } ); if ( ! $do_update ) { continue; } // We need the `wp_slash` in order to avoid the unslashing during the `update_metadata` $json_value = wp_slash( wp_json_encode( $data ) ); update_metadata( 'post', $post_id, '_elementor_data', $json_value ); } // End foreach(). } private static function save_editor( $post_id, $posted ) { // Change the global post to current library post, so widgets can use `get_the_ID` and other post data if ( isset( $GLOBALS['post'] ) ) { $global_post = $GLOBALS['post']; } $GLOBALS['post'] = get_post( $post_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited $editor_data = self::get_editor_data( $posted ); // We need the `wp_slash` in order to avoid the unslashing during the `update_post_meta` $json_value = wp_slash( wp_json_encode( $editor_data ) ); $is_meta_updated = update_metadata( 'post', $post_id, '_elementor_data', $json_value ); if ( $is_meta_updated ) { Revisions_Manager::handle_revision(); } // Restore global post if ( isset( $global_post ) ) { $GLOBALS['post'] = $global_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited } else { unset( $GLOBALS['post'] ); } /** * After editor saves data. * * Fires after Elementor editor data was saved. * * @since 1.0.0 * * @param int $post_id The ID of the post. * @param array $editor_data The editor data. */ do_action( 'elementor/editor/after_save', $post_id, $editor_data ); } private static function get_editor_data( $data, $with_html_content = false ) { $editor_data = []; foreach ( $data as $element_data ) { $element = Plugin::elementor()->elements_manager->create_element_instance( $element_data ); if ( ! $element ) { continue; } $editor_data[] = $element->get_raw_data( $with_html_content ); } // End Section return $editor_data; } public static function _v_2_5_0_form( $updater ) { $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_repeater_settings' ], 'control_ids' => [ 'form_fields' => [ '_id' => 'custom_id', ], ], ], ]; return self::_update_widget_settings( 'form', $updater, $changes ); } public static function _v_2_5_0_woocommerce_menu_cart( $updater ) { $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings' ], 'control_ids' => [ 'checkout_button_border_color' => 'checkout_border_color', 'view_cart_button_border_color' => 'view_cart_border_color', ], ], [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_slider_to_border_settings' ], 'control_ids' => [ 'checkout_button_border_width' => [ 'new' => 'checkout_border_width', 'add' => 'checkout_border_border', ], 'view_cart_button_border_width' => [ 'new' => 'view_cart_border_width', 'add' => 'view_cart_border_border', ], ], ], ]; return self::_update_widget_settings( 'woocommerce-menu-cart', $updater, $changes ); } public static function _v_3_7_2_woocommerce_rename_related_to_related_products( $updater ) { $changes = self::get_woocommerce_rename_related_to_related_products_changes(); return self::_update_widget_settings( 'woocommerce-products', $updater, $changes ); } public static function _slider_to_border_settings( $element, $args ) { $widget_id = $args['widget_id']; $changes = $args['control_ids']; if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { return $element; } foreach ( $changes as $old => $new ) { if ( ! empty( $element['settings'][ $old ] ) && ! isset( $element['settings'][ $new['new'] ] ) ) { $new_border_width = [ 'unit' => $element['settings'][ $old ]['unit'], 'top' => $element['settings'][ $old ]['size'], 'bottom' => $element['settings'][ $old ]['size'], 'left' => $element['settings'][ $old ]['size'], 'right' => $element['settings'][ $old ]['size'], 'isLinked' => true, ]; $element['settings'][ $new ['new'] ] = $new_border_width; $element['settings'][ $new ['add'] ] = 'solid'; $args['do_update'] = true; } } return $element; } /** * @param $element * @param $args * * @return mixed */ public static function _rename_repeater_settings( $element, $args ) { $widget_id = $args['widget_id']; $changes = $args['control_ids']; if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { return $element; } foreach ( $changes as $change_key => $change ) { foreach ( $change as $old => $new ) { foreach ( $element['settings'][ $change_key ] as &$repeater ) { if ( ! empty( $repeater[ $old ] ) && ! isset( $repeater[ $new ] ) ) { $repeater[ $new ] = $repeater[ $old ]; $args['do_update'] = true; } } } } return $element; } private static function taxonomies_mapping( $prefix, $map_to ) { $taxonomy_filter_args = [ 'show_in_nav_menus' => true, ]; $taxonomies = get_taxonomies( $taxonomy_filter_args ); $mapping = []; foreach ( $taxonomies as $taxonomy ) { $mapping[ $prefix . $taxonomy . '_ids' ] = $map_to; } return $mapping; } public static function _v_2_5_0_posts( $updater ) { $add_taxonomies = self::taxonomies_mapping( 'posts_', [ 'posts_include' => 'terms' ] ); $merge_taxonomies = self::taxonomies_mapping( 'posts_', 'posts_include_term_ids' ); $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings' ], 'control_ids' => [ 'orderby' => 'posts_orderby', 'order' => 'posts_order', 'offset' => 'posts_offset', 'exclude' => 'posts_exclude', 'exclude_ids' => 'posts_exclude_ids', 'posts_query_id' => 'posts_posts_query_id', 'avoid_duplicates' => 'posts_avoid_duplicates', 'posts_authors' => 'posts_include_authors', ], ], [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_add_widget_settings_to_array' ], 'control_ids' => array_merge( $add_taxonomies, [ 'posts_authors' => [ 'posts_include' => 'authors' ], ] ), ], [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_merge_widget_settings' ], 'control_ids' => $merge_taxonomies, ], ]; return self::_update_widget_settings( 'posts', $updater, $changes ); } public static function _v_2_5_0_portfolio( $updater ) { $add_taxonomies = self::taxonomies_mapping( 'posts_', [ 'posts_include' => 'terms' ] ); $merge_taxonomies = self::taxonomies_mapping( 'posts_', 'posts_include_term_ids' ); $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings' ], 'control_ids' => [ 'orderby' => 'posts_orderby', 'order' => 'posts_order', 'offset' => 'posts_offset', 'exclude' => 'posts_exclude', 'exclude_ids' => 'posts_exclude_ids', 'posts_query_id' => 'posts_posts_query_id', 'avoid_duplicates' => 'posts_avoid_duplicates', 'posts_authors' => 'posts_include_authors', ], ], [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_add_widget_settings_to_array' ], 'control_ids' => array_merge( $add_taxonomies, [ 'posts_authors' => [ 'posts_include' => 'authors' ], ] ), ], [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_merge_widget_settings' ], 'control_ids' => $merge_taxonomies, ], ]; return self::_update_widget_settings( 'portfolio', $updater, $changes ); } public static function _v_2_5_0_products( $updater ) { $add_taxonomies = self::taxonomies_mapping( 'query_', [ 'query_include' => 'terms' ] ); $merge_taxonomies = self::taxonomies_mapping( 'query_', 'query_include_term_ids' ); $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings' ], 'control_ids' => [ 'orderby' => 'query_orderby', 'order' => 'query_order', 'exclude' => 'query_exclude', 'exclude_ids' => 'query_exclude_ids', 'query_authors' => 'query_include_authors', 'query_product_tag_ids' => 'query_include_term_ids', 'query_product_cat_ids' => 'query_include_term_ids', ], ], [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_add_widget_settings_to_array' ], 'control_ids' => array_merge( $add_taxonomies, [ 'query_authors' => [ 'query_include' => 'authors' ], ] ), ], [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_merge_widget_settings' ], 'control_ids' => $merge_taxonomies, ], ]; return self::_update_widget_settings( 'woocommerce-products', $updater, $changes ); } /** * @param $updater * * @return bool Should run again. */ public static function _v_2_5_0_sitemap( $updater ) { $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings' ], 'control_ids' => [ 'exclude' => 'sitemap_exclude', 'exclude_ids' => 'sitemap_exclude_ids', ], ], ]; return self::_update_widget_settings( 'sitemap', $updater, $changes ); } /** * @param Updater $updater * * @return bool */ public static function _v_2_5_0_popup_border_radius( $updater ) { global $wpdb; $post_ids = $updater->query_col( "SELECT pm1.post_id FROM {$wpdb->postmeta} AS pm1 LEFT JOIN {$wpdb->postmeta} AS pm2 ON (pm1.post_id = pm2.post_id) WHERE pm1.meta_key = '_elementor_template_type' AND pm1.meta_value = 'popup' AND pm2.`meta_key` = '" . Document::PAGE_META_KEY . "' AND pm2.`meta_value` LIKE '%border_radius%';" ); if ( empty( $post_ids ) ) { return false; } foreach ( $post_ids as $post_id ) { // Clear WP cache for next step. $document = Plugin::elementor()->documents->get( $post_id ); if ( ! $document ) { continue; } $page_settings = $document->get_settings(); // Check if there isn't 'border_radius' setting or if it has already been upgraded if ( empty( $page_settings['border_radius']['size'] ) ) { continue; } $border_radius = $page_settings['border_radius']; $new_border_radius = [ 'unit' => $border_radius['unit'], 'top' => $border_radius['size'], 'bottom' => $border_radius['size'], 'left' => $border_radius['size'], 'right' => $border_radius['size'], 'isLinked' => true, ]; $page_settings['border_radius'] = $new_border_radius; // TODO: `$document->update_settings`. $document->update_meta( Document::PAGE_META_KEY, $page_settings ); wp_cache_flush(); } // End foreach(). return $updater->should_run_again( $post_ids ); } public static function _v_2_5_4_posts( $updater ) { $merge_taxonomies = self::taxonomies_mapping( 'posts_', 'posts_include_term_ids' ); $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_term_id_to_term_taxonomy_id' ], 'control_ids' => $merge_taxonomies, 'prefix' => 'posts_', 'new_id' => 'include_term_ids', ], ]; return self::_update_widget_settings( 'posts', $updater, $changes ); } public static function _v_2_5_4_portfolio( $updater ) { $merge_taxonomies = self::taxonomies_mapping( 'posts_', 'posts_include_term_ids' ); $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_term_id_to_term_taxonomy_id' ], 'control_ids' => $merge_taxonomies, 'prefix' => 'posts_', 'new_id' => 'include_term_ids', ], ]; return self::_update_widget_settings( 'portfolio', $updater, $changes ); } public static function _v_2_5_4_products( $updater ) { $merge_taxonomies = self::taxonomies_mapping( 'query_', 'query_include_term_ids' ); $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_term_id_to_term_taxonomy_id' ], 'control_ids' => $merge_taxonomies, 'prefix' => 'query_', 'new_id' => 'include_term_ids', ], ]; return self::_update_widget_settings( 'woocommerce-products', $updater, $changes ); } public static function _v_2_5_4_form( $updater ) { $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_missing_form_custom_id_settings' ], 'control_ids' => [], ], ]; return self::_update_widget_settings( 'form', $updater, $changes ); } public static function _v_3_1_0_media_carousel( $updater ) { $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_progress_to_progressbar' ], 'control_ids' => [], ], ]; return self::_update_widget_settings( 'media-carousel', $updater, $changes ); } public static function _v_3_1_0_reviews( $updater ) { $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_progress_to_progressbar' ], 'control_ids' => [], ], ]; return self::_update_widget_settings( 'reviews', $updater, $changes ); } public static function _v_3_1_0_testimonial_carousel( $updater ) { $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_progress_to_progressbar' ], 'control_ids' => [], ], ]; return self::_update_widget_settings( 'testimonial-carousel', $updater, $changes ); } public static function _v_3_1_0_slides( $updater ) { $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_migrate_slides_button_color_settings' ], 'control_ids' => [], ], ]; return self::_update_widget_settings( 'slides', $updater, $changes ); } public static function _v_3_3_0_nav_menu_icon( $updater ) { $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_migrate_indicator_control_to_submenu_icon' ], 'control_ids' => [], ], ]; return self::_update_widget_settings( 'nav-menu', $updater, $changes ); } public static function _v_3_3_0_recalc_usage_data( $updater ) { return Core_Upgrades::recalc_usage_data( $updater ); } public static function _v_3_5_0_price_list( $updater ) { $changes = [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_copy_title_styles_to_new_price_controls' ], 'control_ids' => [], ], ]; return self::_update_widget_settings( 'price-list', $updater, $changes ); } /** * $changes is an array of arrays in the following format: * [ * 'control_ids' => array of control ids * 'callback' => user callback to manipulate the control_ids * ] * * @param $widget_id * @param $updater * @param array $changes * * @return bool */ public static function _update_widget_settings( $widget_id, $updater, $changes ) { global $wpdb; $post_ids = $updater->query_col( 'SELECT `post_id` FROM `' . $wpdb->postmeta . '` WHERE `meta_key` = "_elementor_data" AND `meta_value` LIKE \'%"widgetType":"' . $widget_id . '"%\';' ); if ( empty( $post_ids ) ) { return false; } foreach ( $post_ids as $post_id ) { $do_update = false; $document = Plugin::elementor()->documents->get( $post_id ); if ( ! $document ) { continue; } $data = $document->get_elements_data(); if ( empty( $data ) ) { continue; } // loop through callbacks & array foreach ( $changes as $change ) { $args = [ 'do_update' => &$do_update, 'widget_id' => $widget_id, 'control_ids' => $change['control_ids'], ]; if ( isset( $change['prefix'] ) ) { $args['prefix'] = $change['prefix']; $args['new_id'] = $change['new_id']; } $data = Plugin::elementor()->db->iterate_data( $data, $change['callback'], $args ); if ( ! $do_update ) { continue; } // We need the `wp_slash` in order to avoid the unslashing during the `update_metadata` $json_value = wp_slash( wp_json_encode( $data ) ); update_metadata( 'post', $post_id, '_elementor_data', $json_value ); } } // End foreach(). return $updater->should_run_again( $post_ids ); } /** * @param $element * @param $args * * @return mixed */ public static function _rename_widget_settings( $element, $args ) { $widget_id = $args['widget_id']; $changes = $args['control_ids']; if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { return $element; } foreach ( $changes as $old => $new ) { if ( ! empty( $element['settings'][ $old ] ) && ! isset( $element['settings'][ $new ] ) ) { $element['settings'][ $new ] = $element['settings'][ $old ]; $args['do_update'] = true; } } return $element; } /** * @param $element * @param $args * * @return mixed */ public static function _rename_widget_settings_value( $element, $args ) { $widget_id = $args['widget_id']; $changes = $args['control_ids']; if ( self::is_widget_matched( $element, $widget_id ) ) { $element = self::apply_rename( $changes, $element, $args ); } return $element; } /** * @param $element * @param $args * * @return mixed */ public static function _add_widget_settings_to_array( $element, $args ) { $widget_id = $args['widget_id']; $changes = $args['control_ids']; if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { return $element; } foreach ( $changes as $old_key => $added_key ) { if ( ! empty( $element['settings'][ $old_key ] ) ) { foreach ( $added_key as $control_id => $val ) { if ( ! in_array( $val, $element['settings'][ $control_id ], true ) ) { $element['settings'][ $control_id ][] = $val; $args['do_update'] = true; } } } } return $element; } /** * @param $element * @param $args * * @return mixed */ public static function _merge_widget_settings( $element, $args ) { $widget_id = $args['widget_id']; $changes = $args['control_ids']; if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { return $element; } foreach ( $changes as $old => $new ) { if ( ! empty( $element['settings'][ $old ] ) ) { if ( ! isset( $element['settings'][ $new ] ) ) { $element['settings'][ $new ] = $element['settings'][ $old ]; } else { $element['settings'][ $new ] = array_unique( array_merge( $element['settings'][ $old ], $element['settings'][ $new ] ) ); } $args['do_update'] = true; } } return $element; } /** * Possible scenarios: * 1) custom_id is not empty --> do nothing * 2) Existing _id: Empty or Missing custom_id --> create custom_id and set the value to the value of _id * 3) Missing _id: Empty or Missing custom_id --> generate a unique key and set it as custom_id value * @param $element * @param $args * * @return mixed */ public static function _missing_form_custom_id_settings( $element, $args ) { $widget_id = $args['widget_id']; if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { return $element; } $random_id = (int) substr( time(), -5 ); //form_fields loop: foreach ( $element['settings']['form_fields'] as &$repeater_item ) { if ( ! empty( $repeater_item['custom_id'] ) ) { // Scenario 1 continue; } if ( ! empty( $repeater_item['_id'] ) ) { // Scenario 2 $repeater_item['custom_id'] = $repeater_item['_id']; } else { // Scenario 3 $repeater_item['custom_id'] = 'field_' . $random_id; $random_id++; } $args['do_update'] = true; } return $element; } /** * Migrates the value saved for the 'indicator' SELECT control in the Nav Menu Widget to the new replacement * 'submenu_icon' ICONS control. * * @param $element * @param $args * * @return mixed; */ public static function _migrate_indicator_control_to_submenu_icon( $element, $args ) { $widget_id = $args['widget_id']; // If the current element is not a Nav Menu widget, go to the next one. if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { return $element; } // If this Nav Menu widget's 'indicator' control value is the default one (there is no value in the DB), // there is nothing to migrate, since the default icon is identical in the new control. Go to the next element. if ( ! isset( $element['settings']['indicator'] ) ) { return $element; } $new_value = ''; $new_library = 'fa-solid'; switch ( $element['settings']['indicator'] ) { case 'none': $new_library = ''; break; case 'classic': $new_value = 'fa-caret-down'; break; case 'chevron': $new_value = 'fa-chevron-down'; break; case 'angle': $new_value = 'fa-angle-down'; break; case 'plus': $new_value = 'e-plus-icon'; $new_library = ''; break; } // This is done in order to make sure that the menu will not look any different for users who upgrade. // The 'None' option should be completely empty. if ( $new_value ) { if ( Icons_Manager::is_migration_allowed() ) { // If the site has been migrated to FA5, add the new FA Solid class. $new_value = 'fas ' . $new_value; } else { // If the site has not been migrated, add the old generic 'fa' class. $new_value = 'fa ' . $new_value; } } // Set the migrated value for the new control. $element['settings']['submenu_icon'] = [ 'value' => $new_value, 'library' => $new_library, ]; $args['do_update'] = true; return $element; } /** * @param $element * @param $args * * @return mixed */ public static function _convert_term_id_to_term_taxonomy_id( $element, $args ) { $widget_id = $args['widget_id']; $changes = $args['control_ids']; $prefix = $args['prefix']; $new_id = $prefix . $args['new_id']; if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { return $element; } // Exit if new is empty (should not happen) if ( empty( $element['settings'][ $new_id ] ) ) { return $element; } // 1) Convert each term-id to the equivalent term_taxonomy_id $term_taxonomy_ids = []; $old_term_ids = []; foreach ( $changes as $old => $new ) { if ( ! empty( $element['settings'][ $old ] ) ) { $start = strlen( $prefix ); $end = -strlen( '_ids' ); $taxonomy = substr( $old, $start, $end ); foreach ( $element['settings'][ $old ] as $term_id ) { $old_term_ids[] = $term_id; $term_obj = get_term( $term_id, $taxonomy, OBJECT ); if ( $term_obj && ! is_wp_error( $term_obj ) ) { $term_taxonomy_ids[] = $term_obj->term_taxonomy_id; } } } } // 2) Check if the widget's settings were changed after the u/g to 2.5.0 $diff = array_diff( $element['settings'][ $new_id ], array_unique( $old_term_ids ) ); if ( empty( $diff ) ) { // Nothing was changed $element['settings'][ $new_id . '_backup' ] = $element['settings'][ $new_id ]; $element['settings'][ $new_id ] = $term_taxonomy_ids; $args['do_update'] = true; } return $element; } /** * Convert 'progress' to 'progressbar' * * Before Elementor 2.2.0, the progress bar option key was 'progress'. In Elementor 2.2.0, * it was changed to 'progressbar'. This upgrade script migrated the DB data for old websites using 'progress'. * * @param $element * @param $args * @return mixed */ public static function _convert_progress_to_progressbar( $element, $args ) { $widget_id = $args['widget_id']; if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { return $element; } if ( 'progress' === $element['settings']['pagination'] ) { $element['settings']['pagination'] = 'progressbar'; $args['do_update'] = true; } return $element; } /** * Migrate Slides Button Color Settings * * Move Slides Widget's 'button_color' settings to 'button_text_color' and 'button_border_color' as necessary, * to allow for removing the redundant control. * * @param $element * @param $args * @return mixed */ public static function _migrate_slides_button_color_settings( $element, $args ) { if ( empty( $element['widgetType'] ) || $args['widget_id'] !== $element['widgetType'] ) { return $element; } // If the element doesn't use the 'button_color' control, no need to do anything. if ( ! isset( $element['settings']['button_color'] ) ) { return $element; } // Check if button_text_color is set. If it is not set, transfer the value from button_color to button_text_color. if ( ! isset( $element['settings']['button_text_color'] ) ) { $element['settings']['button_text_color'] = $element['settings']['button_color']; $args['do_update'] = true; } // Check if button_border_color is set. If it is not set, transfer the value from button_color to button_border_color. if ( ! isset( $element['settings']['button_border_color'] ) ) { $element['settings']['button_border_color'] = $element['settings']['button_color']; $args['do_update'] = true; } return $element; } /** * Copy Title Styles to New Price Controls * * Copy the values from the Price List widget's Title Style controls to new Price Style controls. * * @param $element * @param $args * @return mixed * @since 3.4.0 * */ public static function _copy_title_styles_to_new_price_controls( $element, $args ) { if ( empty( $element['widgetType'] ) || $args['widget_id'] !== $element['widgetType'] ) { return $element; } if ( ! empty( $element['settings']['heading_color'] ) ) { $element['settings']['price_color'] = $element['settings']['heading_color']; $args['do_update'] = true; } $old_control_prefix = 'heading_typography_'; $new_control_prefix = 'price_typography_'; foreach ( self::$typography_control_names as $control_name ) { if ( ! empty( $element['settings'][ $old_control_prefix . $control_name ] ) ) { $element['settings'][ $new_control_prefix . $control_name ] = $element['settings'][ $old_control_prefix . $control_name ]; $args['do_update'] = true; } } return $element; } public static function _remove_remote_info_api_data() { global $wpdb; $key = API::TRANSIENT_KEY_PREFIX; return $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '{$key}%';"); // phpcs:ignore } /** * @param $element * @param $to * @param $control_id * @param $args * @return array */ protected static function set_new_value( $element, $to, $control_id, $args ) { $element['settings'][ $control_id ] = $to; $args['do_update'] = true; return $element; } /** * * @param $change * @param array $element * @param $args * @return array */ protected static function replace_value_if_found( $change, array $element, $args ) { $control_id = key( $args['control_ids'] ); $from = $change['from']; $to = $change['to']; if ( self::is_control_exist_in_settings( $element, $control_id ) && self::is_need_to_replace_value( $element, $control_id, $from ) ) { $element = self::set_new_value( $element, $to, $control_id, $args ); } return $element; } /** * @param $element * @param $widget_id * @return bool */ protected static function is_widget_matched( $element, $widget_id ) { return ! empty( $element['widgetType'] ) && $widget_id === $element['widgetType']; } /** * @param $changes * @param $element * @param $args * @return array|mixed */ protected static function apply_rename( $changes, $element, $args ) { foreach ( $changes as $change ) { $element = self::replace_value_if_found( $change, $element, $args ); } return $element; } /** * @param $element * @param $control_id * @return bool */ protected static function is_control_exist_in_settings( $element, $control_id ) { return ! empty( $element['settings'][ $control_id ] ); } /** * @param $element * @param $new * @return bool */ protected static function is_need_to_replace_value( $element, $control_id, $value_to_replace ) { return $element['settings'][ $control_id ] === $value_to_replace; } /** * @return array[] */ public static function get_woocommerce_rename_related_to_related_products_changes() { return [ [ 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings_value' ], 'control_ids' => [ 'query_post_type' => [ 'from' => 'related', 'to' => 'related_products', ], ], ], ]; } } upgrade/manager.php 0000666 00000001453 15165257376 0010346 0 ustar 00 <?php namespace ElementorPro\Core\Upgrade; use Elementor\Core\Upgrade\Manager as Upgrades_Manager; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Manager extends Upgrades_Manager { public function get_action() { return 'elementor_pro_updater'; } public function get_plugin_name() { return 'elementor-pro'; } public function get_plugin_label() { return esc_html__( 'Elementor Pro', 'elementor-pro' ); } public function get_updater_label() { return esc_html__( 'Elementor Pro Data Updater', 'elementor-pro' ); } public function get_new_version() { return ELEMENTOR_PRO_VERSION; } public function get_version_option_name() { return 'elementor_pro_version'; } public function get_upgrades_class() { return 'ElementorPro\Core\Upgrade\Upgrades'; } } includes/class-astra-addon-admin-ajax.php 0000666 00000031604 15165304350 0014404 0 ustar 00 <?php /** * Astra Admin Ajax Base. * * @package Astra * @since 4.0.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Class Astra_Addon_Admin_Ajax. * * @since 4.0.0 */ class Astra_Addon_Admin_Ajax { /** * Ajax action prefix. * * @var string * @since 4.0.0 */ private $prefix = 'astra'; /** * Instance * * @var object Class object. * @since 4.0.0 */ private static $instance; /** * Initiator * * @since 4.0.0 * @return object initialized object of class. */ public static function get_instance() { if ( ! isset( self::$instance ) ) { self::$instance = new self(); } return self::$instance; } /** * Erros class instance. * * @var object * @since 4.0.0 */ private $errors = array(); /** * Constructor * * @since 4.0.0 */ public function __construct() { $this->errors = array( 'permission' => __( 'Sorry, you are not allowed to do this operation.', 'astra-addon' ), 'nonce' => __( 'Nonce validation failed', 'astra-addon' ), 'default' => __( 'Sorry, something went wrong.', 'astra-addon' ), 'invalid' => __( 'No post data found!', 'astra-addon' ), ); // Ajax requests. add_action( 'wp_ajax_astra_addon_update_module_status', array( $this, 'update_module_status' ) ); add_action( 'wp_ajax_astra_addon_bulk_activate_modules', array( $this, 'bulk_activate_modules' ) ); add_action( 'wp_ajax_astra_addon_bulk_deactivate_modules', array( $this, 'bulk_deactivate_modules' ) ); add_action( 'wp_ajax_astra_addon_clear_cache', array( $this, 'clear_cache' ) ); // Enable/Disable beta updates. add_action( 'wp_ajax_astra_beta_updates', array( $this, 'enable_disable_beta_updates' ) ); // Enable/Disable file generation. add_action( 'wp_ajax_astra_file_generation', array( $this, 'enable_disable_file_generation' ) ); // Enable/Disable file generation. add_action( 'wp_ajax_astra_addon_update_whitelabel', array( $this, 'astra_addon_update_whitelabel' ) ); } /** * Return settings for admin dashboard app. * * @param array $data_settings admin settings based on their data types. * * @return array * @since 4.0.0 */ public function astra_admin_settings_typewise( $data_settings ) { return $data_settings; } /** * Update pro addon module status. activate/deactivate * * @since 4.0.0 */ public function update_module_status() { $response_data = array( 'message' => $this->get_error_msg( 'permission' ) ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( $response_data ); } if ( empty( $_POST ) ) { $response_data = array( 'message' => $this->get_error_msg( 'invalid' ) ); wp_send_json_error( $response_data ); } /** * Nonce verification. */ if ( ! check_ajax_referer( 'astra_addon_update_admin_setting', 'security', false ) ) { $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) ); wp_send_json_error( $response_data ); } $module_id = isset( $_POST['module_id'] ) ? sanitize_text_field( $_POST['module_id'] ) : ''; $module_status = isset( $_POST['module_status'] ) ? sanitize_text_field( $_POST['module_status'] ) : ''; $extensions = Astra_Ext_Extension::get_enabled_addons(); $extensions[ $module_id ] = 'activate' === $module_status ? $module_id : false; $extensions = array_map( 'esc_attr', $extensions ); Astra_Admin_Helper::update_admin_settings_option( '_astra_ext_enabled_extensions', $extensions ); if ( 'http2' == $module_id ) { Astra_Admin_Helper::update_admin_settings_option( '_astra_ext_http2', true ); } set_transient( 'astra_addon_activated_transient', $module_id ); wp_send_json_success(); } /** * Activate all module */ public function bulk_activate_modules() { $response_data = array( 'message' => $this->get_error_msg( 'permission' ) ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( $response_data ); } if ( empty( $_POST ) ) { $response_data = array( 'message' => $this->get_error_msg( 'invalid' ) ); wp_send_json_error( $response_data ); } /** * Nonce verification. */ if ( ! check_ajax_referer( 'astra_addon_update_admin_setting', 'security', false ) ) { $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) ); wp_send_json_error( $response_data ); } // Get all extensions. $all_extensions = Astra_Ext_Extension::get_addons(); // Sanitize Addon list. foreach ( $all_extensions as $key => $value ) { $all_extensions[ sanitize_key( $key ) ] = $value; } $new_extensions = array(); // Set all extension to enabled. foreach ( $all_extensions as $slug => $value ) { $new_extensions[ $slug ] = $slug; } // Escape attrs. $new_extensions = array_map( 'esc_attr', $new_extensions ); // Update new_extensions. Astra_Admin_Helper::update_admin_settings_option( '_astra_ext_enabled_extensions', $new_extensions ); Astra_Admin_Helper::update_admin_settings_option( '_astra_ext_http2', true ); set_transient( 'astra_addon_activated_transient', $new_extensions ); wp_send_json_success(); } /** * Deactivate all module */ public function bulk_deactivate_modules() { $response_data = array( 'message' => $this->get_error_msg( 'permission' ) ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( $response_data ); } if ( empty( $_POST ) ) { $response_data = array( 'message' => $this->get_error_msg( 'invalid' ) ); wp_send_json_error( $response_data ); } /** * Nonce verification. */ if ( ! check_ajax_referer( 'astra_addon_update_admin_setting', 'security', false ) ) { $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) ); wp_send_json_error( $response_data ); } // Get all extensions. $old_extensions = array_map( 'sanitize_text_field', Astra_Ext_Extension::get_enabled_addons() ); $new_extensions = array(); // Set all extension to enabled. foreach ( $old_extensions as $slug => $value ) { $new_extensions[ $slug ] = false; } // Escape attrs. $new_extensions = array_map( 'esc_attr', $new_extensions ); // Update new_extensions. Astra_Admin_Helper::update_admin_settings_option( '_astra_ext_enabled_extensions', $new_extensions ); Astra_Admin_Helper::delete_admin_settings_option( '_astra_ext_http2' ); set_transient( 'astra_addon_deactivated_transient', $new_extensions ); wp_send_json_success(); } /** * Clear assets cache. */ public function clear_cache() { Astra_Minify::refresh_assets(); wp_send_json_success(); } /** * Ajax handler to enable / disable the beta updates for Astra Theme and Astra Pro. * * @since 1.5.1 * @return void */ public function enable_disable_beta_updates() { $response_data = array( 'message' => $this->get_error_msg( 'permission' ) ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( $response_data ); } if ( empty( $_POST ) ) { $response_data = array( 'message' => $this->get_error_msg( 'invalid' ) ); wp_send_json_error( $response_data ); } /** * Nonce verification. */ if ( ! check_ajax_referer( 'astra_addon_update_admin_setting', 'security', false ) ) { $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) ); wp_send_json_error( $response_data ); } $status = isset( $_POST['status'] ) ? sanitize_text_field( $_POST['status'] ) : false; if ( false !== $status ) { Astra_Admin_Helper::update_admin_settings_option( '_astra_beta_updates', $status, true ); wp_send_json_success(); } wp_send_json_error(); } /** * Ajax handler to enable / disable the file generation of scripts/styles for Astra Theme and Astra Pro. * * @since 1.5.1 * @return void */ public function enable_disable_file_generation() { $response_data = array( 'message' => $this->get_error_msg( 'permission' ) ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( $response_data ); } if ( empty( $_POST ) ) { $response_data = array( 'message' => $this->get_error_msg( 'invalid' ) ); wp_send_json_error( $response_data ); } /** * Nonce verification. */ if ( ! check_ajax_referer( 'astra_addon_update_admin_setting', 'security', false ) ) { $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) ); wp_send_json_error( $response_data ); } $status = isset( $_POST['status'] ) ? sanitize_text_field( $_POST['status'] ) : false; if ( false !== $status ) { update_option( '_astra_file_generation', $status ); wp_send_json_success(); } wp_send_json_error(); } /** * Save whitelabel Settings. * * @since 4.0.0 */ public function astra_addon_update_whitelabel() { $response_data = array( 'message' => $this->get_error_msg( 'permission' ) ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( $response_data ); } if ( empty( $_POST ) ) { $response_data = array( 'message' => $this->get_error_msg( 'invalid' ) ); wp_send_json_error( $response_data ); } /** * Nonce verification. */ if ( ! check_ajax_referer( 'astra_addon_update_admin_setting', 'security', false ) ) { $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) ); wp_send_json_error( $response_data ); } $white_label_settings = Astra_Ext_White_Label_Markup::get_white_labels(); $data = isset( $_POST['data'] ) ? array_map( 'sanitize_text_field', json_decode( stripslashes( sanitize_text_field( $_POST['data'] ) ), true ) ) : array(); $parent = isset( $_POST['parent'] ) ? sanitize_text_field( $_POST['parent'] ) : false; $key = isset( $_POST['key'] ) ? sanitize_text_field( $_POST['key'] ) : false; $value = isset( $_POST['value'] ) ? stripslashes( sanitize_text_field( $_POST['value'] ) ) : false; if ( $parent && $key ) { if ( ! $parent || ! $key || ! $value ) { $response_data = array( 'message' => $this->get_error_msg( 'invalid' ) ); wp_send_json_error( $response_data ); } $white_label_settings[ $parent ][ $key ] = $value; } else { if ( ! $data ) { $response_data = array( 'message' => $this->get_error_msg( 'invalid' ) ); wp_send_json_error( $response_data ); } $white_label_settings['astra-agency']['author'] = isset( $data['agencyAuthorName'] ) ? $data['agencyAuthorName'] : $white_label_settings['astra-agency']['author']; $white_label_settings['astra-agency']['author_url'] = isset( $data['agencyAuthorURL'] ) ? $data['agencyAuthorURL'] : $white_label_settings['astra-agency']['author_url']; $white_label_settings['astra-agency']['licence'] = isset( $data['agencyLicenseLink'] ) ? $data['agencyLicenseLink'] : $white_label_settings['astra-agency']['licence']; $white_label_settings['astra']['name'] = isset( $data['themeName'] ) ? $data['themeName'] : $white_label_settings['astra']['name']; $white_label_settings['astra']['description'] = isset( $data['themeDescription'] ) ? $data['themeDescription'] : $white_label_settings['astra']['description']; $white_label_settings['astra']['screenshot'] = isset( $data['themeScreenshotURL'] ) ? $data['themeScreenshotURL'] : $white_label_settings['astra']['screenshot']; $white_label_settings['astra']['icon'] = isset( $data['themeIconURL'] ) ? $data['themeIconURL'] : $white_label_settings['astra']['icon']; $white_label_settings['astra-pro']['name'] = isset( $data['pluginName'] ) ? $data['pluginName'] : $white_label_settings['astra-pro']['name']; $white_label_settings['astra-pro']['description'] = isset( $data['pluginDescription'] ) ? $data['pluginDescription'] : $white_label_settings['astra-pro']['description']; $white_label_settings['astra-sites']['name'] = isset( $data['sTPluginName'] ) ? $data['sTPluginName'] : $white_label_settings['astra-sites']['name']; $white_label_settings['astra-sites']['description'] = isset( $data['sTPluginDescription'] ) ? $data['sTPluginDescription'] : $white_label_settings['astra-sites']['description']; } Astra_Admin_Helper::update_admin_settings_option( '_astra_ext_white_label', $white_label_settings, true ); $theme_name = Astra_Ext_White_Label_Markup::get_whitelabel_string( 'astra', 'name', 'astra' ); $response = array( 'rebranded_theme_name' => rawurlencode( $theme_name ), ); wp_send_json_success( $response ); } /** * Get ajax error message. * * @param string $type Message type. * @return string * @since 4.0.0 */ public function get_error_msg( $type ) { if ( ! isset( $this->errors[ $type ] ) ) { $type = 'default'; } return $this->errors[ $type ]; } } Astra_Addon_Admin_Ajax::get_instance(); assets/css/admin-custom.css 0000666 00000014001 15165304350 0011752 0 ustar 00 .ast-admin-top-bar-root { font-family: Inter, sans-serif; } .mb-4 { margin-bottom: 1rem; } .justify-items-end { justify-items: end; } .w-max { width: max-content; } .mt-4 { margin-top: 1rem/* 16px */; } .pr-4 { padding-right: 1rem/* 16px */; } .ml-8 { margin-left: 2rem/* 32px */; } .astra-parent-field-false + .astra-child-field { pointer-events: none; opacity: 0.4; } .w-4\/5 { width: 80%; } .text-right { text-align: right; } .top-1\/2 { top: 50%; } .-translate-y-1\/2 { transform: translateY(-50%); } .left-3 { left: 0.75rem; } .pl-3 { padding-left: 0.75rem; } .pt-14 { padding-top: 3.5rem/* 56px */; } .gap-6 { gap: 1.5rem/* 24px */; } .-mb-11\.5 { margin-bottom: -2.875rem/* 46px */; } .min-h-24 { min-height: 6rem/* 96px */; } .border-r { border-right-width: 1px; } .border-slate-200 { --tw-border-opacity: 1; border-color: rgb(226 232 240 / var(--tw-border-opacity)); } a.bsf-core-license-form-btn { text-decoration: underline; color: #2271b1; } .ast-whitelabel-wrap .ast-admin_input-field { border: 1px solid #CBD5E1; padding: 6px 13px; border-radius: 6px; } .ast-licensing-wrap .ast-admin_license-input-field { border: 1px solid #CBD5E1; padding: 6px 36px; border-radius: 6px; width: 28.3125rem; } @media(max-width: 781px) { .ast-admin_license-input-field { width: 100%; } section.ast-whitelabel-wrap input, section.ast-whitelabel-wrap textarea { width: 100%; } div .tablet\:justify-items-start { justify-items: start; } } /* Rollback popup CSS compatibility. */ .ast-rollback__dialog { z-index: 99999; } .backdrop-blur-sm { --tw-backdrop-blur: blur(4px); -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); } .bg-gray-500 { --tw-bg-opacity: 1; background-color: rgb(107 114 128 / var(--tw-bg-opacity)); } .bg-opacity-75 { --tw-bg-opacity: 0.75; } .bg-red-50 { --tw-bg-opacity: 1; background-color: rgb(254 242 242 / var(--tw-bg-opacity)); } .stroke-red-600 { stroke: #dc2626; } .min-h-screen { min-height: 100vh; } .items-end { align-items: flex-end; } .ast-rollback__dialog .bg-red-600 { --tw-bg-opacity: 1; background-color: rgb(220 38 38 / var(--tw-bg-opacity)); } @media (min-width: 640px) { .ast-rollback__dialog .sm\:mx-0 { margin-left: 0px; margin-right: 0px; } .ast-rollback__dialog .sm\:w-16 { width: 4rem; } .ast-rollback__dialog .sm\:block { display: block; } .sm\:align-middle { vertical-align: middle; } .sm\:max-w-lg { max-width: 32rem; } .sm\:w-full { width: 100%; } .sm\:my-8 { margin-top: 2rem; margin-bottom: 2rem; } .sm\.pr-8 { padding-right: 2rem; } .sm\:h-16 { height: 4rem; } .sm\:text-left { text-align: left; } .sm\:ml-4 { margin-left: 1rem; } .sm\:ml-20 { margin-left: 5rem; } .sm\:mr-3 { margin-right: 0.75rem; } .sm\:inline-block { display: inline-block; } .sm\:h-screen { height: 100vh; } .sm\:pt-0 { padding-top: 0; } .sm\:pb-0 { padding-bottom: 0; } .sm\:min-h-15 { min-height: 3.9375rem/* 63px */; } .sm\:mb-0 { margin-bottom: 0; } } /* Post types compatibility CSS */ .post-type-astra-advanced-hook #wpcontent #wpbody #wpbody-content .wrap h1.wp-heading-inline, .post-type-astra_adv_header #wpcontent #wpbody #wpbody-content h1.wp-heading-inline, .post-type-astra-advanced-hook #wpcontent #wpbody #wpbody-content .wrap a.page-title-action, .post-type-astra_adv_header #wpcontent #wpbody #wpbody-content .wrap a.page-title-action { display: none; } .post-type-astra-advanced-hook #posts-filter .search-box, .post-type-astra_adv_header #posts-filter .search-box { display: flex; } .post-type-astra-advanced-hook .wrap, .post-type-astra_adv_header .wrap { margin-top: 45px; } .post-type-astra_adv_header #wpcontent, .post-type-astra-advanced-hook #wpcontent { padding-left: 0; } .post-type-astra-advanced-hook #wpbody-content, .post-type-astra_adv_header #wpbody-content { padding: 0 0 65px 20px; } .post-type-astra-advanced-hook table.wp-list-table, .post-type-astra_adv_header table.wp-list-table { position: relative; } .post-type-astra-advanced-hook .wrap div.notice:first-of-type, .post-type-astra_adv_header .wrap div.notice:first-of-type { margin-top: 2.5rem; } .post-type-astra-advanced-hook .tablenav, .post-type-astra_adv_header .tablenav { margin: 6px 0 9px; } .post-type-astra-advanced-hook #screen-options-wrap input:not([type=submit]), .post-type-astra_adv_header #screen-options-wrap input:not([type=submit]), table.wp-list-table input[type='checkbox'], table.wp-list-table input[type='radio'], table.wp-list-table input[type='checkbox']:hover, table.wp-list-table input[type='radio']:hover, table.wp-list-table input[type='checkbox']:focus, table.wp-list-table input[type='radio']:focus { background-color: #ffff; border: 1px solid #8c8f94; } table .type-astra_adv_header .title, table .type-astra-advanced-hook .title, .post-type-astra_adv_header .subsubsub a, .post-type-astra-advanced-hook .subsubsub a, .post-type-astra_adv_header table .column-title a, .post-type-astra-advanced-hook table .column-title a { color: #2271b1; } .post-type-astra_adv_header .subsubsub a.current, .post-type-astra-advanced-hook .subsubsub a.current { color: #000; } .ast-addon-inactive a.customize-module, .ast-addon-inactive a.advanced-module { pointer-events: none; } .flex-wrap { flex-wrap: wrap; } .text-red-400{ color: #f87171; } .ast-admin-top-bar-root a:focus, .ast-admin-top-bar-root a:active { box-shadow: none; } assets/css/admin-custom-rtl.css 0000666 00000014003 15165304350 0012553 0 ustar 00 .ast-admin-top-bar-root { font-family: Inter, sans-serif; } .mb-4 { margin-bottom: 1rem; } .justify-items-end { justify-items: end; } .w-max { width: max-content; } .mt-4 { margin-top: 1rem/* 16px */; } .pr-4 { padding-left: 1rem/* 16px */; } .ml-8 { margin-right: 2rem/* 32px */; } .astra-parent-field-false + .astra-child-field { pointer-events: none; opacity: 0.4; } .w-4\/5 { width: 80%; } .text-right { text-align: left; } .top-1\/2 { top: 50%; } .-translate-y-1\/2 { transform: translateY(-50%); } .left-3 { right: 0.75rem; } .pl-3 { padding-right: 0.75rem; } .pt-14 { padding-top: 3.5rem/* 56px */; } .gap-6 { gap: 1.5rem/* 24px */; } .-mb-11\.5 { margin-bottom: -2.875rem/* 46px */; } .min-h-24 { min-height: 6rem/* 96px */; } .border-r { border-left-width: 1px; } .border-slate-200 { --tw-border-opacity: 1; border-color: rgb(226 232 240 / var(--tw-border-opacity)); } a.bsf-core-license-form-btn { text-decoration: underline; color: #2271b1; } .ast-whitelabel-wrap .ast-admin_input-field { border: 1px solid #CBD5E1; padding: 6px 13px; border-radius: 6px; } .ast-licensing-wrap .ast-admin_license-input-field { border: 1px solid #CBD5E1; padding: 6px 36px; border-radius: 6px; width: 28.3125rem; } @media(max-width: 781px) { .ast-admin_license-input-field { width: 100%; } section.ast-whitelabel-wrap input, section.ast-whitelabel-wrap textarea { width: 100%; } div .tablet\:justify-items-start { justify-items: start; } } /* Rollback popup CSS compatibility. */ .ast-rollback__dialog { z-index: 99999; } .backdrop-blur-sm { --tw-backdrop-blur: blur(4px); -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); } .bg-gray-500 { --tw-bg-opacity: 1; background-color: rgb(107 114 128 / var(--tw-bg-opacity)); } .bg-opacity-75 { --tw-bg-opacity: 0.75; } .bg-red-50 { --tw-bg-opacity: 1; background-color: rgb(254 242 242 / var(--tw-bg-opacity)); } .stroke-red-600 { stroke: #dc2626; } .min-h-screen { min-height: 100vh; } .items-end { align-items: flex-end; } .ast-rollback__dialog .bg-red-600 { --tw-bg-opacity: 1; background-color: rgb(220 38 38 / var(--tw-bg-opacity)); } @media (min-width: 640px) { .ast-rollback__dialog .sm\:mx-0 { margin-right: 0px; margin-left: 0px; } .ast-rollback__dialog .sm\:w-16 { width: 4rem; } .ast-rollback__dialog .sm\:block { display: block; } .sm\:align-middle { vertical-align: middle; } .sm\:max-w-lg { max-width: 32rem; } .sm\:w-full { width: 100%; } .sm\:my-8 { margin-top: 2rem; margin-bottom: 2rem; } .sm\.pr-8 { padding-left: 2rem; } .sm\:h-16 { height: 4rem; } .sm\:text-left { text-align: right; } .sm\:ml-4 { margin-right: 1rem; } .sm\:ml-20 { margin-right: 5rem; } .sm\:mr-3 { margin-left: 0.75rem; } .sm\:inline-block { display: inline-block; } .sm\:h-screen { height: 100vh; } .sm\:pt-0 { padding-top: 0; } .sm\:pb-0 { padding-bottom: 0; } .sm\:min-h-15 { min-height: 3.9375rem/* 63px */; } .sm\:mb-0 { margin-bottom: 0; } } /* Post types compatibility CSS */ .post-type-astra-advanced-hook #wpcontent #wpbody #wpbody-content .wrap h1.wp-heading-inline, .post-type-astra_adv_header #wpcontent #wpbody #wpbody-content h1.wp-heading-inline, .post-type-astra-advanced-hook #wpcontent #wpbody #wpbody-content .wrap a.page-title-action, .post-type-astra_adv_header #wpcontent #wpbody #wpbody-content .wrap a.page-title-action { display: none; } .post-type-astra-advanced-hook #posts-filter .search-box, .post-type-astra_adv_header #posts-filter .search-box { display: flex; } .post-type-astra-advanced-hook .wrap, .post-type-astra_adv_header .wrap { margin-top: 45px; } .post-type-astra_adv_header #wpcontent, .post-type-astra-advanced-hook #wpcontent { padding-right: 0; } .post-type-astra-advanced-hook #wpbody-content, .post-type-astra_adv_header #wpbody-content { padding: 0 20px 65px 0; } .post-type-astra-advanced-hook table.wp-list-table, .post-type-astra_adv_header table.wp-list-table { position: relative; } .post-type-astra-advanced-hook .wrap div.notice:first-of-type, .post-type-astra_adv_header .wrap div.notice:first-of-type { margin-top: 2.5rem; } .post-type-astra-advanced-hook .tablenav, .post-type-astra_adv_header .tablenav { margin: 6px 0 9px; } .post-type-astra-advanced-hook #screen-options-wrap input:not([type=submit]), .post-type-astra_adv_header #screen-options-wrap input:not([type=submit]), table.wp-list-table input[type='checkbox'], table.wp-list-table input[type='radio'], table.wp-list-table input[type='checkbox']:hover, table.wp-list-table input[type='radio']:hover, table.wp-list-table input[type='checkbox']:focus, table.wp-list-table input[type='radio']:focus { background-color: #ffff; border: 1px solid #8c8f94; } table .type-astra_adv_header .title, table .type-astra-advanced-hook .title, .post-type-astra_adv_header .subsubsub a, .post-type-astra-advanced-hook .subsubsub a, .post-type-astra_adv_header table .column-title a, .post-type-astra-advanced-hook table .column-title a { color: #2271b1; } .post-type-astra_adv_header .subsubsub a.current, .post-type-astra-advanced-hook .subsubsub a.current { color: #000; } .ast-addon-inactive a.customize-module, .ast-addon-inactive a.advanced-module { pointer-events: none; } .flex-wrap { flex-wrap: wrap; } .text-red-400{ color: #f87171; } .ast-admin-top-bar-root a:focus, .ast-admin-top-bar-root a:active { box-shadow: none; } assets/build/dashboard-app.asset.php 0000666 00000000222 15165304350 0013503 0 ustar 00 <?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-element', 'wp-i18n'), 'version' => 'c512e9ce08799fc3ae25'); assets/build/dashboard-app.js 0000666 00000220154 15165304350 0012222 0 ustar 00 !function(){"use strict";var e={n:function(t){var a=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(a,{a:a}),a},d:function(t,a){for(var n in a)e.o(a,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:a[n]})},o:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}},t=window.wp.element,a=window.React,n=window.wp.i18n,r={"version-control":(0,t.createElement)("svg",{className:"flex-shrink-0 mr-4 fill-inherit",stroke:"none",width:"16",height:"20",viewBox:"0 0 16 20",xmlns:"http://www.w3.org/2000/svg"},(0,t.createElement)("path",{d:"M5.70711 11.2929C5.31658 10.9024 4.68342 10.9024 4.29289 11.2929C3.90237 11.6834 3.90237 12.3166 4.29289 12.7071L5.70711 11.2929ZM7 14L6.29289 14.7071C6.68342 15.0976 7.31658 15.0976 7.70711 14.7071L7 14ZM11.7071 10.7071C12.0976 10.3166 12.0976 9.68342 11.7071 9.29289C11.3166 8.90237 10.6834 8.90237 10.2929 9.29289L11.7071 10.7071ZM14 5V17H16V5H14ZM13 18H3V20H13V18ZM2 17V5H0V17H2ZM3 4H5V2H3V4ZM11 4H13V2H11V4ZM3 18C2.44772 18 2 17.5523 2 17H0C0 18.6569 1.34315 20 3 20V18ZM14 17C14 17.5523 13.5523 18 13 18V20C14.6569 20 16 18.6569 16 17H14ZM16 5C16 3.34315 14.6569 2 13 2V4C13.5523 4 14 4.44772 14 5H16ZM2 5C2 4.44772 2.44772 4 3 4V2C1.34315 2 0 3.34315 0 5H2ZM4.29289 12.7071L6.29289 14.7071L7.70711 13.2929L5.70711 11.2929L4.29289 12.7071ZM7.70711 14.7071L11.7071 10.7071L10.2929 9.29289L6.29289 13.2929L7.70711 14.7071ZM7 2H9V0H7V2ZM9 4H7V6H9V4ZM7 4C6.44772 4 6 3.55228 6 3H4C4 4.65685 5.34315 6 7 6V4ZM10 3C10 3.55228 9.55228 4 9 4V6C10.6569 6 12 4.65685 12 3H10ZM9 2C9.55228 2 10 2.44772 10 3H12C12 1.34315 10.6569 0 9 0V2ZM7 0C5.34315 0 4 1.34315 4 3H6C6 2.44772 6.44772 2 7 2V0Z"})),"white-label":(0,t.createElement)("svg",{className:"flex-shrink-0 mr-4 stroke-inherit",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},(0,t.createElement)("path",{d:"M6.99963 10.0006L8.99963 12.0006L12.9996 8.00061M5.83437 2.69766C6.55191 2.6404 7.23309 2.35824 7.78095 1.89136C9.05944 0.801839 10.9398 0.801839 12.2183 1.89136C12.7662 2.35824 13.4474 2.6404 14.1649 2.69766C15.8393 2.83128 17.169 4.16092 17.3026 5.83535C17.3598 6.55288 17.642 7.23407 18.1089 7.78193C19.1984 9.06042 19.1984 10.9408 18.1089 12.2193C17.642 12.7672 17.3598 13.4483 17.3026 14.1659C17.169 15.8403 15.8393 17.1699 14.1649 17.3036C13.4474 17.3608 12.7662 17.643 12.2183 18.1099C10.9398 19.1994 9.05944 19.1994 7.78095 18.1099C7.23309 17.643 6.55191 17.3608 5.83437 17.3036C4.15994 17.1699 2.8303 15.8403 2.69668 14.1659C2.63942 13.4483 2.35727 12.7672 1.89038 12.2193C0.800862 10.9408 0.800862 9.06042 1.89038 7.78193C2.35727 7.23407 2.63942 6.55288 2.69668 5.83535C2.8303 4.16092 4.15994 2.83128 5.83437 2.69766Z",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}))};function l(e,t,...a){if(e in t){let n=t[e];return"function"==typeof n?n(...a):n}let n=new Error(`Tried to handle "${e}" but there is no handler defined. Only defined handlers are: ${Object.keys(t).map((e=>`"${e}"`)).join(", ")}.`);throw Error.captureStackTrace&&Error.captureStackTrace(n,l),n}var s,o=((s=o||{})[s.None=0]="None",s[s.RenderStrategy=1]="RenderStrategy",s[s.Static=2]="Static",s),i=(e=>(e[e.Unmount=0]="Unmount",e[e.Hidden=1]="Hidden",e))(i||{});function d({ourProps:e,theirProps:t,slot:a,defaultTag:n,features:r,visible:s=!0,name:o}){let i=u(t,e);if(s)return c(i,a,n,o);let d=null!=r?r:0;if(2&d){let{static:e=!1,...t}=i;if(e)return c(t,a,n,o)}if(1&d){let{unmount:e=!0,...t}=i;return l(e?0:1,{0(){return null},1(){return c({...t,hidden:!0,style:{display:"none"}},a,n,o)}})}return c(i,a,n,o)}function c(e,t={},n,r){let{as:l=n,children:s,refName:o="ref",...i}=p(e,["unmount","static"]),d=void 0!==e.ref?{[o]:e.ref}:{},c="function"==typeof s?s(t):s;i.className&&"function"==typeof i.className&&(i.className=i.className(t));let m={};if(t){let e=!1,a=[];for(let[n,r]of Object.entries(t))"boolean"==typeof r&&(e=!0),!0===r&&a.push(n);e&&(m["data-headlessui-state"]=a.join(" "))}if(l===a.Fragment&&Object.keys(f(i)).length>0){if(!(0,a.isValidElement)(c)||Array.isArray(c)&&c.length>1)throw new Error(['Passing props on "Fragment"!',"",`The current component <${r} /> is rendering a "Fragment".`,"However we need to passthrough the following props:",Object.keys(i).map((e=>` - ${e}`)).join("\n"),"","You can apply a few solutions:",['Add an `as="..."` prop, to ensure that we render an actual element instead of a "Fragment".',"Render a single element as the child so that we can forward the props onto that element."].map((e=>` - ${e}`)).join("\n")].join("\n"));return(0,a.cloneElement)(c,Object.assign({},u(c.props,f(p(i,["ref"]))),m,d,function(...e){return{ref:e.every((e=>null==e))?void 0:t=>{for(let a of e)null!=a&&("function"==typeof a?a(t):a.current=t)}}}(c.ref,d.ref)))}return(0,a.createElement)(l,Object.assign({},p(i,["ref"]),l!==a.Fragment&&d,l!==a.Fragment&&m),c)}function u(...e){if(0===e.length)return{};if(1===e.length)return e[0];let t={},a={};for(let n of e)for(let e in n)e.startsWith("on")&&"function"==typeof n[e]?(null!=a[e]||(a[e]=[]),a[e].push(n[e])):t[e]=n[e];if(t.disabled||t["aria-disabled"])return Object.assign(t,Object.fromEntries(Object.keys(a).map((e=>[e,void 0]))));for(let e in a)Object.assign(t,{[e](t,...n){let r=a[e];for(let e of r){if((t instanceof Event||(null==t?void 0:t.nativeEvent)instanceof Event)&&t.defaultPrevented)return;e(t,...n)}}});return t}function m(e){var t;return Object.assign((0,a.forwardRef)(e),{displayName:null!=(t=e.displayName)?t:e.name})}function f(e){let t=Object.assign({},e);for(let e in t)void 0===t[e]&&delete t[e];return t}function p(e,t=[]){let a=Object.assign({},e);for(let e of t)e in a&&delete a[e];return a}let h=(0,a.createContext)(null);h.displayName="OpenClosedContext";var g=(e=>(e[e.Open=0]="Open",e[e.Closed=1]="Closed",e))(g||{});function b(){return(0,a.useContext)(h)}function v({value:e,children:t}){return a.createElement(h.Provider,{value:e},t)}const _="undefined"==typeof window||"undefined"==typeof document;let w=_?a.useEffect:a.useLayoutEffect;function E(){let e=(0,a.useRef)(!1);return w((()=>(e.current=!0,()=>{e.current=!1})),[]),e}function x(e){let t=(0,a.useRef)(e);return w((()=>{t.current=e}),[e]),t}let y={serverHandoffComplete:!1};function k(){let[e,t]=(0,a.useState)(y.serverHandoffComplete);return(0,a.useEffect)((()=>{!0!==e&&t(!0)}),[e]),(0,a.useEffect)((()=>{!1===y.serverHandoffComplete&&(y.serverHandoffComplete=!0)}),[]),e}let N=function(e){let t=x(e);return a.useCallback(((...e)=>t.current(...e)),[t])},C=Symbol();function S(e,t=!0){return Object.assign(e,{[C]:t})}function F(...e){let t=(0,a.useRef)(e);(0,a.useEffect)((()=>{t.current=e}),[e]);let n=N((e=>{for(let a of t.current)null!=a&&("function"==typeof a?a(e):a.current=e)}));return e.every((e=>null==e||(null==e?void 0:e[C])))?void 0:n}function T(e){"function"==typeof queueMicrotask?queueMicrotask(e):Promise.resolve().then(e).catch((e=>setTimeout((()=>{throw e}))))}function P(){let e=[],t=[],a={enqueue(e){t.push(e)},addEventListener(e,t,n,r){return e.addEventListener(t,n,r),a.add((()=>e.removeEventListener(t,n,r)))},requestAnimationFrame(...e){let t=requestAnimationFrame(...e);return a.add((()=>cancelAnimationFrame(t)))},nextFrame(...e){return a.requestAnimationFrame((()=>a.requestAnimationFrame(...e)))},setTimeout(...e){let t=setTimeout(...e);return a.add((()=>clearTimeout(t)))},microTask(...e){let t={current:!0};return T((()=>{t.current&&e[0]()})),a.add((()=>{t.current=!1}))},add(t){return e.push(t),()=>{let a=e.indexOf(t);if(a>=0){let[t]=e.splice(a,1);t()}}},dispose(){for(let t of e.splice(0))t()},async workQueue(){for(let e of t.splice(0))await e()}};return a}function L(e,...t){e&&t.length>0&&e.classList.add(...t)}function R(e,...t){e&&t.length>0&&e.classList.remove(...t)}var A,D=((A=D||{}).Ended="ended",A.Cancelled="cancelled",A);function M(){let[e]=(0,a.useState)(P);return(0,a.useEffect)((()=>()=>e.dispose()),[e]),e}function j({container:e,direction:t,classes:a,onStart:n,onStop:r}){let s=E(),o=M(),i=x(t);w((()=>{let t=P();o.add(t.dispose);let d=e.current;if(d&&"idle"!==i.current&&s.current)return t.dispose(),n.current(i.current),t.add(function(e,t,a,n){let r=a?"enter":"leave",s=P(),o=void 0!==n?function(e){let t={called:!1};return(...a)=>{if(!t.called)return t.called=!0,e(...a)}}(n):()=>{};"enter"===r&&(e.removeAttribute("hidden"),e.style.display="");let i=l(r,{enter:()=>t.enter,leave:()=>t.leave}),d=l(r,{enter:()=>t.enterTo,leave:()=>t.leaveTo}),c=l(r,{enter:()=>t.enterFrom,leave:()=>t.leaveFrom});return R(e,...t.enter,...t.enterTo,...t.enterFrom,...t.leave,...t.leaveFrom,...t.leaveTo,...t.entered),L(e,...i,...c),s.nextFrame((()=>{R(e,...c),L(e,...d),function(e,t){let a=P();if(!e)return a.dispose;let{transitionDuration:n,transitionDelay:r}=getComputedStyle(e),[l,s]=[n,r].map((e=>{let[t=0]=e.split(",").filter(Boolean).map((e=>e.includes("ms")?parseFloat(e):1e3*parseFloat(e))).sort(((e,t)=>t-e));return t}));if(l+s!==0){let n=[];n.push(a.addEventListener(e,"transitionrun",(r=>{r.target===r.currentTarget&&(n.splice(0).forEach((e=>e())),n.push(a.addEventListener(e,"transitionend",(e=>{e.target===e.currentTarget&&(t("ended"),n.splice(0).forEach((e=>e())))})),a.addEventListener(e,"transitioncancel",(e=>{e.target===e.currentTarget&&(t("cancelled"),n.splice(0).forEach((e=>e())))}))))})))}else t("ended");a.add((()=>t("cancelled"))),a.dispose}(e,(a=>("ended"===a&&(R(e,...i),L(e,...t.entered)),o(a))))})),s.dispose}(d,a.current,"enter"===i.current,(e=>{t.dispose(),l(e,{[D.Ended](){r.current(i.current)},[D.Cancelled]:()=>{}})}))),t.dispose}),[t])}function O(e=""){return e.split(" ").filter((e=>e.trim().length>1))}let H=(0,a.createContext)(null);H.displayName="TransitionContext";var V,B=((V=B||{}).Visible="visible",V.Hidden="hidden",V);let I=(0,a.createContext)(null);function U(e){return"children"in e?U(e.children):e.current.filter((({el:e})=>null!==e.current)).filter((({state:e})=>"visible"===e)).length>0}function $(e,t){let n=x(e),r=(0,a.useRef)([]),s=E(),o=M(),d=N(((e,t=i.Hidden)=>{let a=r.current.findIndex((({el:t})=>t===e));-1!==a&&(l(t,{[i.Unmount](){r.current.splice(a,1)},[i.Hidden](){r.current[a].state="hidden"}}),o.microTask((()=>{var e;!U(r)&&s.current&&(null==(e=n.current)||e.call(n))})))})),c=N((e=>{let t=r.current.find((({el:t})=>t===e));return t?"visible"!==t.state&&(t.state="visible"):r.current.push({el:e,state:"visible"}),()=>d(e,i.Unmount)})),u=(0,a.useRef)([]),m=(0,a.useRef)(Promise.resolve()),f=(0,a.useRef)({enter:[],leave:[],idle:[]}),p=N(((e,a,n)=>{u.current.splice(0),t&&(t.chains.current[a]=t.chains.current[a].filter((([t])=>t!==e))),null==t||t.chains.current[a].push([e,new Promise((e=>{u.current.push(e)}))]),null==t||t.chains.current[a].push([e,new Promise((e=>{Promise.all(f.current[a].map((([e,t])=>t))).then((()=>e()))}))]),"enter"===a?m.current=m.current.then((()=>null==t?void 0:t.wait.current)).then((()=>n(a))):n(a)})),h=N(((e,t,a)=>{Promise.all(f.current[t].splice(0).map((([e,t])=>t))).then((()=>{var e;null==(e=u.current.shift())||e()})).then((()=>a(t)))}));return(0,a.useMemo)((()=>({children:r,register:c,unregister:d,onStart:p,onStop:h,wait:m,chains:f})),[c,d,r,p,h,f,m])}function Z(){}I.displayName="NestingContext";let W=["beforeEnter","afterEnter","beforeLeave","afterLeave"];function z(e){var t;let a={};for(let n of W)a[n]=null!=(t=e[n])?t:Z;return a}let q=o.RenderStrategy,Y=m((function(e,t){let{beforeEnter:n,afterEnter:r,beforeLeave:s,afterLeave:o,enter:c,enterFrom:u,enterTo:m,entered:f,leave:p,leaveFrom:h,leaveTo:b,..._}=e,w=(0,a.useRef)(null),E=F(w,t),y=_.unmount?i.Unmount:i.Hidden,{show:C,appear:S,initial:T}=function(){let e=(0,a.useContext)(H);if(null===e)throw new Error("A <Transition.Child /> is used but it is missing a parent <Transition /> or <Transition.Root />.");return e}(),[P,L]=(0,a.useState)(C?"visible":"hidden"),R=function(){let e=(0,a.useContext)(I);if(null===e)throw new Error("A <Transition.Child /> is used but it is missing a parent <Transition /> or <Transition.Root />.");return e}(),{register:A,unregister:D}=R,M=(0,a.useRef)(null);(0,a.useEffect)((()=>A(w)),[A,w]),(0,a.useEffect)((()=>{if(y===i.Hidden&&w.current)return C&&"visible"!==P?void L("visible"):l(P,{hidden:()=>D(w),visible:()=>A(w)})}),[P,w,A,D,C,y]);let V=x({enter:O(c),enterFrom:O(u),enterTo:O(m),entered:O(f),leave:O(p),leaveFrom:O(h),leaveTo:O(b)}),B=function(e){let t=(0,a.useRef)(z(e));return(0,a.useEffect)((()=>{t.current=z(e)}),[e]),t}({beforeEnter:n,afterEnter:r,beforeLeave:s,afterLeave:o}),Z=k();(0,a.useEffect)((()=>{if(Z&&"visible"===P&&null===w.current)throw new Error("Did you forget to passthrough the `ref` to the actual DOM node?")}),[w,P,Z]);let W=T&&!S,Y=!Z||W||M.current===C?"idle":C?"enter":"leave",G=N((e=>l(e,{enter:()=>B.current.beforeEnter(),leave:()=>B.current.beforeLeave(),idle:()=>{}}))),J=N((e=>l(e,{enter:()=>B.current.afterEnter(),leave:()=>B.current.afterLeave(),idle:()=>{}}))),K=$((()=>{L("hidden"),D(w)}),R);j({container:w,classes:V,direction:Y,onStart:x((e=>{K.onStart(w,e,G)})),onStop:x((e=>{K.onStop(w,e,J),"leave"===e&&!U(K)&&(L("hidden"),D(w))}))}),(0,a.useEffect)((()=>{!W||(y===i.Hidden?M.current=null:M.current=C)}),[C,W,P]);let Q=_,X={ref:E};return a.createElement(I.Provider,{value:K},a.createElement(v,{value:l(P,{visible:g.Open,hidden:g.Closed})},d({ourProps:X,theirProps:Q,defaultTag:"div",features:q,visible:"visible"===P,name:"Transition.Child"})))})),G=m((function(e,t){let{show:n,appear:r=!1,unmount:s,...o}=e,i=(0,a.useRef)(null),c=F(i,t);k();let u=b();if(void 0===n&&null!==u&&(n=l(u,{[g.Open]:!0,[g.Closed]:!1})),![!0,!1].includes(n))throw new Error("A <Transition /> is used but it is missing a `show={true | false}` prop.");let[m,f]=(0,a.useState)(n?"visible":"hidden"),p=$((()=>{f("hidden")})),[h,v]=(0,a.useState)(!0),_=(0,a.useRef)([n]);w((()=>{!1!==h&&_.current[_.current.length-1]!==n&&(_.current.push(n),v(!1))}),[_,n]);let E=(0,a.useMemo)((()=>({show:n,appear:r,initial:h})),[n,r,h]);(0,a.useEffect)((()=>{if(n)f("visible");else if(U(p)){let e=i.current;if(!e)return;let t=e.getBoundingClientRect();0===t.x&&0===t.y&&0===t.width&&0===t.height&&f("hidden")}else f("hidden")}),[n,p]);let x={unmount:s};return a.createElement(I.Provider,{value:p},a.createElement(H.Provider,{value:E},d({ourProps:{...x,as:a.Fragment,children:a.createElement(Y,{ref:c,...x,...o})},theirProps:{},defaultTag:a.Fragment,features:q,visible:"visible"===m,name:"Transition"})))})),J=m((function(e,t){let n=null!==(0,a.useContext)(H),r=null!==b();return a.createElement(a.Fragment,null,!n&&r?a.createElement(G,{ref:t,...e}):a.createElement(Y,{ref:t,...e}))})),K=Object.assign(G,{Child:J,Root:G});var Q,X=(e=>(e.Space=" ",e.Enter="Enter",e.Escape="Escape",e.Backspace="Backspace",e.Delete="Delete",e.ArrowLeft="ArrowLeft",e.ArrowUp="ArrowUp",e.ArrowRight="ArrowRight",e.ArrowDown="ArrowDown",e.Home="Home",e.End="End",e.PageUp="PageUp",e.PageDown="PageDown",e.Tab="Tab",e))(X||{});function ee(e){let t=e.parentElement,a=null;for(;t&&!(t instanceof HTMLFieldSetElement);)t instanceof HTMLLegendElement&&(a=t),t=t.parentElement;let n=""===(null==t?void 0:t.getAttribute("disabled"));return(!n||!function(e){if(!e)return!1;let t=e.previousElementSibling;for(;null!==t;){if(t instanceof HTMLLegendElement)return!1;t=t.previousElementSibling}return!0}(a))&&n}let te=0;function ae(){return++te}let ne=null!=(Q=a.useId)?Q:function(){let e=k(),[t,n]=a.useState(e?ae:null);return w((()=>{null===t&&n(ae())}),[t]),null!=t?""+t:void 0};var re=(e=>(e[e.None=1]="None",e[e.Focusable=2]="Focusable",e[e.Hidden=4]="Hidden",e))(re||{});let le=m((function(e,t){let{features:a=1,...n}=e;return d({ourProps:{ref:t,"aria-hidden":2==(2&a)||void 0,style:{position:"fixed",top:1,left:1,width:1,height:0,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:"0",...4==(4&a)&&2!=(2&a)&&{display:"none"}}},theirProps:n,slot:{},defaultTag:"div",name:"Hidden"})}));function se(e){return _?null:e instanceof Node?e.ownerDocument:null!=e&&e.hasOwnProperty("current")&&e.current instanceof Node?e.current.ownerDocument:document}let oe=["[contentEditable=true]","[tabindex]","a[href]","area[href]","button:not([disabled])","iframe","input:not([disabled])","select:not([disabled])","textarea:not([disabled])"].map((e=>`${e}:not([tabindex='-1'])`)).join(",");var ie=(e=>(e[e.First=1]="First",e[e.Previous=2]="Previous",e[e.Next=4]="Next",e[e.Last=8]="Last",e[e.WrapAround=16]="WrapAround",e[e.NoScroll=32]="NoScroll",e))(ie||{}),de=(e=>(e[e.Error=0]="Error",e[e.Overflow=1]="Overflow",e[e.Success=2]="Success",e[e.Underflow=3]="Underflow",e))(de||{}),ce=(e=>(e[e.Previous=-1]="Previous",e[e.Next=1]="Next",e))(ce||{});var ue=(e=>(e[e.Strict=0]="Strict",e[e.Loose=1]="Loose",e))(ue||{});function me(e){null==e||e.focus({preventScroll:!0})}let fe=["textarea","input"].join(",");function pe(e,t,a=!0,n=null){let r=Array.isArray(e)?e.length>0?e[0].ownerDocument:document:e.ownerDocument,l=Array.isArray(e)?a?function(e,t=(e=>e)){return e.slice().sort(((e,a)=>{let n=t(e),r=t(a);if(null===n||null===r)return 0;let l=n.compareDocumentPosition(r);return l&Node.DOCUMENT_POSITION_FOLLOWING?-1:l&Node.DOCUMENT_POSITION_PRECEDING?1:0}))}(e):e:function(e=document.body){return null==e?[]:Array.from(e.querySelectorAll(oe))}(e);n=null!=n?n:r.activeElement;let s,o=(()=>{if(5&t)return 1;if(10&t)return-1;throw new Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),i=(()=>{if(1&t)return 0;if(2&t)return Math.max(0,l.indexOf(n))-1;if(4&t)return Math.max(0,l.indexOf(n))+1;if(8&t)return l.length-1;throw new Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),d=32&t?{preventScroll:!0}:{},c=0,u=l.length;do{if(c>=u||c+u<=0)return 0;let e=i+c;if(16&t)e=(e+u)%u;else{if(e<0)return 3;if(e>=u)return 1}s=l[e],null==s||s.focus(d),c+=o}while(s!==r.activeElement);return 6&t&&function(e){var t,a;return null!=(a=null==(t=null==e?void 0:e.matches)?void 0:t.call(e,fe))&&a}(s)&&s.select(),s.hasAttribute("tabindex")||s.setAttribute("tabindex","0"),2}var he=(e=>(e[e.Forwards=0]="Forwards",e[e.Backwards=1]="Backwards",e))(he||{});function ge(...e){return(0,a.useMemo)((()=>se(...e)),[...e])}function be(e,t,n,r){let l=x(n);(0,a.useEffect)((()=>{function a(e){l.current(e)}return(e=null!=e?e:window).addEventListener(t,a,r),()=>e.removeEventListener(t,a,r)}),[e,t,r])}function ve(e,t){let n=(0,a.useRef)([]),r=N(e);(0,a.useEffect)((()=>{let e=[...n.current];for(let[a,l]of t.entries())if(n.current[a]!==l){let a=r(t,e);return n.current=t,a}}),[r,...t])}var _e=(e=>(e[e.None=1]="None",e[e.InitialFocus=2]="InitialFocus",e[e.TabLock=4]="TabLock",e[e.FocusLock=8]="FocusLock",e[e.RestoreFocus=16]="RestoreFocus",e[e.All=30]="All",e))(_e||{});let we=Object.assign(m((function(e,t){let n=(0,a.useRef)(null),r=F(n,t),{initialFocus:s,containers:o,features:i=30,...c}=e;k()||(i=1);let u=ge(n);!function({ownerDocument:e},t){let n=(0,a.useRef)(null);be(null==e?void 0:e.defaultView,"focusout",(e=>{!t||n.current||(n.current=e.target)}),!0),ve((()=>{t||((null==e?void 0:e.activeElement)===(null==e?void 0:e.body)&&me(n.current),n.current=null)}),[t]);let r=(0,a.useRef)(!1);(0,a.useEffect)((()=>(r.current=!1,()=>{r.current=!0,T((()=>{!r.current||(me(n.current),n.current=null)}))})),[])}({ownerDocument:u},Boolean(16&i));let m=function({ownerDocument:e,container:t,initialFocus:n},r){let l=(0,a.useRef)(null),s=E();return ve((()=>{if(!r)return;let a=t.current;!a||T((()=>{if(!s.current)return;let t=null==e?void 0:e.activeElement;if(null!=n&&n.current){if((null==n?void 0:n.current)===t)return void(l.current=t)}else if(a.contains(t))return void(l.current=t);null!=n&&n.current?me(n.current):pe(a,ie.First)===de.Error&&console.warn("There are no focusable elements inside the <FocusTrap />"),l.current=null==e?void 0:e.activeElement}))}),[r]),l}({ownerDocument:u,container:n,initialFocus:s},Boolean(2&i));!function({ownerDocument:e,container:t,containers:a,previousActiveElement:n},r){let l=E();be(null==e?void 0:e.defaultView,"focus",(e=>{if(!r||!l.current)return;let s=new Set(null==a?void 0:a.current);s.add(t);let o=n.current;if(!o)return;let i=e.target;i&&i instanceof HTMLElement?function(e,t){var a;for(let n of e)if(null!=(a=n.current)&&a.contains(t))return!0;return!1}(s,i)?(n.current=i,me(i)):(e.preventDefault(),e.stopPropagation(),me(o)):me(n.current)}),!0)}({ownerDocument:u,container:n,containers:o,previousActiveElement:m},Boolean(8&i));let f=function(){let e=(0,a.useRef)(0);return function(e,t,n){let r=x(t);(0,a.useEffect)((()=>{function t(e){r.current(e)}return window.addEventListener(e,t,n),()=>window.removeEventListener(e,t,n)}),[e,n])}("keydown",(t=>{"Tab"===t.key&&(e.current=t.shiftKey?1:0)}),!0),e}(),p=N((()=>{let e=n.current;!e||l(f.current,{[he.Forwards]:()=>pe(e,ie.First),[he.Backwards]:()=>pe(e,ie.Last)})})),h={ref:r};return a.createElement(a.Fragment,null,Boolean(4&i)&&a.createElement(le,{as:"button",type:"button",onFocus:p,features:re.Focusable}),d({ourProps:h,theirProps:c,defaultTag:"div",name:"FocusTrap"}),Boolean(4&i)&&a.createElement(le,{as:"button",type:"button",onFocus:p,features:re.Focusable}))})),{features:_e}),Ee=new Set,xe=new Map;function ye(e){e.setAttribute("aria-hidden","true"),e.inert=!0}function ke(e){let t=xe.get(e);!t||(null===t["aria-hidden"]?e.removeAttribute("aria-hidden"):e.setAttribute("aria-hidden",t["aria-hidden"]),e.inert=t.inert)}var Ne=window.ReactDOM;let Ce=(0,a.createContext)(!1);function Se(){return(0,a.useContext)(Ce)}function Fe(e){return a.createElement(Ce.Provider,{value:e.force},e.children)}let Te=a.Fragment,Pe=m((function(e,t){let n=e,r=(0,a.useRef)(null),l=F(S((e=>{r.current=e})),t),s=ge(r),o=function(e){let t=Se(),n=(0,a.useContext)(Re),r=ge(e),[l,s]=(0,a.useState)((()=>{if(!t&&null!==n||_)return null;let e=null==r?void 0:r.getElementById("headlessui-portal-root");if(e)return e;if(null===r)return null;let a=r.createElement("div");return a.setAttribute("id","headlessui-portal-root"),r.body.appendChild(a)}));return(0,a.useEffect)((()=>{null!==l&&(null!=r&&r.body.contains(l)||null==r||r.body.appendChild(l))}),[l,r]),(0,a.useEffect)((()=>{t||null!==n&&s(n.current)}),[n,s,t]),l}(r),[i]=(0,a.useState)((()=>{var e;return _?null:null!=(e=null==s?void 0:s.createElement("div"))?e:null})),c=k(),u=(0,a.useRef)(!1);return w((()=>{if(u.current=!1,o&&i)return o.contains(i)||(i.setAttribute("data-headlessui-portal",""),o.appendChild(i)),()=>{u.current=!0,T((()=>{var e;!u.current||!o||!i||(o.removeChild(i),o.childNodes.length<=0&&(null==(e=o.parentElement)||e.removeChild(o)))}))}}),[o,i]),c&&o&&i?(0,Ne.createPortal)(d({ourProps:{ref:l},theirProps:n,defaultTag:Te,name:"Portal"}),i):null})),Le=a.Fragment,Re=(0,a.createContext)(null),Ae=m((function(e,t){let{target:n,...r}=e,l={ref:F(t)};return a.createElement(Re.Provider,{value:n},d({ourProps:l,theirProps:r,defaultTag:Le,name:"Popover.Group"}))})),De=Object.assign(Pe,{Group:Ae}),Me=(0,a.createContext)(null);function je(){let e=(0,a.useContext)(Me);if(null===e){let e=new Error("You used a <Description /> component, but it is not inside a relevant parent.");throw Error.captureStackTrace&&Error.captureStackTrace(e,je),e}return e}function Oe(){let[e,t]=(0,a.useState)([]);return[e.length>0?e.join(" "):void 0,(0,a.useMemo)((()=>function(e){let n=N((e=>(t((t=>[...t,e])),()=>t((t=>{let a=t.slice(),n=a.indexOf(e);return-1!==n&&a.splice(n,1),a}))))),r=(0,a.useMemo)((()=>({register:n,slot:e.slot,name:e.name,props:e.props})),[n,e.slot,e.name,e.props]);return a.createElement(Me.Provider,{value:r},e.children)}),[t])]}let He=m((function(e,t){let a=je(),n=`headlessui-description-${ne()}`,r=F(t);w((()=>a.register(n)),[n,a.register]);let l=e;return d({ourProps:{ref:r,...a.props,id:n},theirProps:l,slot:a.slot||{},defaultTag:"p",name:a.name||"Description"})})),Ve=(0,a.createContext)((()=>{}));Ve.displayName="StackContext";var Be=(e=>(e[e.Add=0]="Add",e[e.Remove=1]="Remove",e))(Be||{});function Ie({children:e,onUpdate:t,type:n,element:r,enabled:l}){let s=(0,a.useContext)(Ve),o=N(((...e)=>{null==t||t(...e),s(...e)}));return w((()=>{let e=void 0===l||!0===l;return e&&o(0,n,r),()=>{e&&o(1,n,r)}}),[o,n,r,l]),a.createElement(Ve.Provider,{value:o},e)}function Ue(e,t,n){let r=x(t);(0,a.useEffect)((()=>{function t(e){r.current(e)}return document.addEventListener(e,t,n),()=>document.removeEventListener(e,t,n)}),[e,n])}var $e=(e=>(e[e.Open=0]="Open",e[e.Closed=1]="Closed",e))($e||{}),Ze=(e=>(e[e.SetTitleId=0]="SetTitleId",e))(Ze||{});let We={0(e,t){return e.titleId===t.id?e:{...e,titleId:t.id}}},ze=(0,a.createContext)(null);function qe(e){let t=(0,a.useContext)(ze);if(null===t){let t=new Error(`<${e} /> is missing a parent <Dialog /> component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,qe),t}return t}function Ye(e,t){return l(t.type,We,e,t)}ze.displayName="DialogContext";let Ge=o.RenderStrategy|o.Static,Je=m((function(e,t){let{open:n,onClose:r,initialFocus:s,__demoMode:o=!1,...i}=e,[c,u]=(0,a.useState)(0),m=b();void 0===n&&null!==m&&(n=l(m,{[g.Open]:!0,[g.Closed]:!1}));let f=(0,a.useRef)(new Set),p=(0,a.useRef)(null),h=F(p,t),v=(0,a.useRef)(null),_=ge(p),E=e.hasOwnProperty("open")||null!==m,x=e.hasOwnProperty("onClose");if(!E&&!x)throw new Error("You have to provide an `open` and an `onClose` prop to the `Dialog` component.");if(!E)throw new Error("You provided an `onClose` prop to the `Dialog`, but forgot an `open` prop.");if(!x)throw new Error("You provided an `open` prop to the `Dialog`, but forgot an `onClose` prop.");if("boolean"!=typeof n)throw new Error(`You provided an \`open\` prop to the \`Dialog\`, but the value is not a boolean. Received: ${n}`);if("function"!=typeof r)throw new Error(`You provided an \`onClose\` prop to the \`Dialog\`, but the value is not a function. Received: ${r}`);let y=n?0:1,[C,S]=(0,a.useReducer)(Ye,{titleId:null,descriptionId:null,panelRef:(0,a.createRef)()}),T=N((()=>r(!1))),L=N((e=>S({type:0,id:e}))),R=!!k()&&!o&&0===y,A=c>1,D=null!==(0,a.useContext)(ze),M=A?"parent":"leaf";(function(e,t=!0){w((()=>{if(!t||!e.current)return;let a=e.current,n=se(a);if(n){Ee.add(a);for(let e of xe.keys())e.contains(a)&&(ke(e),xe.delete(e));return n.querySelectorAll("body > *").forEach((e=>{if(e instanceof HTMLElement){for(let t of Ee)if(e.contains(t))return;1===Ee.size&&(xe.set(e,{"aria-hidden":e.getAttribute("aria-hidden"),inert:e.inert}),ye(e))}})),()=>{if(Ee.delete(a),Ee.size>0)n.querySelectorAll("body > *").forEach((e=>{if(e instanceof HTMLElement&&!xe.has(e)){for(let t of Ee)if(e.contains(t))return;xe.set(e,{"aria-hidden":e.getAttribute("aria-hidden"),inert:e.inert}),ye(e)}}));else for(let e of xe.keys())ke(e),xe.delete(e)}}}),[t])})(p,!!A&&R),function(e,t,n=!0){let r=(0,a.useRef)(!1);function s(a,n){if(!r.current||a.defaultPrevented)return;let s=function e(t){return"function"==typeof t?e(t()):Array.isArray(t)||t instanceof Set?t:[t]}(e),o=n(a);if(null!==o&&o.ownerDocument.documentElement.contains(o)){for(let e of s){if(null===e)continue;let t=e instanceof HTMLElement?e:e.current;if(null!=t&&t.contains(o))return}return!function(e,t=0){var a;return e!==(null==(a=se(e))?void 0:a.body)&&l(t,{0(){return e.matches(oe)},1(){let t=e;for(;null!==t;){if(t.matches(oe))return!0;t=t.parentElement}return!1}})}(o,ue.Loose)&&-1!==o.tabIndex&&a.preventDefault(),t(a,o)}}(0,a.useEffect)((()=>{requestAnimationFrame((()=>{r.current=n}))}),[n]);let o=(0,a.useRef)(null);Ue("mousedown",(e=>{var t,a;r.current&&(o.current=(null==(a=null==(t=e.composedPath)?void 0:t.call(e))?void 0:a[0])||e.target)}),!0),Ue("click",(e=>{!o.current||(s(e,(()=>o.current)),o.current=null)}),!0),Ue("blur",(e=>s(e,(()=>window.document.activeElement instanceof HTMLIFrameElement?window.document.activeElement:null))),!0)}((()=>{var e,t;return[...Array.from(null!=(e=null==_?void 0:_.querySelectorAll("body > *, [data-headlessui-portal]"))?e:[]).filter((e=>!(!(e instanceof HTMLElement)||e.contains(v.current)||C.panelRef.current&&e.contains(C.panelRef.current)))),null!=(t=C.panelRef.current)?t:p.current]}),T,R&&!A),be(null==_?void 0:_.defaultView,"keydown",(e=>{e.defaultPrevented||e.key===X.Escape&&0===y&&(A||(e.preventDefault(),e.stopPropagation(),T()))})),function(e,t){(0,a.useEffect)((()=>{var a;if(!t||!e)return;let n=P();function r(e,t,a){let r=e.style.getPropertyValue(t);return Object.assign(e.style,{[t]:a}),n.add((()=>{Object.assign(e.style,{[t]:r})}))}let l=e.documentElement,s=(null!=(a=e.defaultView)?a:window).innerWidth-l.clientWidth;if(r(l,"overflow","hidden"),s>0&&r(l,"paddingRight",s-(l.clientWidth-l.offsetWidth)+"px"),/iPhone/gi.test(window.navigator.platform)||/Mac/gi.test(window.navigator.platform)&&window.navigator.maxTouchPoints>0){let e=window.pageYOffset;r(l,"position","fixed"),r(l,"marginTop",`-${e}px`),r(l,"width","100%"),n.add((()=>window.scrollTo(0,e)))}return n.dispose}),[e,t])}(_,0===y&&!D),(0,a.useEffect)((()=>{if(0!==y||!p.current)return;let e=new IntersectionObserver((e=>{for(let t of e)0===t.boundingClientRect.x&&0===t.boundingClientRect.y&&0===t.boundingClientRect.width&&0===t.boundingClientRect.height&&T()}));return e.observe(p.current),()=>e.disconnect()}),[y,p,T]);let[j,O]=Oe(),H=`headlessui-dialog-${ne()}`,V=(0,a.useMemo)((()=>[{dialogState:y,close:T,setTitleId:L},C]),[y,C,T,L]),B=(0,a.useMemo)((()=>({open:0===y})),[y]),I={ref:h,id:H,role:"dialog","aria-modal":0===y||void 0,"aria-labelledby":C.titleId,"aria-describedby":j};return a.createElement(Ie,{type:"Dialog",enabled:0===y,element:p,onUpdate:N(((e,t,a)=>{"Dialog"===t&&l(e,{[Be.Add](){f.current.add(a),u((e=>e+1))},[Be.Remove](){f.current.add(a),u((e=>e-1))}})}))},a.createElement(Fe,{force:!0},a.createElement(De,null,a.createElement(ze.Provider,{value:V},a.createElement(De.Group,{target:p},a.createElement(Fe,{force:!1},a.createElement(O,{slot:B,name:"Dialog.Description"},a.createElement(we,{initialFocus:s,containers:f,features:R?l(M,{parent:we.features.RestoreFocus,leaf:we.features.All&~we.features.FocusLock}):we.features.None},d({ourProps:I,theirProps:i,slot:B,defaultTag:"div",features:Ge,visible:0===y,name:"Dialog"})))))))),a.createElement(le,{features:re.Hidden,ref:v}))})),Ke=m((function(e,t){let[{dialogState:n,close:r}]=qe("Dialog.Overlay");return d({ourProps:{ref:F(t),id:`headlessui-dialog-overlay-${ne()}`,"aria-hidden":!0,onClick:N((e=>{if(e.target===e.currentTarget){if(ee(e.currentTarget))return e.preventDefault();e.preventDefault(),e.stopPropagation(),r()}}))},theirProps:e,slot:(0,a.useMemo)((()=>({open:0===n})),[n]),defaultTag:"div",name:"Dialog.Overlay"})})),Qe=m((function(e,t){let[{dialogState:n},r]=qe("Dialog.Backdrop"),l=F(t),s=`headlessui-dialog-backdrop-${ne()}`;(0,a.useEffect)((()=>{if(null===r.panelRef.current)throw new Error("A <Dialog.Backdrop /> component is being used, but a <Dialog.Panel /> component is missing.")}),[r.panelRef]);let o=(0,a.useMemo)((()=>({open:0===n})),[n]);return a.createElement(Fe,{force:!0},a.createElement(De,null,d({ourProps:{ref:l,id:s,"aria-hidden":!0},theirProps:e,slot:o,defaultTag:"div",name:"Dialog.Backdrop"})))})),Xe=m((function(e,t){let[{dialogState:n},r]=qe("Dialog.Panel"),l=F(t,r.panelRef),s=`headlessui-dialog-panel-${ne()}`,o=(0,a.useMemo)((()=>({open:0===n})),[n]);return d({ourProps:{ref:l,id:s,onClick:N((e=>{e.stopPropagation()}))},theirProps:e,slot:o,defaultTag:"div",name:"Dialog.Panel"})})),et=m((function(e,t){let[{dialogState:n,setTitleId:r}]=qe("Dialog.Title"),l=`headlessui-dialog-title-${ne()}`,s=F(t);(0,a.useEffect)((()=>(r(l),()=>r(null))),[l,r]);let o=(0,a.useMemo)((()=>({open:0===n})),[n]);return d({ourProps:{ref:s,id:l},theirProps:e,slot:o,defaultTag:"h2",name:"Dialog.Title"})})),tt=Object.assign(Je,{Backdrop:Qe,Panel:Xe,Overlay:Ke,Title:et,Description:He});var at=e=>{const{openPopup:r,setopenPopup:l,previousVersionSelect:s,productSelect:o}=e,[i,d]=(0,a.useState)(r);let c=astra_addon_admin.rollback_theme_name;"astra-addon"===o&&(c=astra_addon_admin.rollback_plugin_name);const u=(0,a.useRef)(null);return(0,a.useEffect)((()=>{d(r)}),[r]),(0,t.createElement)(K.Root,{show:i,as:a.Fragment},(0,t.createElement)(tt,{as:"div",className:"ast-rollback__dialog fixed backdrop-blur-sm inset-0 overflow-y-auto",initialFocus:u,onClose:d},(0,t.createElement)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"},(0,t.createElement)(K.Child,{as:a.Fragment,enter:"ease-out duration-300",enterFrom:"opacity-0",enterTo:"opacity-100",leave:"ease-in duration-200",leaveFrom:"opacity-100",leaveTo:"opacity-0"},(0,t.createElement)("div",{className:"fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"})),(0,t.createElement)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true"},""),(0,t.createElement)(K.Child,{as:a.Fragment,enter:"ease-out duration-300",enterFrom:"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95",enterTo:"opacity-100 translate-y-0 sm:scale-100",leave:"ease-in duration-200",leaveFrom:"opacity-100 translate-y-0 sm:scale-100",leaveTo:"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"},(0,t.createElement)("div",{className:"inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6"},(0,t.createElement)("div",{className:"sm:flex sm:items-start"},(0,t.createElement)("div",{className:"mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-50 sm:mx-0 sm:h-16 sm:w-16"},(0,t.createElement)("svg",{className:"h-8 w-8 stroke-red-600",viewBox:"0 0 34 33",fill:"none",xmlns:"http://www.w3.org/2000/svg"},(0,t.createElement)("path",{d:"M17 9.83333V16.5M17 23.1667H17.0167M32 16.5C32 24.7843 25.2843 31.5 17 31.5C8.71573 31.5 2 24.7843 2 16.5C2 8.21573 8.71573 1.5 17 1.5C25.2843 1.5 32 8.21573 32 16.5Z",strokeWidth:"3",strokeLinecap:"round",strokeLinejoin:"round"}))),(0,t.createElement)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"},(0,t.createElement)(tt.Title,{as:"h3",className:"text-2xl font-semibold text-slate-800"},(0,n.__)("Rollback to Previous Version","astra-addon")),(0,t.createElement)("p",{className:"mt-2 text-sm text-slate-500"},(0,n.__)(`Are you sure you want to rollback to ${c} v${s}?`,"astra-addon")))),(0,t.createElement)("div",{className:"mt-6 sm:flex sm:flex-row sm:ml-20"},(0,t.createElement)("button",{type:"button",className:"w-full inline-flex justify-center rounded border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white focus:bg-red-700 hover:bg-red-700 focus:outline-none sm:mr-3 sm:w-auto sm:text-sm",onClick:()=>{let e="";e="astra-addon"===c?astra_addon_admin.addon_rollback_url.replace("VERSION",s):astra_addon_admin.theme_rollback_url.replace("VERSION",s),l(!1),window.location.href=e}},(0,n.__)("Rollback","astra-addon")),(0,t.createElement)("button",{type:"button",className:"mt-3 w-full inline-flex justify-center rounded border border-slate-200 shadow-sm px-4 py-2 bg-white text-base font-medium text-slate-800 focus:bg-gray-50 hover:bg-gray-50 focus:outline-none sm:mt-0 sm:w-auto sm:text-sm",onClick:()=>{l(!r)},ref:u},(0,n.__)("Cancel","astra-addon"))))))))},nt=()=>{const e=astra_addon_admin.theme_versions,r=astra_addon_admin.addon_versions,l=(0,a.useRef)(e[0].value),s=(0,a.useRef)(r[0].value),[o,i]=(0,a.useState)(null),[d,c]=(0,a.useState)("astra-theme"),[u,m]=(0,a.useState)(!1),f=e=>{"addon"===e.target.dataset.product_type?(i(s.current.value),c("astra-addon")):(i(l.current.value),c("astra-theme")),m(!0)};return astra_addon_admin.license_status?(0,t.createElement)(t.Fragment,null,(0,t.createElement)("section",{className:"flex flex-col sm:flex-row border-b border-solid border-slate-200 px-8 py-8 justify-between"},(0,t.createElement)("div",{className:"mr-16 w-full sm:w-6/12 block items-center mb-2 sm:mb-0"},(0,t.createElement)("h3",{className:"p-0 block text-xl leading-8 font-semibold text-slate-800"},(0,n.__)("Rollback to Previous Version","astra-addon")),(0,t.createElement)("p",{className:"block mt-2 text-sm text-slate-500"},(0,n.__)("Experiencing an issue with current versions of software? Roll back to a previous version to help troubleshoot the issue.","astra-addon"))),(0,t.createElement)("div",{className:"grid justify-items-end tablet:justify-items-start tablet:my-2"},(0,t.createElement)("div",{className:"inline-flex w-max mb-4"},(0,t.createElement)("h4",{className:"mr-4 text-sm leading-8 font-medium text-slate-800"},astra_admin.theme_name),(0,t.createElement)("select",{id:"themeVersionRollback",name:"themeVersionRollback",className:"block h-9 mr-2 sm:text-sm astra-admin__input-field astra-admin__dropdown",ref:l,onBlur:e=>{c("astra-theme")}},e.map((e=>(0,t.createElement)("option",{key:e.value,value:e.value},e.label)))),(0,t.createElement)("button",{type:"button",className:"inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-astra transition focus:bg-astra-hover hover:bg-astra-hover focus:outline-none h-9",onClick:f,"data-product_type":"theme"},(0,n.__)("Rollback","astra-addon"))),(0,t.createElement)("div",{className:"inline-flex w-max"},(0,t.createElement)("h4",{className:"mr-4 text-sm leading-8 font-medium text-slate-800"},astra_addon_admin.addon_name),(0,t.createElement)("div",{className:"bsf-rollback-version"},(0,t.createElement)("input",{type:"hidden",name:"product-name",id:"bsf-product-name",value:astra_addon_admin.addon_name}),(0,t.createElement)("select",{id:"addonVersionRollback",name:"addonVersionRollback",className:"bsf-rollback-version-select block h-9 mr-2 sm:text-sm astra-admin__input-field astra-admin__dropdown",ref:s,onBlur:e=>{c("astra-addon")}},r.map((e=>(0,t.createElement)("option",{key:e.value,value:e.value},e.label))))),(0,t.createElement)("button",{type:"button",className:"bsf-rollback-button inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-astra transition focus:bg-astra-hover hover:bg-astra-hover focus:outline-none h-9",onClick:f,"data-product_type":"addon","data-placeholder-url":astra_addon_admin.addon_rollback_nonce_placeholder_url},(0,n.__)("Rollback","astra-addon"))))),(0,t.createElement)(at,{openPopup:u,setopenPopup:m,previousVersionSelect:o,productSelect:d})):""};let rt=(0,a.createContext)(null);function lt(){let e=(0,a.useContext)(rt);if(null===e){let e=new Error("You used a <Label /> component, but it is not inside a relevant parent.");throw Error.captureStackTrace&&Error.captureStackTrace(e,lt),e}return e}let st=m((function(e,t){let{passive:a=!1,...n}=e,r=lt(),l=`headlessui-label-${ne()}`,s=F(t);w((()=>r.register(l)),[l,r.register]);let o={ref:s,...r.props,id:l};return a&&("onClick"in o&&delete o.onClick,"onClick"in n&&delete n.onClick),d({ourProps:o,theirProps:n,slot:r.slot||{},defaultTag:"label",name:r.name||"Label"})}));function ot(e){var t;if(e.type)return e.type;let a=null!=(t=e.as)?t:"button";return"string"==typeof a&&"button"===a.toLowerCase()?"button":void 0}function it(e,t){let[n,r]=(0,a.useState)((()=>ot(e)));return w((()=>{r(ot(e))}),[e.type,e.as]),w((()=>{n||!t.current||t.current instanceof HTMLButtonElement&&!t.current.hasAttribute("type")&&r("button")}),[n,t]),n}let dt=(0,a.createContext)(null);dt.displayName="GroupContext";let ct=a.Fragment,ut=m((function(e,t){let{checked:n,defaultChecked:r=!1,onChange:l,name:s,value:o,...i}=e,c=`headlessui-switch-${ne()}`,u=(0,a.useContext)(dt),m=(0,a.useRef)(null),p=F(m,t,null===u?null:u.setSwitch),[h,g]=function(e,t,n){let[r,l]=(0,a.useState)(n),s=void 0!==e;return[s?e:r,N((e=>(s||l(e),null==t?void 0:t(e))))]}(n,l,r),b=N((()=>null==g?void 0:g(!h))),v=N((e=>{if(ee(e.currentTarget))return e.preventDefault();e.preventDefault(),b()})),_=N((e=>{e.key===X.Space?(e.preventDefault(),b()):e.key===X.Enter&&function(e){var t;let a=null!=(t=null==e?void 0:e.form)?t:e.closest("form");if(a)for(let e of a.elements)if("INPUT"===e.tagName&&"submit"===e.type||"BUTTON"===e.tagName&&"submit"===e.type||"INPUT"===e.nodeName&&"image"===e.type)return void e.click()}(e.currentTarget)})),w=N((e=>e.preventDefault())),E=(0,a.useMemo)((()=>({checked:h})),[h]),x={id:c,ref:p,role:"switch",type:it(e,m),tabIndex:0,"aria-checked":h,"aria-labelledby":null==u?void 0:u.labelledby,"aria-describedby":null==u?void 0:u.describedby,onClick:v,onKeyUp:_,onKeyPress:w};return a.createElement(a.Fragment,null,null!=s&&h&&a.createElement(le,{features:re.Hidden,...f({as:"input",type:"checkbox",hidden:!0,readOnly:!0,checked:h,name:s,value:o})}),d({ourProps:x,theirProps:i,slot:E,defaultTag:"button",name:"Switch"}))})),mt=Object.assign(ut,{Group:function(e){let[t,n]=(0,a.useState)(null),[r,l]=function(){let[e,t]=(0,a.useState)([]);return[e.length>0?e.join(" "):void 0,(0,a.useMemo)((()=>function(e){let n=N((e=>(t((t=>[...t,e])),()=>t((t=>{let a=t.slice(),n=a.indexOf(e);return-1!==n&&a.splice(n,1),a}))))),r=(0,a.useMemo)((()=>({register:n,slot:e.slot,name:e.name,props:e.props})),[n,e.slot,e.name,e.props]);return a.createElement(rt.Provider,{value:r},e.children)}),[t])]}(),[s,o]=Oe(),i=(0,a.useMemo)((()=>({switch:t,setSwitch:n,labelledby:r,describedby:s})),[t,n,r,s]),c=e;return a.createElement(o,{name:"Switch.Description"},a.createElement(l,{name:"Switch.Label",props:{onClick(){!t||(t.click(),t.focus({preventScroll:!0}))}}},a.createElement(dt.Provider,{value:i},d({ourProps:{},theirProps:c,defaultTag:ct,name:"Switch.Group"}))))},Label:st,Description:He});var ft=window.wp.apiFetch,pt=e.n(ft),ht=a.forwardRef((function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"}))})),gt=a.forwardRef((function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{fillRule:"evenodd",d:"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",clipRule:"evenodd"}))})),bt=()=>{const[e,r]=(0,a.useState)(!0);return(0,t.createElement)(a.Fragment,null,(0,t.createElement)("div",{"aria-live":"assertive",className:"fixed inset-10 z-10 flex px-4 py-6 pointer-events-none sm:p-6 sm:items-start"},(0,t.createElement)("div",{className:"w-full flex flex-col items-center space-y-4 sm:items-end"},(0,t.createElement)(K,{show:e,as:a.Fragment,enter:"transform ease-out duration-300 transition",enterFrom:"translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2",enterTo:"translate-y-0 opacity-100 sm:translate-x-0",leave:"transition ease-in duration-100",leaveFrom:"opacity-100",leaveTo:"opacity-0"},(0,t.createElement)("div",{className:"max-w-sm w-full bg-white shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden"},(0,t.createElement)("div",{className:"p-4"},(0,t.createElement)("div",{className:"flex items-start"},(0,t.createElement)("div",{className:"flex-shrink-0"},(0,t.createElement)(ht,{className:"h-6 w-6 text-green-400","aria-hidden":"true"})),(0,t.createElement)("div",{className:"ml-3 w-0 flex-1 pt-0.5"},(0,t.createElement)("p",{className:"text-sm font-medium text-gray-900"}," ",(0,n.__)("Successfully saved!","astra-addon")," ")),(0,t.createElement)("div",{className:"ml-4 flex-shrink-0 flex"},(0,t.createElement)("button",{className:"bg-white rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500",onClick:()=>{r(!1)}},(0,t.createElement)("span",{className:"sr-only"}," ",(0,n.__)("Close ","astra-addon")," "),(0,t.createElement)(gt,{className:"h-5 w-5","aria-hidden":"true"}))))))))))};function vt(){for(var e=arguments.length,t=new Array(e),a=0;a<e;a++)t[a]=arguments[a];return t.filter(Boolean).join(" ")}var _t=()=>{const e="disable"!==astra_addon_admin.enable_beta,[r,l]=(0,a.useState)(e),[s,o]=(0,a.useState)(!1);return(0,t.createElement)(t.Fragment,null,(0,t.createElement)("section",{className:"block border-b border-solid border-slate-200 px-8 py-8 justify-between"},(0,t.createElement)("div",{className:"mr-16 w-full flex items-center"},(0,t.createElement)("h3",{className:"p-0 flex-1 justify-right inline-flex text-xl leading-8 font-semibold text-slate-800"},(0,n.__)("Enable Beta","astra-addon")),(0,t.createElement)(mt,{checked:r,onChange:()=>{let e;e=r?"disable":"enable",l(!r);const t=new window.FormData;t.append("action","astra_beta_updates"),t.append("security",astra_addon_admin.update_nonce),t.append("status",e),pt()({url:astra_admin.ajax_url,method:"POST",body:t}).then((t=>{t.success&&(window.astra_addon_admin.enable_beta=e,o(!0),setTimeout((()=>{o(!1)}),2e3))}))},className:vt(r?"bg-astra":"bg-slate-200","group relative inline-flex h-4 w-9 flex-shrink-0 cursor-pointer items-center justify-center rounded-full focus:outline-none focus:ring-2 focus:ring-astra focus:ring-offset-2")},(0,t.createElement)("span",{"aria-hidden":"true",className:"pointer-events-none absolute h-full w-full rounded-md bg-white"}),(0,t.createElement)("span",{"aria-hidden":"true",className:vt(r?"bg-astra":"bg-gray-200","pointer-events-none absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out")}),(0,t.createElement)("span",{"aria-hidden":"true",className:vt(r?"translate-x-5":"translate-x-0","toggle-bubble pointer-events-none absolute left-0 inline-block h-5 w-5 transform rounded-full border border-gray-200 bg-white shadow ring-0 transition-transform duration-200 ease-in-out")}))),(0,t.createElement)("p",{className:"mt-2 w-full text-sm text-slate-500"},(0,n.__)("Enable this option to receive update notifications for beta versions. Please read ","astra-addon"),(0,t.createElement)("a",{className:"text-astra focus:text-astra-hover active:text-astra-hover hover:text-astra-hover underline",href:"https://wpastra.com/docs/automatic-beta-updates-for-astra/?utm_source=wp&utm_medium=dashboard",target:"_blank",rel:"noreferrer"}," ",(0,n.__)("this article","astra-addon")," "),(0,n.__)(" to know more.","astra-addon"))),s&&(0,t.createElement)(bt,null))},wt=a.forwardRef((function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"}))})),Et=e=>{const{message:r,checkVerification:l}=e,[s,o]=(0,a.useState)(!0);return(0,t.createElement)(a.Fragment,null,(0,t.createElement)("div",{"aria-live":"assertive",className:"fixed inset-10 z-10 flex px-4 py-6 pointer-events-none sm:p-6 sm:items-start"},(0,t.createElement)("div",{className:"w-full flex flex-col items-center space-y-4 sm:items-end"},(0,t.createElement)(K,{show:s,as:a.Fragment,enter:"transform ease-out duration-300 transition",enterFrom:"translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2",enterTo:"translate-y-0 opacity-100 sm:translate-x-0",leave:"transition ease-in duration-100",leaveFrom:"opacity-100",leaveTo:"opacity-0"},(0,t.createElement)("div",{className:"max-w-sm w-full bg-white shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden"},(0,t.createElement)("div",{className:"p-4"},(0,t.createElement)("div",{className:"flex items-start"},(0,t.createElement)("div",{className:"flex-shrink-0"},l?(0,t.createElement)(ht,{className:"h-6 w-6 text-green-400","aria-hidden":"true"}):(0,t.createElement)(wt,{className:"h-6 w-6 text-red-400","aria-hidden":"true"})),(0,t.createElement)("div",{className:"ml-3 w-0 flex-1 pt-0.5"},(0,t.createElement)("p",{className:"text-sm font-medium text-gray-900"}," ",r," ")),(0,t.createElement)("div",{className:"ml-4 flex-shrink-0 flex"},(0,t.createElement)("button",{className:"bg-white rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500",onClick:()=>{o(!1)}},(0,t.createElement)("span",{className:"sr-only"}," ",(0,n.__)("Close ","astra-addon")," "),(0,t.createElement)(gt,{className:"h-5 w-5","aria-hidden":"true"}))))))))))},xt=()=>{const e=astra_addon_admin.license_status,r=e?"bsf_deactivate_license":"bsf_activate_license",[l,s]=(0,a.useState)(e?"activated":""),o=e?(0,n.__)("Deactivate","astra-addon"):(0,n.__)("Activate","astra-addon"),i=e?(0,n.__)("Your license is active.","astra-addon"):(0,n.__)("Paste your license key here","astra-addon"),d=e?(0,n.__)("You are using","astra-addon"):(0,n.__)(`Please enter your valid license key below to activate ${astra_admin.plugin_name}!`,"astra-addon"),[c,u]=(0,a.useState)(!1),[m,f]=(0,a.useState)(""),[p,h]=(0,a.useState)(!1);return(0,t.createElement)(t.Fragment,null,(0,t.createElement)("section",{className:"ast-licensing-wrap block border-b border-solid border-slate-200 px-8 py-8 justify-between"},(0,t.createElement)("div",{className:"mr-16 w-full"},(0,t.createElement)("form",{method:"post",id:"bsf-astra-license-form",className:"form-wrap bsf-license-register-astra-addon form-submited-astra-addon"},(0,t.createElement)("h3",{className:"flex items-center mb-4 p-0 text-xl leading-6 font-semibold text-slate-800"},(0,t.createElement)("svg",{width:"20",height:"20",viewBox:"0 0 20 20",className:"mr-2",fill:"none",xmlns:"http://www.w3.org/2000/svg"},(0,t.createElement)("path",{d:"M6.66683 9.16667V5.83333C6.66683 3.99238 8.15921 2.5 10.0002 2.5C11.8411 2.5 13.3335 3.99238 13.3335 5.83333M10.0002 12.5V14.1667M5.00016 17.5H15.0002C15.9206 17.5 16.6668 16.7538 16.6668 15.8333V10.8333C16.6668 9.91286 15.9206 9.16667 15.0002 9.16667H5.00016C4.07969 9.16667 3.3335 9.91286 3.3335 10.8333V15.8333C3.3335 16.7538 4.07969 17.5 5.00016 17.5Z",stroke:"#1E293B",strokeWidth:"1.4",strokeLinecap:"round"})),(0,n.__)("Your License","astra-addon")),!e&&(0,t.createElement)("div",{className:"text-sm text-slate-600"},(0,n.__)("Activate ","astra-addon"),(0,t.createElement)("a",{href:astra_admin.show_self_branding?astra_admin.upgrade_url:astra_addon_admin.agency_license_link,className:"text-astra font-medium underline",target:"_blank"},astra_admin.plugin_name)," ",(0,n.__)(" addon to get professional support and automatic updates from your WordPress dashboard.","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("label",{htmlFor:"bsf_license_manager[license_key]",className:"block mb-4 text-sm text-slate-500"},d," ",e?(0,t.createElement)("span",{className:"font-medium"}," ",astra_admin.theme_name," + ",astra_admin.plugin_name,". "):""),(0,t.createElement)("div",{className:"flex flex-col sm:flex-row"},(0,t.createElement)("div",{className:"relative"},(0,t.createElement)("input",{type:"hidden",id:"bsf_graupi_nonce",name:"bsf_graupi_nonce",value:astra_addon_admin.bsf_graupi_nonce}),(0,t.createElement)("input",{type:"hidden",name:"_wp_http_referer",value:window.location.href.replace(window.location.origin,"")}),(0,t.createElement)("input",{type:"hidden",name:r,value:""}),e&&(0,t.createElement)("input",{type:"hidden",id:"bsf_license_manager[license_key]",name:"bsf_license_manager[license_key]",value:i}),(0,t.createElement)("input",{className:"ast-admin_license-input-field h-10 block w-[28rem] shadow-sm focus:border-astra focus:ring-astra sm:text-sm text-slate-400",id:e?"astra_addon_license_key":"bsf_license_manager[license_key]",name:e?"astra_addon_license_key":"bsf_license_manager[license_key]",type:"text",value:e?i:l,placeholder:i,readOnly:!!e,onChange:e=>s(e.target.value)}),(0,t.createElement)("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg",className:"absolute top-1/2 -translate-y-1/2 left-3"},(0,t.createElement)("path",{d:"M10 4.66667C10.7364 4.66667 11.3333 5.26362 11.3333 6M14 6C14 8.20914 12.2091 10 10 10C9.59589 10 9.20577 9.94007 8.83805 9.82862L7.33333 11.3333H6V12.6667H4.66667V14H2.66667C2.29848 14 2 13.7015 2 13.3333V11.6095C2 11.4327 2.07024 11.2631 2.19526 11.1381L6.17138 7.16195C6.05993 6.79423 6 6.40412 6 6C6 3.79086 7.79086 2 10 2C12.2091 2 14 3.79086 14 6Z",stroke:"#94A3B8",strokeWidth:"1.4",strokeLinecap:"round",strokeLinejoin:"round"})),(0,t.createElement)("input",{type:"hidden",id:"bsf_license_manager[product_id]",name:"bsf_license_manager[product_id]",value:astra_addon_admin.product})),(0,t.createElement)("button",{disabled:""===l,onClick:e=>{e.preventDefault(),h("loading"),"deactivate"===e.target.dataset.trigger?document.getElementById("bsf-astra-license-form").submit():pt()({path:`/bsf-core/v1/license/${e.target.dataset.trigger}`,method:"POST",data:{"license-key":l,"product-id":"astra-addon"}}).then((async e=>{h(!1),f(e.message),u(!0),setTimeout((()=>{u(!1)}),2e3),e.success?(h(!0),location.reload()):s("")}))},"data-trigger":e?"deactivate":"activate",name:r,className:"inline-flex items-center sm:ml-4 px-4 py-2 mt-2 sm:mt-0 text font-medium rounded-md shadow-sm ml-4 "+(e?"text-[#4AB866] bg-gray-50":""===l?"bg-gray-50 text-astra":"bg-astra text-white")},o,"loading"===p&&(0,t.createElement)("svg",{className:"animate-spin -mr-1 ml-3 h-5 w-5 fill-current",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24"},(0,t.createElement)("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),(0,t.createElement)("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})))))),!astra_admin.is_whitelabel&&(0,t.createElement)("div",{className:"mt-4"},e?(0,t.createElement)("a",{href:"https://store.brainstormforce.com/upgrades/",target:"_blank",className:"text-sm font-medium leading-[0.875rem] text-astra"}," ",(0,n.__)("Need more license?","astra-addon")," "):(0,t.createElement)("a",{href:"https://store.brainstormforce.com/support/",target:"_blank",className:"text-sm font-medium leading-[0.875rem] text-astra"}," ",(0,n.__)("Need Help?","astra-addon")," ")))),c&&(0,t.createElement)(Et,{checkVerification:p,message:m}))};function yt(){for(var e=arguments.length,t=new Array(e),a=0;a<e;a++)t[a]=arguments[a];return t.filter(Boolean).join(" ")}var kt=()=>{const e="disable"!==astra_addon_admin.enable_file_generation,[r,l]=(0,a.useState)(e),[s,o]=(0,a.useState)(!1);return(0,t.createElement)(t.Fragment,null,(0,t.createElement)("section",{className:`astra-parent-field-${r} block px-8 py-8 justify-between`},(0,t.createElement)("div",{className:"mr-16 w-full flex items-center"},(0,t.createElement)("h3",{className:"p-0 flex-1 justify-right inline-flex text-xl leading-8 font-semibold text-slate-800"},(0,n.__)("File Generation","astra-addon")),(0,t.createElement)(mt,{checked:r,onChange:()=>{let e;e=r?"disable":"enable",l(!r);const t=new window.FormData;t.append("action","astra_file_generation"),t.append("security",astra_addon_admin.update_nonce),t.append("status",e),pt()({url:astra_admin.ajax_url,method:"POST",body:t}).then((t=>{t.success&&(window.astra_addon_admin.enable_file_generation=e,o(!0),setTimeout((()=>{o(!1)}),2e3))}))},className:yt(r?"bg-astra":"bg-slate-200","group relative inline-flex h-4 w-9 flex-shrink-0 cursor-pointer items-center justify-center rounded-full focus:outline-none focus:ring-2 focus:ring-astra focus:ring-offset-2")},(0,t.createElement)("span",{"aria-hidden":"true",className:"pointer-events-none absolute h-full w-full rounded-md bg-white"}),(0,t.createElement)("span",{"aria-hidden":"true",className:yt(r?"bg-astra":"bg-gray-200","pointer-events-none absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out")}),(0,t.createElement)("span",{"aria-hidden":"true",className:yt(r?"translate-x-5":"translate-x-0","toggle-bubble pointer-events-none absolute left-0 inline-block h-5 w-5 transform rounded-full border border-gray-200 bg-white shadow ring-0 transition-transform duration-200 ease-in-out")}))),astra_admin.show_self_branding&&(0,t.createElement)("p",{className:"mt-2 w-9/12 text-sm text-slate-500 tablet:w-full"},(0,n.__)(`${astra_admin.theme_name} loads the CSS and JS inline on the page by default. If you want to generate separate CSS and JS files for individual addons, enable this option. Please read `,"astra-addon"),(0,t.createElement)("a",{className:"text-astra focus:text-astra-hover active:text-astra-hover hover:text-astra-hover underline",href:"https://wpastra.com/astra-2-1/?utm_source=wp&utm_medium=dashboard",target:"_blank",rel:"noreferrer"}," ",(0,n.__)("this article","astra-addon")," "),(0,n.__)(" to learn the difference between generating CSS and JS inline and in a separate file.","astra-addon")),!astra_admin.show_self_branding&&(0,t.createElement)("p",{className:"mt-2 w-9/12 text-sm text-slate-500 tablet:w-full"},(0,n.__)(`${astra_admin.theme_name} loads the CSS and JS inline on the page by default. If you want to generate separate CSS and JS files for individual addons, enable this option.`,"astra-addon"))),s&&(0,t.createElement)(bt,null))},Nt=()=>{const[e,r]=(0,a.useState)(!1),[l,s]=(0,a.useState)(!1);return(0,t.createElement)(t.Fragment,null,(0,t.createElement)("section",{className:"astra-child-field block px-8 py-8 justify-between"},(0,t.createElement)("div",{className:"w-full flex items-center"},(0,t.createElement)("h3",{className:"p-0 flex-1 inline-flex justify-right text-xl leading-6 font-semibold text-slate-800"},(0,n.__)("Asset Regeneration","astra-addon")),(0,t.createElement)("div",{className:"flex justify-right items-center"},(0,t.createElement)("button",{type:"button",className:"inline-flex px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-astra focus-visible:bg-astra-hover hover:bg-astra-hover focus:outline-none",onClick:()=>{s("loading");const e=new window.FormData;e.append("action","astra_refresh_assets_files"),e.append("security",astra_addon_admin.update_nonce),pt()({url:astra_admin.ajax_url,method:"POST",body:e}).then((e=>{s(!1),r(!0),setTimeout((()=>{r(!1)}),2e3)}))}},(0,n.__)("Regenerate Assets","astra-addon"),"loading"===l&&(0,t.createElement)("svg",{className:"animate-spin -mr-1 ml-3 h-5 w-5 text-white",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24"},(0,t.createElement)("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),(0,t.createElement)("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"}))))),(0,t.createElement)("p",{className:"mt-2 text-sm text-slate-600 w-9/12 tablet:w-full"},(0,n.__)("Facing issues with style, layout, color or another page element? Use this option to regenerate CSS and Javascript assets. It can help with all kinds of asset issues.","astra-addon"))),e&&(0,t.createElement)(bt,null))};function Ct(){for(var e=arguments.length,t=new Array(e),a=0;a<e;a++)t[a]=arguments[a];return t.filter(Boolean).join(" ")}var St=()=>{const e=!astra_addon_admin.show_self_branding,[r,l]=(0,a.useState)(!1),[s,o]=(0,a.useState)(e);return(0,t.createElement)(t.Fragment,null,(0,t.createElement)("section",{className:"block border-b border-t border-solid border-slate-200 px-8 py-8 justify-between"},(0,t.createElement)("div",{className:"mr-16 w-full flex items-center"},(0,t.createElement)("h3",{className:"p-0 flex-1 justify-right inline-flex text-xl leading-8 font-semibold text-slate-800"},(0,n.__)("Enable White Label","astra-addon")),(0,t.createElement)(mt,{checked:s,onChange:()=>{let e;e=!s,o(!s);const t=new window.FormData;t.append("action","astra_addon_update_whitelabel"),t.append("security",astra_addon_admin.update_nonce),t.append("key","hide_branding"),t.append("parent","astra-agency"),t.append("value",e),pt()({url:astra_admin.ajax_url,method:"POST",body:t}).then((t=>{t.success&&(window.astra_addon_admin.show_self_branding=e,l(!0),setTimeout((()=>{l(!1);let e=t.data.rebranded_theme_name.replaceAll("%","");window.open(`${astra_admin.admin_url}?page=${e.toLowerCase()}&path=settings`,"_self")}),500))}))},className:Ct(s?"bg-astra":"bg-slate-200","group relative inline-flex h-4 w-9 flex-shrink-0 cursor-pointer items-center justify-center rounded-full focus:outline-none focus:ring-2 focus:ring-astra focus:ring-offset-2")},(0,t.createElement)("span",{"aria-hidden":"true",className:"pointer-events-none absolute h-full w-full rounded-md bg-white"}),(0,t.createElement)("span",{"aria-hidden":"true",className:Ct(s?"bg-astra":"bg-gray-200","pointer-events-none absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out")}),(0,t.createElement)("span",{"aria-hidden":"true",className:Ct(s?"translate-x-5":"translate-x-0","toggle-bubble pointer-events-none absolute left-0 inline-block h-5 w-5 transform rounded-full border border-gray-200 bg-white shadow ring-0 transition-transform duration-200 ease-in-out")}))),(0,t.createElement)("p",{className:"mt-2 w-9/12 text-sm text-slate-500 tablet:w-full"},(0,n.__)("White Label removes any links to Astra website and change the identity in the dashboard. This setting is mostly used by agencies and developers who are building websites for clients.","astra-addon"))),r&&(0,t.createElement)(bt,null))},Ft=()=>{const[e,r]=(0,a.useState)(!1),[l,s]=(0,a.useState)(!1),[o,i]=(0,a.useState)({agencyAuthorName:astra_addon_admin.agency_author_name,agencyLicenseLink:astra_addon_admin.agency_license_link,agencyAuthorURL:astra_addon_admin.agency_author_url,themeName:astra_addon_admin.theme_name,themeDescription:astra_addon_admin.theme_description,themeScreenshotURL:astra_addon_admin.theme_screenshot_url,themeIconURL:astra_addon_admin.theme_icon_url,pluginName:astra_addon_admin.plugin_name,pluginDescription:astra_addon_admin.plugin_description,sTPluginName:astra_addon_admin.st_plugin_name,sTPluginDescription:astra_addon_admin.st_plugin_description}),d=e=>{let t=e.target.value,a=e.target.dataset.key,n=e.target.dataset.restkey,r={...o};r[a]=t,i(r),window.astra_addon_admin[n]=t};return(0,t.createElement)(t.Fragment,null,(0,t.createElement)("section",{className:"ast-whitelabel-wrap"},(0,t.createElement)("div",{className:"block px-8 py-8 justify-between"},(0,t.createElement)("div",{className:"w-full "},(0,t.createElement)("h3",{className:"p-0 text-base leading-6 font-semibold text-slate-800 mb-8"},(0,n.__)("Agency Details","astra-addon")),(0,t.createElement)("div",{className:"mb-6"},(0,t.createElement)("label",{htmlFor:"author",className:"block text-sm font-medium text-slate-600"},(0,n.__)("Agency author name","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("input",{onChange:d,value:o.agencyAuthorName,"data-parent":"astra-agency","data-key":"agencyAuthorName","data-restkey":"agency_author_name",type:"text",name:"author",id:"author",className:"ast-admin_input-field h-10 block w-4/5 shadow-sm focus:border-astra focus:ring-astra sm:text-sm",placeholder:""}))),(0,t.createElement)("div",{className:"mb-6"},(0,t.createElement)("label",{htmlFor:"author_url",className:"block text-sm font-medium text-slate-600"},(0,n.__)("Agency author URL","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("input",{onChange:d,type:"text",value:o.agencyAuthorURL,"data-parent":"astra-agency","data-key":"agencyAuthorURL","data-restkey":"agency_author_url",name:"author_url",id:"author_url",className:"ast-admin_input-field h-10 block w-4/5 shadow-sm focus:border-astra focus:ring-astra sm:text-sm",placeholder:""}))),(0,t.createElement)("div",{className:""},(0,t.createElement)("label",{htmlFor:"licence",className:"block text-sm font-medium text-slate-600"},(0,n.__)("Agency license link","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("input",{onChange:d,type:"text",value:o.agencyLicenseLink,"data-parent":"astra-agency","data-key":"agencyLicenseLink","data-restkey":"agency_license_link",name:"licence",id:"licence",className:"ast-admin_input-field h-10 block w-4/5 shadow-sm focus:border-astra focus:ring-astra sm:text-sm",placeholder:""})),(0,t.createElement)("p",{className:"mt-2 text-sm text-slate-500"},(0,n.__)("Get license link will be displayed in the license form when the purchase key is expired/not valid.","astra-addon"))))),(0,t.createElement)("div",{className:"block border-t border-solid border-slate-200 px-8 py-8 justify-between"},(0,t.createElement)("div",{className:"w-full"},(0,t.createElement)("h3",{className:"p-0 text-base leading-6 font-semibold text-slate-800 mb-8"},(0,n.__)("Astra Theme Branding","astra-addon")),(0,t.createElement)("div",{className:"mb-6"},(0,t.createElement)("label",{htmlFor:"name",className:"block text-sm font-medium text-slate-600"},(0,n.__)("Theme Name","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("input",{onChange:d,type:"text",value:o.themeName,"data-parent":"astra","data-key":"themeName","data-restkey":"theme_name",name:"name",id:"name",className:"ast-admin_input-field h-10 block w-4/5 shadow-sm focus:border-astra focus:ring-astra sm:text-sm",placeholder:""}))),(0,t.createElement)("div",{className:"mb-6"},(0,t.createElement)("label",{htmlFor:"description",className:"block text-sm font-medium text-slate-600"},(0,n.__)("Theme Description","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("textarea",{name:"description",id:"description","data-parent":"astra","data-key":"themeDescription","data-restkey":"theme_description",onChange:d,value:o.themeDescription,rows:"4",className:"ast-admin_input-field block w-4/5 shadow-sm focus:border-astra focus:ring-astra sm:text-sm"}))),(0,t.createElement)("div",{className:"mb-6"},(0,t.createElement)("label",{htmlFor:"screenshot",className:"block text-sm font-medium text-slate-600"},(0,n.__)("Theme Screenshot URL","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("input",{onChange:d,"data-parent":"astra","data-key":"themeScreenshotURL","data-restkey":"theme_screenshot_url",type:"text",value:o.themeScreenshotURL,name:"screenshot",id:"screenshot",className:"ast-admin_input-field h-10 block w-4/5 shadow-sm focus:border-astra focus:ring-astra sm:text-sm",placeholder:""})),(0,t.createElement)("p",{className:"mt-2 text-sm text-slate-500"},(0,n.__)("The recommended image size is 1200px wide by 900px tall.","astra-addon"))),(0,t.createElement)("div",{className:""},(0,t.createElement)("label",{htmlFor:"icon",className:"block text-sm font-medium text-slate-600"},(0,n.__)("Theme Icon URL","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("input",{onChange:d,"data-parent":"astra","data-key":"themeIconURL","data-restkey":"theme_icon_url",type:"text",value:o.themeIconURL,name:"icon",id:"icon",className:"ast-admin_input-field h-10 block w-4/5 shadow-sm focus:border-astra focus:ring-astra sm:text-sm",placeholder:""})),(0,t.createElement)("p",{className:"mt-2 text-sm text-slate-500"},(0,n.__)("The recommended icon should have some background to get adjust properly on white background too.","astra-addon"))))),(0,t.createElement)("div",{className:"block border-t border-solid border-slate-200 px-8 py-8 justify-between"},(0,t.createElement)("div",{className:"w-full "},(0,t.createElement)("h3",{className:"p-0 text-base leading-6 font-semibold text-slate-800 mb-8"},(0,n.__)("Astra Pro Branding","astra-addon")),(0,t.createElement)("div",{className:"mb-6"},(0,t.createElement)("label",{htmlFor:"astra_pro_name",className:"block text-sm font-medium text-slate-600"},(0,n.__)("Plugin Name","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("input",{onChange:d,type:"text","data-parent":"astra-pro","data-key":"pluginName","data-restkey":"plugin_name",value:o.pluginName,name:"astra_pro_name",id:"astra_pro_name",className:"ast-admin_input-field h-10 block w-4/5 shadow-sm focus:border-astra focus:ring-astra sm:text-sm",placeholder:""}))),(0,t.createElement)("div",{className:""},(0,t.createElement)("label",{htmlFor:"astra_pro_description",className:"block text-sm font-medium text-slate-600"},(0,n.__)("Plugin Description","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("textarea",{name:"astra_pro_description",id:"astra_pro_description","data-parent":"astra-pro","data-key":"pluginDescription","data-restkey":"plugin_description",onChange:d,value:o.pluginDescription,rows:"4",className:"ast-admin_input-field block w-4/5 shadow-sm focus:border-astra focus:ring-astra sm:text-sm"}))))),astra_admin.starter_templates_data.is_available&&(0,t.createElement)("div",{className:"block border-t border-solid border-slate-200 px-8 py-8 justify-between"},(0,t.createElement)("div",{className:"w-full "},(0,t.createElement)("h3",{className:"p-0 text-base leading-6 font-semibold text-slate-800 mb-8"},(0,n.__)("Starter Templates Branding","astra-addon")),(0,t.createElement)("div",{className:"mb-6"},(0,t.createElement)("label",{htmlFor:"st_name",className:"block text-sm font-medium text-slate-600"},(0,n.__)("Plugin Name","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("input",{onChange:d,type:"text","data-parent":"astra-sites","data-key":"sTPluginName","data-restkey":"st_plugin_name",value:o.sTPluginName,name:"st_name",id:"st_name",className:"ast-admin_input-field h-10 block w-4/5 shadow-sm focus:border-astra focus:ring-astra sm:text-sm",placeholder:""}))),(0,t.createElement)("div",{className:""},(0,t.createElement)("label",{htmlFor:"st_description",className:"block text-sm font-medium text-slate-600"},(0,n.__)("Plugin Description","astra-addon")),(0,t.createElement)("div",{className:"mt-2"},(0,t.createElement)("textarea",{name:"st_description",id:"st_description","data-parent":"astra-sites","data-key":"sTPluginDescription","data-restkey":"st_plugin_description",onChange:d,value:o.sTPluginDescription,rows:"4",className:"ast-admin_input-field block w-4/5 shadow-sm focus:border-astra focus:ring-astra sm:text-sm"}))))),(0,t.createElement)("div",{className:"block mb-8 w-4/5 px-8"},(0,t.createElement)("button",{onClick:()=>{s("loading");const e=new window.FormData;e.append("action","astra_addon_update_whitelabel"),e.append("security",astra_addon_admin.update_nonce),e.append("data",JSON.stringify(o)),pt()({url:astra_admin.ajax_url,method:"POST",body:e}).then((e=>{e.success&&(s(!1),r(!0),setTimeout((()=>{r(!1);let t=e.data.rebranded_theme_name.replaceAll("%","");window.open(`${astra_admin.admin_url}?page=${t.toLowerCase()}&path=settings`,"_self")}),500))}))},className:"inline-flex ml-auto px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-astra focus-visible:bg-astra-hover hover:bg-astra-hover focus:outline-none"},(0,n.__)("Save","astra-addon"),"loading"===l&&(0,t.createElement)("svg",{className:"animate-spin -mr-1 ml-3 h-5 w-5 text-white",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24"},(0,t.createElement)("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),(0,t.createElement)("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"}))))),e&&(0,t.createElement)(bt,null))};wp.hooks.addFilter("astra_dashboard.after_navigation_version","astra_addon/dashboard_app",(function(e){return astra_addon_admin.is_bsf_package&&(e=astra_addon_admin.license_status?(0,t.createElement)("div",{className:"pl-3 text-[#4AB866]"}," ",(0,n.__)("License activated","astra-addon")," "):(0,t.createElement)("div",{className:"pl-3"},(0,t.createElement)("a",{className:"hover:text-astra text-slate-400",href:`admin.php?page=${astra_admin.home_slug}&path=settings`}," ",(0,n.__)("License not activated","astra-addon")," "))),e})),wp.hooks.addFilter("astra_dashboard.main_navigation","astra_addon/dashboard_app",(function(e){return e.pop(),e})),wp.hooks.addFilter("astra_dashboard.settings_navigation","astra_addon/dashboard_app",(function(e){let t=!0;return astra_admin.show_self_branding||astra_addon_admin.license_status||(t=!1),astra_addon_admin.is_bsf_package||(t=!1),t&&e.push({name:(0,n.__)("Version Control","astra-addon"),slug:"version-control",icon:r["version-control"]}),astra_admin.show_self_branding&&e.push({name:(0,n.__)("White Label","astra-addon"),slug:"white-label",icon:r["white-label"]}),e})),wp.hooks.addFilter("astra_dashboard.changelog_products","astra_addon/dashboard_app",(function(e){return e.push({name:"Astra Pro",value:"astra-pro"}),e})),wp.hooks.addFilter("astra_dashboard.settings_tab_wrappers","astra_addon/dashboard_app",(function(e){return astra_addon_admin.is_bsf_package?astra_admin.show_self_branding?(e["version-control"]=(0,t.createElement)(t.Fragment,null," ",(0,t.createElement)(nt,null)," ",(0,t.createElement)(_t,null)," "),e["white-label"]=(0,t.createElement)(t.Fragment,null," ",(0,t.createElement)(Ft,null)," ",(0,t.createElement)(St,null)," ")):e["version-control"]=(0,t.createElement)(t.Fragment,null," ",(0,t.createElement)(nt,null)," "):astra_admin.show_self_branding&&(e["white-label"]=(0,t.createElement)(t.Fragment,null," ",(0,t.createElement)(Ft,null)," ",(0,t.createElement)(St,null)," ")),e})),wp.hooks.addFilter("astra_dashboard.settings_screen_before_global-settings","astra_addon/dashboard_app",(function(e){return astra_addon_admin.is_bsf_package?(0,t.createElement)(t.Fragment,null," ",(0,t.createElement)(xt,null)," ",(0,t.createElement)(kt,null)," ",(0,t.createElement)(Nt,null)," "):(0,t.createElement)(t.Fragment,null," ",(0,t.createElement)(kt,null)," ",(0,t.createElement)(Nt,null)," ")})),wp.hooks.addFilter("astra_dashboard.welcome_screen_after_integrations","astra_addon/dashboard_app",(function(e){return astra_addon_admin.is_bsf_package&&(e=(0,t.createElement)(xt,null)),e}))}(); assets/images/whitelabel-branding-dark.svg 0000666 00000000507 15165304350 0014665 0 ustar 00 <svg width="40" height="40" viewBox="0 0 40 40" fill="#1e293b" xmlns="http://www.w3.org/2000/svg"> <path d="M22.2 20L36 4L14 20H18L4 36L26 20H22.2ZM13.6 22H7.8L12.8 18.4L27.8 7.4L20 4L6 10V16C6 20 7 23.8 9 27.2L13.6 22ZM26.4 18H32L27.2 21.6L13 32C15 33.8 17.4 35.2 20 36C28.4 33 34.2 25 34 16V10L33.6 9.8L26.4 18Z"/> </svg> assets/images/whitelabel-branding.svg 0000666 00000000505 15165304350 0013744 0 ustar 00 <svg width="40" height="40" viewBox="0 0 40 40" fill="white" xmlns="http://www.w3.org/2000/svg"> <path d="M22.2 20L36 4L14 20H18L4 36L26 20H22.2ZM13.6 22H7.8L12.8 18.4L27.8 7.4L20 4L6 10V16C6 20 7 23.8 9 27.2L13.6 22ZM26.4 18H32L27.2 21.6L13 32C15 33.8 17.4 35.2 20 36C28.4 33 34.2 25 34 16V10L33.6 9.8L26.4 18Z"/> </svg> class-astra-addon-admin-loader.php 0000666 00000047663 15165304350 0013135 0 ustar 00 <?php /** * Astra Admin Loader * * @package Astra * @since 4.0.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Astra_Addon_Admin_Loader * * @since 4.0.0 */ class Astra_Addon_Admin_Loader { /** * Instance * * @var object Class object. * @since 4.0.0 */ private static $instance; /** * Option name * * @var string $option_name DB option name. * @since 4.0.0 */ private static $option_name = 'astra_admin_settings'; /** * Admin settings dataset * * @var array $astra_admin_settings Settings array. * @since 4.0.0 */ private static $astra_admin_settings = array(); /** * Plugin slug * * @since 1.0 * @var array $plugin_slug */ public static $plugin_slug = 'astra'; /** * Initiator * * @since 4.0.0 * @return object initialized object of class. */ public static function get_instance() { if ( ! isset( self::$instance ) ) { self::$instance = new self(); } return self::$instance; } /** * Constructor * * @since 4.0.0 */ public function __construct() { self::$astra_admin_settings = get_option( self::$option_name, array() ); define( 'ASTRA_ADDON_ADMIN_DIR', ASTRA_EXT_DIR . 'admin/core/' ); define( 'ASTRA_ADDON_ADMIN_URL', ASTRA_EXT_URI . 'admin/core/' ); $this->includes(); add_filter( 'astra_menu_priority', array( $this, 'update_admin_menu_position' ) ); add_filter( 'astra_dashboard_rest_options', array( $this, 'update_addon_options_defaults' ) ); add_filter( 'astra_admin_settings_datatypes', array( $this, 'update_addon_options_datatypes' ) ); add_action( 'after_setup_theme', array( $this, 'init_admin_settings' ), 99 ); add_action( 'admin_init', array( $this, 'settings_admin_scripts' ) ); } /** * Update Astra's menu priority to show after Dashboard menu. * * @param int $menu_priority priority for admin menu. * @since 4.0.0 */ public function update_admin_menu_position( $menu_priority ) { return 2.1; } /** * Update datatypes for further AJAX move. * * @param array $defaults Defaults for admin app. * * @since 4.0.0 */ public function update_addon_options_datatypes( $defaults ) { $defaults['enable_beta'] = 'string'; $defaults['enable_file_generation'] = 'string'; return $defaults; } /** * Update defaults on REST call. * * @param array $defaults Defaults for admin app. * * @since 4.0.0 */ public function update_addon_options_defaults( $defaults ) { $white_label_markup_instance = Astra_Ext_White_Label_Markup::get_instance(); $defaults['pro_addons'] = Astra_Ext_Extension::get_enabled_addons(); $defaults['enable_file_generation'] = get_option( '_astra_file_generation', 'disable' ); $defaults['show_self_branding'] = Astra_Ext_White_Label_Markup::show_branding(); $defaults['enable_beta_update'] = Astra_Admin_Helper::get_admin_settings_option( '_astra_beta_updates', true, 'disable' ); $defaults['plugin_description'] = $white_label_markup_instance->astra_pro_whitelabel_description(); $defaults['plugin_name'] = $white_label_markup_instance->astra_pro_whitelabel_name(); $defaults['theme_screenshot_url'] = $white_label_markup_instance::get_whitelabel_string( 'astra', 'screenshot', false ); $defaults['theme_description'] = $white_label_markup_instance::get_whitelabel_string( 'astra', 'description', false ); $defaults['theme_name'] = $white_label_markup_instance::get_whitelabel_string( 'astra', 'name', false ); $defaults['agency_license_link'] = $white_label_markup_instance::get_whitelabel_string( 'astra-agency', 'licence', false ); $defaults['agency_author_url'] = $white_label_markup_instance->astra_pro_whitelabel_author_url(); $defaults['agency_author_name'] = $white_label_markup_instance->astra_pro_whitelabel_author(); $defaults['theme_icon_url'] = $white_label_markup_instance::get_whitelabel_string( 'astra', 'icon', false ); $defaults['st_plugin_name'] = $white_label_markup_instance::get_whitelabel_string( 'astra-sites', 'name', false ); $defaults['st_plugin_description'] = $white_label_markup_instance::get_whitelabel_string( 'astra-sites', 'description', false ); return $defaults; } /** * Include required classes. * * @since 4.0.0 */ public function init_admin_settings() { self::$plugin_slug = is_callable( 'Astra_Menu::get_theme_page_slug' ) ? Astra_Menu::get_theme_page_slug() : 'astra'; } /** * Include required classes. * * @since 4.0.0 */ public function includes() { if ( is_admin() ) { /* Ajax init */ require_once ASTRA_ADDON_ADMIN_DIR . 'includes/class-astra-addon-admin-ajax.php'; } } /** * Get Changelogs from API. * * @since 4.0.0 * @return array $changelog_data Changelog Data. */ public static function astra_get_addon_changelog_feed_data() { $changelog_data = array(); $posts = json_decode( wp_remote_retrieve_body( wp_remote_get( 'https://wpastra.com/wp-json/wp/v2/changelog?product=98&per_page=3' ) ) ); // Astra Pro. if ( isset( $posts ) && is_array( $posts ) ) { foreach ( $posts as $post ) { $changelog_data[] = array( 'title' => $post->title->rendered, 'date' => gmdate( 'l F j, Y', strtotime( $post->date ) ), 'description' => $post->content->rendered, 'link' => $post->link, ); } } return $changelog_data; } /** * Get Theme Rollback versions. * * @param string $product astra-theme|astra-addon. * @return array * @since 4.0.0 */ public static function astra_get_rollback_versions( $product = 'astra-theme' ) { $rollback_versions_options = array(); if ( ASTRA_ADDON_BSF_PACKAGE ) { $rollback_versions = Astra_Rollback_version::get_theme_all_versions(); if ( 'astra-addon' === $product ) { $product_id = bsf_extract_product_id( ASTRA_EXT_DIR ); $product_details = get_brainstorm_product( $product_id ); $installed_version = isset( $product_details['version'] ) ? $product_details['version'] : ''; $product_versions = BSF_Rollback_Version::bsf_get_product_versions( $product_id ); // Get Remote versions // Show versions above than latest install version of the product. $rollback_versions = BSF_Rollback_Version::sort_product_versions( $product_versions, $installed_version ); } foreach ( $rollback_versions as $version ) { $version = array( 'label' => $version, 'value' => $version, ); $rollback_versions_options[] = $version; } } return $rollback_versions_options; } /** * Returns an value, * based on the settings database option for the admin settings page. * * @param string $key The sub-option key. * @param mixed $default Option default value if option is not available. * @return mixed Return the option value based on provided key * @since 4.0.0 */ public static function get_admin_settings_option( $key, $default = false ) { $value = isset( self::$astra_admin_settings[ $key ] ) ? self::$astra_admin_settings[ $key ] : $default; return $value; } /** * Update an value of a key, * from the settings database option for the admin settings page. * * @param string $key The option key. * @param mixed $value The value to update. * @return mixed Return the option value based on provided key * @since 4.0.0 */ public static function update_admin_settings_option( $key, $value ) { $astra_admin_updated_settings = get_option( self::$option_name, array() ); $astra_admin_updated_settings[ $key ] = $value; update_option( self::$option_name, $astra_admin_updated_settings ); } /** * Initialize after Astra gets loaded. * * @since 4.0.0 */ public function settings_admin_scripts() { // Enqueue admin scripts. if ( ! empty( $_GET['page'] ) && ( self::$plugin_slug === sanitize_text_field( $_GET['page'] ) || false !== strpos( sanitize_text_field( $_GET['page'] ), self::$plugin_slug . '_' ) ) ) { //phpcs:ignore add_action( 'admin_enqueue_scripts', array( $this, 'styles_scripts' ) ); } } /** * Enqueues the needed CSS/JS for the builder's admin settings page. * * @since 4.0.0 */ public function styles_scripts() { if ( is_customize_preview() ) { return; } $handle = 'astra-addon-admin-dashboard-app'; $build_path = ASTRA_ADDON_ADMIN_DIR . 'assets/build/'; $build_url = ASTRA_ADDON_ADMIN_URL . 'assets/build/'; $script_asset_path = $build_path . 'dashboard-app.asset.php'; $script_info = file_exists( $script_asset_path ) ? include $script_asset_path : array( 'dependencies' => array(), 'version' => ASTRA_EXT_VER, ); $script_dep = $script_info['dependencies']; wp_register_script( $handle, $build_url . 'dashboard-app.js', $script_dep, $script_info['version'], true ); wp_register_style( $handle, ASTRA_ADDON_ADMIN_URL . 'assets/css/admin-custom.css', array(), ASTRA_EXT_VER ); wp_enqueue_script( $handle ); wp_set_script_translations( $handle, 'astra-addon' ); wp_enqueue_style( $handle ); wp_style_add_data( $handle, 'rtl', 'replace' ); $product_id = ASTRA_ADDON_BSF_PACKAGE ? bsf_extract_product_id( ASTRA_EXT_DIR ) : ''; $white_label_markup_instance = Astra_Ext_White_Label_Markup::get_instance(); $rollback_version = isset( self::astra_get_rollback_versions( 'astra-addon' )[0] ) ? self::astra_get_rollback_versions( 'astra-addon' )[0] : ''; // phpcs:ignore PHPCompatibility.Syntax.NewFunctionArrayDereferencing.Found $localize = array( 'theme_versions' => self::astra_get_rollback_versions(), 'addon_versions' => self::astra_get_rollback_versions( 'astra-addon' ), 'addon_rollback_nonce_url' => esc_url( add_query_arg( 'version_no', $rollback_version, wp_nonce_url( admin_url( 'index.php?action=bsf_rollback&product_id=' . $product_id ), 'bsf_rollback' ) ) ), 'addon_rollback_nonce_placeholder_url' => esc_url( wp_nonce_url( admin_url( 'index.php?action=bsf_rollback&version_no=VERSION&product_id=' . $product_id ), 'bsf_rollback' ) ), 'astra_pro_changelog_data' => self::astra_get_addon_changelog_feed_data(), 'addon_name' => astra_get_addon_name(), 'rollback_theme_name' => 'astra', 'rollback_plugin_name' => 'astra-addon', 'theme_rollback_url' => esc_url( admin_url() . 'index.php?action=astra-rollback&version_no=VERSION&_wpnonce=' . wp_create_nonce( 'astra_rollback' ) ), 'addon_rollback_url' => esc_url( admin_url() . 'index.php?action=bsf_rollback&version_no=VERSION&product_id=astra-addon&_wpnonce=' . wp_create_nonce( 'bsf_rollback' ) ), 'license_status' => ASTRA_ADDON_BSF_PACKAGE ? BSF_License_Manager::bsf_is_active_license( $product_id ) : false, 'product' => 'astra-addon', 'bsf_graupi_nonce' => wp_create_nonce( 'bsf_license_activation_deactivation_nonce' ), 'update_nonce' => wp_create_nonce( 'astra_addon_update_admin_setting' ), 'enable_beta' => Astra_Admin_Helper::get_admin_settings_option( '_astra_beta_updates', true, 'disable' ), 'enable_file_generation' => get_option( '_astra_file_generation', 'disable' ), 'pro_extensions' => Astra_Ext_Extension::get_enabled_addons(), 'show_self_branding' => Astra_Ext_White_Label_Markup::show_branding(), 'plugin_description' => $white_label_markup_instance->astra_pro_whitelabel_description(), 'plugin_name' => $white_label_markup_instance->astra_pro_whitelabel_name(), 'theme_screenshot_url' => $white_label_markup_instance::get_whitelabel_string( 'astra', 'screenshot', false ), 'theme_description' => $white_label_markup_instance::get_whitelabel_string( 'astra', 'description', false ), 'theme_name' => $white_label_markup_instance::get_whitelabel_string( 'astra', 'name', false ), 'agency_license_link' => $white_label_markup_instance::get_whitelabel_string( 'astra-agency', 'licence', false ), 'agency_author_url' => $white_label_markup_instance->astra_pro_whitelabel_author_url(), 'agency_author_name' => $white_label_markup_instance->astra_pro_whitelabel_author(), 'theme_icon_url' => $white_label_markup_instance::get_whitelabel_string( 'astra', 'icon', false ), 'st_plugin_name' => $white_label_markup_instance::get_whitelabel_string( 'astra-sites', 'name', false ), 'st_plugin_description' => $white_label_markup_instance::get_whitelabel_string( 'astra-sites', 'description', false ), 'rest_api' => get_rest_url( '', 'astra/v1/admin/settings' ), 'is_bsf_package' => ASTRA_ADDON_BSF_PACKAGE, ); wp_localize_script( $handle, 'astra_addon_admin', apply_filters( 'astra_addon_react_admin_localize', $localize ) ); } /** * Get default/active tab for CPT admin tables. * * @since 4.0.0 * @param string $default default tab attr. * @return string $current_tab */ public static function get_active_tab( $default = '' ) { $current_tab = $default; if ( ! empty( $_REQUEST['layout_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $current_tab = sanitize_text_field( $_REQUEST['layout_type'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended } return $current_tab; } /** * HTML Template for Admin Top Header preview. * * @param string $title Title. * @param bool $tabs Show tabs true/false. * @param string $button_url Button redirection URL. * @param string $kb_docs_url Button redirection URL. * * @since 4.0.0 */ public static function admin_dashboard_header( $title, $tabs, $button_url, $kb_docs_url ) { ?> <div class="-ml-5 ast-admin-top-bar-root -mb-11.5 [2.875rem] sm:mb-0"> <div class="bg-white border-b border-slate-200 px-5"> <div class="max-w-3xl mx-auto lg:max-w-full"> <div class="relative flex flex-wrap flex-col sm:flex-row justify-between items-start sm:items-center h-full min-h-24 sm:min-h-15 pt-14 pb-2 sm:pb-0 sm:pt-0"> <div class="flex gap-6"> <a href="" target="_blank" rel="noopener"> <img src="<?php echo esc_url( apply_filters( 'astra_admin_menu_icon', ASTRA_THEME_URI . 'inc/assets/images/astra-logo.svg' ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound ?>" class="lg:block h-[2.6rem] w-auto ast-theme-icon" alt="Workflow" > </a> <div class="flex items-center"> <h5 class="text-lg sm:text-xl leading-6 font-semibold mr-3 pl-6 border-l border-slate-200"><?php echo esc_html( $title ); ?></h5> <a href="<?php echo esc_url( admin_url( $button_url ) ); ?>" class="text-xs text-astra font-medium leading-4 px-3 py-2 rounded-[0.1875rem] border border-astra bg-[#F6F7F7]">Add New</a> </div> </div> <div class="flex justify-end items-center font-inter"> <?php if ( ! astra_is_white_labelled() ) { ?> <div class="text-xs sm:text-sm font-medium sm:leading-[0.875rem] text-slate-600 pr-4 sm:pr-8 border-r border-slate-200"> <a href="<?php echo esc_url( $kb_docs_url ); ?>" target="_blank"><?php esc_html_e( 'Knowledge Base', 'astra-addon' ); ?></a> </div> <?php } ?> <div class="flex items-center text-[0.625rem] sm:text-sm font-medium leading-[1.375rem] text-slate-400 mr-1 sm:mr-3 divide-x divide-slate-200 gap-3 pl-1 sm:pl-3"> <div class="flex items-center"> <span><?php echo esc_html( ASTRA_THEME_VERSION ); ?></span> <span class="ml-1 sm:ml-2 text-[0.625rem] leading-[1rem] border border-slate-400 font-medium rounded-[0.1875rem] relative inline-flex flex-shrink-0 py-[0rem] px-1.5"> <?php esc_html_e( 'CORE', 'astra-addon' ); ?> </span> </div> <div class="flex items-center pl-3"> <span><?php echo esc_html( ASTRA_EXT_VER ); ?></span> <span class="ml-1 sm:ml-2 text-[0.625rem] leading-[1rem] text-white font-medium border border-slate-800 bg-slate-800 rounded-[0.1875rem] relative inline-flex flex-shrink-0 py-[0rem] px-1.5"> <?php esc_html_e( 'PRO', 'astra-addon' ); ?> </span> </div> <?php if ( ASTRA_ADDON_BSF_PACKAGE ) { $highlight_class = BSF_License_Manager::bsf_is_active_license( bsf_extract_product_id( ASTRA_EXT_DIR ) ) ? 'text-[#4AB866]' : ''; $license_status_text = BSF_License_Manager::bsf_is_active_license( bsf_extract_product_id( ASTRA_EXT_DIR ) ) ? '<span class="pl-3 ' . esc_attr( $highlight_class ) . '">' . __( 'License activated', 'astra-addon' ) . '</span>' : '<a href="' . esc_url( admin_url( 'admin.php?page=astra&path=settings' ) ) . '" class="hover:text-astra text-slate-400 ml-3">' . __( 'License not activated', 'astra-addon' ) . '</a>'; echo wp_kses_post( $license_status_text ); } ?> </div> <?php if ( Astra_Ext_White_Label_Markup::show_branding() ) { ?> <a href="<?php echo esc_url( 'https://wpastra.com/changelog/?utm_source=wp&utm_medium=dashboard' ); ?>" target="_blank" class="w-8 sm:w-10 h-8 sm:h-10 flex items-center justify-center cursor-pointer rounded-full border border-slate-200"> <?php echo ( class_exists( 'Astra_Builder_UI_Controller' ) ) ? wp_kses( Astra_Builder_UI_Controller::fetch_svg_icon( 'horn', false ), Astra_Addon_Kses::astra_addon_svg_kses_protocols() ) : ''; ?> </a> <?php } ?> </div> </div> </div> </div> <?php if ( $tabs ) { $current_type = ''; $active_class = ' text-astra border-astra'; $current_tab = self::get_active_tab(); if ( ! empty( $_REQUEST['layout_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $current_type = sanitize_text_field( $_REQUEST['layout_type'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $active_class = ''; } $url_args = array( 'post_type' => ASTRA_ADVANCED_HOOKS_POST_TYPE, 'layout_type' => $current_tab, ); $custom_layout_types = array( 'header' => __( 'Header', 'astra-addon' ), 'footer' => __( 'Footer', 'astra-addon' ), 'hooks' => __( 'Hooks', 'astra-addon' ), '404-page' => __( '404 Page', 'astra-addon' ), 'content' => __( 'Page Content', 'astra-addon' ), ); $baseurl = add_query_arg( $url_args, admin_url( 'edit.php' ) ); ?> <div class="bg-white border-b border-slate-200 flex flex-wrap items-center -mb-0.5"> <a class="text-sm font-medium ml-2 px-5 py-4 border-b-2 <?php echo esc_attr( $active_class ); ?>" href="<?php echo esc_url( admin_url( 'edit.php?post_type=' . ASTRA_ADVANCED_HOOKS_POST_TYPE ) ); ?>"> <?php echo esc_html__( 'All', 'astra-addon' ); ?> </a> <?php foreach ( $custom_layout_types as $type => $title ) { $type_url = esc_url( add_query_arg( 'layout_type', $type, $baseurl ) ); $active_class = ( $current_type === $type ) ? ' text-astra border-astra' : 'text-slate-600 border-white'; ?> <a class="text-sm font-medium px-5 py-4 border-b-2 <?php echo esc_attr( $active_class ); ?>" href="<?php echo esc_url( $type_url ); ?>"> <?php echo esc_attr( $title ); ?> </a> <?php } ?> </div> <?php } ?> </div> <?php } } Astra_Addon_Admin_Loader::get_instance(); wpbc-dates.php 0000666 00000103652 15165312011 0007314 0 ustar 00 <?php /** * @version 1.0 * @package Booking Calendar * @subpackage Dates Functions * @category Functions * * @author wpdevelop * @link https://wpbookingcalendar.com/ * @email info@wpbookingcalendar.com * * @modified 29.09.2015 */ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly /** * Get the dates in different formats * * @param type $str_dates__dd_mm_yyyy * @param type $booking_type * @param type $booking_form_data * @return array( 'string' => "30.02.2014, 31.02.2014, 01.03.2014" , 'array' => array("2014-02-30", "2014-02-31", .... , 'start_time' => array('00','00','00'); , 'end_time' => array('00','00','00'); ); */ function wpbc_get_dates_in_diff_formats( $str_dates__dd_mm_yyyy, $booking_type, $booking_form_data ){ $str_dates__dd_mm_yyyy = str_replace( '|', ',', $str_dates__dd_mm_yyyy); // Check this for some old versions of plugin if ( strpos($str_dates__dd_mm_yyyy,' - ') !== false ) { // Recheck for any type of Range Days Formats $arr_check_in_out_dates = explode(' - ', $str_dates__dd_mm_yyyy ); $str_dates__dd_mm_yyyy = wpbc_get_comma_seprated_dates_from_to_day( $arr_check_in_out_dates[0], $arr_check_in_out_dates[1] ); } $days_array = explode( ',', $str_dates__dd_mm_yyyy ); // Create dates Array $only_days = array(); foreach ($days_array as $new_day) { if ( ! empty($new_day) ) { $new_day = trim( $new_day ); $new_day = str_replace( '-', '.', $new_day); $new_day = explode( '.', $new_day); $only_days[] = sprintf( "%04d-%02d-%02d", intval( $new_day[2] ), intval( $new_day[1] ), intval( $new_day[0] ) ); } } sort($only_days); // Sort Dates // Get Times from booking form if these fields exist $start_end_time = wpbc_get_times_in_form( $booking_form_data, $booking_type ); if ( $start_end_time !== false ) { $start_time = $start_end_time[0]; // array('00','00','01'); $end_time = $start_end_time[1]; // array('00','00','01'); if ( ( '00' == $start_time[0] ) && ( '00' == $start_time[1] ) ) { //FixIn: 8.7.8.8 $start_time = array( '00', '00', '00' ); $end_time = array( '00', '00', '00' ); } else { if ( count( $only_days ) == 1 ) { // add end date if selected 1 day only and times is exist $only_days[] = $only_days[0]; } } } else { $start_time = array( '00', '00', '00' ); $end_time = array( '00', '00', '00' ); } return array( 'string' => $str_dates__dd_mm_yyyy // dd_mm_yyyy , 'array' => $only_days , 'start_time' => $start_time , 'end_time' => $end_time ); } /** * Check for minimum and maximum available times, and restrict value to these limits. * * @param string $time 24:00 * @param string $min_time 00:01 * @param string $max_time 23:59 * * @return string 23:59 */ function wpbc_check_min_max_available_times( $time = '00:00', $min_time = '00:00', $max_time = '23:59' ) { // Time in minutes $time_m = explode( ':', trim( $time ) ); $time_m = intval( $time_m[0] ) * 60 + intval( $time_m[1] ); // Min time in minutes $min_time_m = explode( ':', trim( $min_time ) ); $min_time_m = intval( $min_time_m[0] ) * 60 + intval( $min_time_m[1] ); // Max time in minutes $max_time_m = explode( ':', trim( $max_time ) ); $max_time_m = intval( $max_time_m[0] ) * 60 + intval( $max_time_m[1] ); if ( $time_m < $min_time_m ) { $time_m = $min_time_m; } if ( $time_m > $max_time_m ) { $time_m = $max_time_m; } // Convert time in minutes back to string HH:MM $time_m_h = floor( $time_m / 60 ); $time_m_m = $time_m - $time_m_h * 60; // Check leading 0 if ( $time_m_h < 10 ) { $time_m_h = '0' . $time_m_h; } if ( $time_m_m < 10 ) { $time_m_m = '0' . $time_m_m; } return $time_m_h . ':' . $time_m_m; } /** * Get Times from booking Form, if these times fields exist * * @param type $booking_form_data * @param type $booking_type * @return mixed array ( array('00','00','01'), array('00','00','01') ) || false */ function wpbc_get_times_in_form( $booking_form_data, $booking_type ){ $is_time_exist = false; $start_time = $end_time = '00:00:00'; if ( strpos( $booking_form_data, 'rangetime' . $booking_type ) !== false ) { // ~checkbox^mymultiple4^~checkbox^rangetime4^ ~checkbox^rangetime4^12:00 - 13:00~ checkbox^rangetime4^~checkbox^rangetime4^~text^name4^Jonny~ // Types of the conditions $f_type = '[^\^]*'; $f_name = 'rangetime[\d]*[\[\]]{0,2}'; $f_value = '[\s]*([0-9:]*)[\s]*\-[\s]*([0-9:]*)[\s]*[^~]*'; $pattern_to_search='%[~]?'.$f_type.'\^'.$f_name.'\^'.$f_value.'[~]?%'; preg_match_all($pattern_to_search, $booking_form_data, $matches, PREG_SET_ORDER); /** * Example of $matches: [ [ [0] => '~checkbox^rangetime4^13:00 - 14:00~' [1] => '13:00' [2] => '14:00' ] ] */ if (count($matches)>0){ $start_time = wpbc_get_time_in_24_hours_format( trim( $matches[0][1] ) ); $start_time[2] = '01'; $end_time = wpbc_get_time_in_24_hours_format( trim( $matches[0][2] ) ); $end_time[2] = '02'; $is_time_exist = true; } else { $start_time = array('00','00','01'); $end_time = array('00','00','02'); } } else { if ( strpos( $booking_form_data, 'starttime' . $booking_type ) !== false ) { // Get START TIME From form request $pos1 = strpos( $booking_form_data, 'starttime' . $booking_type ); // Find start time pos $pos1 = strpos( $booking_form_data, '^', $pos1 ) + 1; // Find TIME pos $pos2 = strpos( $booking_form_data, '~', $pos1 ); // Find TIME length if ( $pos2 === false ) { $pos2 = strlen( $booking_form_data ); } $pos2 = $pos2 - $pos1; $start_time = substr( $booking_form_data, $pos1, $pos2 ); if ( $start_time == '' ) { $start_time = '00:00'; } $start_time = wpbc_check_min_max_available_times( $start_time, '00:01', '23:59' ); //FixIn: 8.7.11.1 $start_time = explode( ':', $start_time ); $start_time[2] = '01'; } else { $start_time = explode( ':', $start_time ); } if ( strpos( $booking_form_data, 'endtime' . $booking_type ) !== false ) { // Get END TIME From form request $pos1 = strpos( $booking_form_data, 'endtime' . $booking_type ); // Find start time pos $pos1 = strpos( $booking_form_data, '^', $pos1 ) + 1; // Find TIME pos $pos2 = strpos( $booking_form_data, '~', $pos1 ); // Find TIME length if ( $pos2 === false ) { $pos2 = strlen( $booking_form_data ); } $pos2 = $pos2 - $pos1; $end_time = substr( $booking_form_data, $pos1, $pos2 ); if ( $end_time == '' ) { $end_time = '00:00'; } $end_time = wpbc_check_min_max_available_times( $end_time, '00:01', '23:59' ); //FixIn: 8.7.11.1 $is_time_exist = true; $end_time = explode( ':', $end_time ); $end_time[2] = '02'; } else { $end_time = explode( ':', $end_time ); } if ( strpos( $booking_form_data, 'durationtime' . $booking_type ) !== false ) { // Get END TIME From form request $pos1 = strpos( $booking_form_data, 'durationtime' . $booking_type ); // Find start time pos $pos1 = strpos( $booking_form_data, '^', $pos1 ) + 1; // Find TIME pos $pos2 = strpos( $booking_form_data, '~', $pos1 ); // Find TIME length if ( $pos2 === false ) { $pos2 = strlen( $booking_form_data ); } $pos2 = $pos2 - $pos1; $end_time = substr( $booking_form_data, $pos1, $pos2 ); $is_time_exist = true; $end_time = explode( ':', $end_time ); // Here we are get start time and add duration for end time $new_end_time = mktime( intval( $start_time[0] ), intval( $start_time[1] ) ); $new_end_time = $new_end_time + $end_time[0] * 60 * 60 + $end_time[1] * 60; $end_time = date( 'H:i', $new_end_time ); if ( $end_time == '00:00' ) { $end_time = '23:59'; } $end_time = explode( ':', $end_time ); $end_time[2] = '02'; } } if ( $is_time_exist ) { return array( $start_time, $end_time ); } else { return false; } } //FixIn: TimeFreeGenerator /** * Convert timeslot "10:00 - 12:00" to specfic timeformat, like "10:00 AM - 12:00 PM" * @param string $timeslot - "10:00 - 12:00" * @param string $time_format = "g:i A" */ function wpbc_time_slot_in_format( $timeslot, $time_format = false ){ //FixIn: 8.9.3.1 if ( ( empty( $timeslot ) ) ) { return ''; } $value_times = explode( '-', $timeslot ); $value_times[0] = trim( $value_times[0] ); $value_times[1] = trim( $value_times[1] ); $s_tm = wpbc_time_localized( $value_times[0] , $time_format); $e_tm = wpbc_time_localized( $value_times[1] , $time_format); $t_delimeter = ' - '; return $s_tm . $t_delimeter . $e_tm ; } //FixIn: 8.4.2.11 Deprecated, use this: wpbc_time_localized /** * Convert timeslot "10:00" to specfic timeformat, like "10:00 AM" * @param string $timeslot - "10:00" * @param string $time_format = "g:i A" */ function wpbc_time_in_format( $timeslot, $time_format = false ){ $s_tm = wpbc_time_localized( $timeslot, $time_format ); return $s_tm; } /** * Get dates from DB of specific booking -> '2023-10-09 12:00:01, 2023-10-09 20:00:02' * * @global type $wpdb * @param type $booking_id_str - booking ID * @return string - comma separated dates in SQL format -> '2023-10-09 12:00:01, 2023-10-09 20:00:02' */ function wpbc_db__get_sql_dates__in_booking__as_str( $booking_id_str ) { global $wpdb; $dates_result = $wpdb->get_results( "SELECT DISTINCT booking_date FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id_str}) ORDER BY booking_date" ); $dates_str = array(); foreach ( $dates_result as $my_date ) { $dates_str[] = $my_date->booking_date; } $dates_str = implode( ', ', $dates_str ); return $dates_str; } /** * Get Only Dates array from Dates Dimes string with comma seperated values * * @param $dates_ymd_his_csv -> '2023-10-09 12:00:01, 2023-10-09 20:00:02' * * @return array|string[] -> '2023-10-09, 2023-10-09' * * * Usually it called like in this Example: * $dates_ymd_his_csv = wpbc_db__get_sql_dates__in_booking__as_str( $booking_id_str ); // '100' | '10,7' - booking ID * $dates_only_arr = wpbc_get_only_dates__from_dates_ymd_his_csv__as_arr( $dates_ymd_his_csv ); // -> '2023-10-09, 2023-10-09' */ function wpbc_get_only_dates__from_dates_ymd_his_csv__as_arr( $dates_ymd_his_csv ){ //FixIn: 10.1.5.6 $dates_only_arr = wpbc_get_dates_arr__from_dates_comma_separated( array( 'dates_separator' => ', ', // ', ' 'dates' => $dates_ymd_his_csv, // '2023-04-04 12:03:00, 2023-04-07 2024-06-30:00' ) ); // Get Only Dates $dates_only_arr = array_map( function ( $date_sql_ymd_his ) { $date_sql_ymd_his = trim( $date_sql_ymd_his ); $date_sql_ymd_his = substr( $date_sql_ymd_his, 0, 10 ); return $date_sql_ymd_his; } , $dates_only_arr ); $dates_only_arr = array_unique( $dates_only_arr ); return $dates_only_arr; } /** * Get Time in 24 hours (military) format, from possible AM/PM format * * @param string $time_str - '01:20 PM' * @return string - '13:20' */ function wpbc_get_time_in_24_hours_format( $time_str ) { $time_str = trim( $time_str ); $time_str_plus = 0; if ( strpos( strtolower( $time_str) ,'am' ) !== false ) { $time_str = str_replace('am', '', $time_str ); $time_str = str_replace('AM', '', $time_str ); } if ( strpos( strtolower( $time_str) ,'pm' ) !== false ) { $time_str = str_replace('pm', '', $time_str ); $time_str = str_replace('PM', '', $time_str ); $time_str_plus = 12; } $time_str = explode( ':', trim( $time_str ) ); //FixIn: 9.9.0.4 $time_str[0] = intval( $time_str[0] ) + $time_str_plus; $time_str[1] = intval( $time_str[1] ); if ($time_str[0] < 10 ) $time_str[0] = '0' . $time_str[0]; if ($time_str[1] < 10 ) $time_str[1] = '0' . $time_str[1]; return $time_str; } /** * Get number of days between 2 dates (dates in mySQL format) * * @param string $day1 - Day in MySQL format * @param string $day2 - Day in MySQL format * * @return int - number of days */ function wpbc_get_difference_in_days( $day1, $day2 ) { return floor( ( strtotime( $day1 ) - strtotime( $day2 ) ) / 86400 ); //FixIn: 8.2.1.11 } /** * Get sorted sql dates array, like: [ '2023-10-18 00:00:00', '2023-10-19 00:00:00', '2023-10-20 00:00:00' ] * * @param string $booking_days '19.10.2023,18.10.2023,20.10.2023' - comma separated dates: * @return array - [ '2023-10-18 00:00:00', '2023-10-19 00:00:00', '2023-10-20 00:00:00' ] - sorted dates array */ function wpbc_get_sorted_days_array( $booking_days ) { if ( strpos($booking_days,' - ') !== false ) { $booking_days = explode(' - ', $booking_days ); $booking_days = wpbc_get_comma_seprated_dates_from_to_day($booking_days[0],$booking_days[1]); } $days_array = explode(',', $booking_days); $only_days = array(); foreach ($days_array as $new_day) { if ( ! empty( $new_day ) ) { $new_day = trim( $new_day ); if ( strpos( $new_day, '.' ) !== false ) $new_day = explode('.',$new_day); else $new_day = explode('-',$new_day); $only_days[] = $new_day[2] .'-' . $new_day[1] .'-' . $new_day[0] . ' 00:00:00'; } } if ( ! empty( $only_days ) ) { sort($only_days); } return $only_days; } /** * Get Dates in Comma seperated format, based on start and end dates. * * @param string $date_str_from - start date: 06.04.2015 * @param string $date_str_to - end date: 08.04.2015 * @return string - comma seperated dates: 06.04.2015, 07.04.2015, 08.04.2015 */ function wpbc_get_comma_seprated_dates_from_to_day( $date_str_from, $date_str_to ) { $date_str_from = explode('.', $date_str_from); $date_str_to = explode('.', $date_str_to); $iDateFrom = mktime( 1, 0, 0, ( intval( $date_str_from[1] ) ), ( intval( $date_str_from[0] ) ), ( intval( $date_str_from[2] ) ) ); $iDateTo = mktime( 1, 0, 0, ( intval( $date_str_to[1] ) ), ( intval( $date_str_to[0] ) ), ( intval( $date_str_to[2] ) ) ); $aryRange=array(); if ( $iDateTo >= $iDateFrom ) { array_push( $aryRange, date( 'd.m.Y', $iDateFrom ) ); // first entry while ($iDateFrom<$iDateTo) { $iDateFrom+=86400; // add 24 hours array_push( $aryRange, date( 'd.m.Y', $iDateFrom ) ); } } $aryRange = implode(', ', $aryRange); return $aryRange; } /** * Get dates array based on start and end dates. * * @param string $sStartDate - start date: 2015-04-06 * @param string $sEndDate - end date: 2015-04-08 * @return array - array( 2015-04-06, 2015-04-07, 2015-04-08 ) */ function wpbc_get_dates_array_from_start_end_days( $sStartDate, $sEndDate ){ // Firstly, format the provided dates. // This function works best with YYYY-MM-DD // but other date formats will work thanks // to strtotime(). $sStartDate = gmdate("Y-m-d", strtotime($sStartDate)); $sEndDate = gmdate("Y-m-d", strtotime($sEndDate)); // Start the variable off with the start date $aDays[] = $sStartDate; // Set a 'temp' variable, sCurrentDate, with // the start date - before beginning the loop $sCurrentDate = $sStartDate; // While the current date is less than the end date while($sCurrentDate < $sEndDate){ // Add a day to the current date $sCurrentDate = gmdate("Y-m-d", strtotime("+1 day", strtotime($sCurrentDate))); // Add this new day to the aDays array $aDays[] = $sCurrentDate; } // Once the loop has finished, return the // array of days. return $aDays; } /** * Get dates array, from range days selection * * @param $params = array( * 'dates_separator' => ' ~ ', // Dates separator * 'dates' => '2023-04-04 ~ 2023-04-07' // Dates in 'Y-m-d' format: '2023-01-31' * ) * * @return array = array( * [0] => 2023-04-04 * [1] => 2023-04-05 * [2] => 2023-04-06 * [3] => 2023-04-07 * ) * * Example #1: wpbc_get_dates_arr__from_dates_range( array( 'dates_separator' => ' ~ ', 'dates' => '2023-04-04 ~ 2023-04-07' ) ); * Example #2: wpbc_get_dates_arr__from_dates_range( array( 'dates_separator' => ' - ', 'dates' => '2023-04-04 - 2023-04-07' ) ); */ function wpbc_get_dates_arr__from_dates_range( $params ){ $defaults = array( 'dates_separator' => ' ~ ', // ' ~ ' 'dates' => '', // '2023-04-04 ~ 2023-04-07' ); $params = wp_parse_args( $params, $defaults ); $dates_arr = array(); if ( ! empty( $params['dates'] ) ) { list( $check_in_date_ymd, $check_out_date_ymd ) = explode( $params['dates_separator'], $params['dates'] ); if ( ( ! empty( $check_in_date_ymd ) ) && ( ! empty( $check_out_date_ymd ) ) ) { $dates_arr = wpbc_get_dates_array_from_start_end_days( $check_in_date_ymd, $check_out_date_ymd ); } } return $dates_arr; } /** * Get dates array, from comma separated dates * * @param $params = array( * 'dates_separator' => ', ', // Dates separator * 'dates' => '2023-04-04, 2023-04-07, 2023-04-05' // Dates in 'Y-m-d' format: '2023-01-31' * ) * * @return array = array( * [0] => 2023-04-04 * [1] => 2023-04-05 * [2] => 2023-04-06 * [3] => 2023-04-07 * ) * * Example #1: wpbc_get_dates_arr__from_dates_comma_separated( array( 'dates_separator' => ', ', 'dates' => '2023-04-04, 2023-04-07, 2023-04-05' ) ); */ function wpbc_get_dates_arr__from_dates_comma_separated( $params ){ $defaults = array( 'dates_separator' => ', ', // ' ~ ' 'dates' => '', // ''2023-04-04, 2023-04-07, 2023-04-05' ); $params = wp_parse_args( $params, $defaults ); $dates_arr = array(); if ( ! empty( $params['dates'] ) ) { $dates_arr = explode( $params['dates_separator'], $params['dates'] ); sort( $dates_arr ); } return $dates_arr; } /** * Get tommorow day from input value * * @param string $nowday : 2015-02-29 * @return int : Unix timestamp for a date like this 2015-02-30 */ function wpbc_get_tommorow_day( $nowday ){ $nowday_d = date( 'm.d.Y', mysql2date( 'U', $nowday ) ); $previos_array = explode( '.', $nowday_d ); $tommorow_day = mktime( 0, 0, 0, intval($previos_array[0]), ( intval($previos_array[1]) + 1 ), intval($previos_array[2]) ) ; return $tommorow_day; } /** * Check if this date is today day * * @param string $some_day : '2015-05-29' * @return boolean : true | false */ function wpbc_is_today_date( $some_day ) { $some_day_d = date( 'm.d.Y', mysql2date( 'U', $some_day ) ); $today_day = date( 'm.d.Y' ); if ( $today_day == $some_day_d ) { return true; } else { return false; } } //FixIn: 8.8.1.2 /** * Check if this date in past * * @param string $some_day : '2015-05-29' * @return boolean : true | false */ function wpbc_is_date_in_past( $some_day ) { $some_day_d = date( 'm.d.Y', mysql2date( 'U', $some_day ) ); $some_array = explode( '.', $some_day_d ); $some_day = mktime( 0, 0, 0, intval($some_array[0]), ( intval($some_array[1]) + 1 ), intval($some_array[2]) ); $today_day = time(); if ( $today_day > $some_day ) { return true; } else { return false; } } //TODO: refactor it, by replacig date_i18n to wp_loc_date... (check depndencies of this function in other usage functions...) /** * Change date / time format * * @param string $dt - MySQL Date - '2015-11-21 00:00:00' * @param type $date_format - Optional. Date format * @param type $time_format - Optional. Time format * @return array( 'DATE in custom Format', 'TIME in custom Format' ) */ function wpbc_get_date_in_correct_format( $dt, $date_format = false, $time_format = false ) { if ( $date_format === false ) $date_format = get_bk_option( 'booking_date_format'); if ( empty( $date_format ) ) $date_format = "m / d / Y, D"; if ( $time_format === false ) $time_format = get_bk_option( 'booking_time_format'); if ( empty( $time_format ) ) $time_format = get_option( 'time_format' ); //'h:i a'; //FixIn: TimeFree 2 - in Booking Calendar Free version show by default times hints in AM/PM format $my_time = date( 'H:i:s' , mysql2date( 'U', $dt ) ); if ( $my_time == '00:00:00' ) $time_format = ''; $bk_date = date_i18n( $date_format, mysql2date( 'U', $dt ) ); $bk_time = date_i18n( ' ' . $time_format , mysql2date( 'U', $dt ) ); if ( $bk_time == ' ' ) $bk_time = ''; return array($bk_date, $bk_time); } /** * Get SHORT Dates showing data * * @param array $bk_dates_short - Array of dates * @param bool $is_approved - is dates approved or not * @param type $bk_dates_short_id * @param type $booking_types * @return string */ function wpbc_get_short_dates_formated_to_show( $bk_dates_short, $is_approved = false, $bk_dates_short_id = array() , $booking_types = array() ){ $short_dates_content = ''; $dcnt = 0; foreach ( $bk_dates_short as $dt ) { if ( $dt == '-' ) { $short_dates_content .= '<span class="date_tire"> - </span>'; } elseif ( $dt == ',' ) { $short_dates_content .= '<span class="date_tire">, </span>'; } else { $short_dates_content .= '<a href="javascript:void(0)" class="field-booking-date label flex-label '; if ( $is_approved ) $short_dates_content .= ' approved'; $short_dates_content .= '">'; $bk_date = wpbc_get_date_in_correct_format( $dt ); $short_dates_content .= $bk_date[0]; $short_dates_content .= '<sup class="field-booking-time">' . $bk_date[1] . '</sup>'; if ( class_exists( 'wpdev_bk_biz_l' ) ) { // BL if ( ( !empty( $bk_dates_short_id[$dcnt] ) ) && ( isset( $booking_types[$bk_dates_short_id[$dcnt]] ) ) ){ $bk_booking_type_name_date = $booking_types[$bk_dates_short_id[$dcnt]]->title; // Default if ( strlen( $bk_booking_type_name_date ) > 19 ) $bk_booking_type_name_date = substr( $bk_booking_type_name_date, 0, 13 ) . '...' . substr( $bk_booking_type_name_date, -3 ); $short_dates_content .= '<sup class="field-booking-time date_from_dif_type"> ' . $bk_booking_type_name_date . '</sup>'; } } $short_dates_content .= '</a>'; } $dcnt++; } return $short_dates_content; } //FixIn: 9.6.3.5 /** * Get booking dates from DB for specific calendar * * @param array $params array( 'approved' => '' // '' - all | '0' - pending | '1' - approved , 'resource_id' => 1 // int or dcv , 'skip_booking_id' => '' // int or dcv ) * * @return array array( [1-7-2023] => array( [sec_0] => stdClass Object ( [booking_date] => 2023-01-07 00:00:00 [approved] => 0 [booking_id] => 96 ) ) [1-8-2023] => Array( [sec_0] => stdClass Object ( [booking_date] => 2023-01-08 00:00:00 [approved] => 0 [booking_id] => 42 ) ) ... */ function wpbc__sql__get_booked_dates( $params ){ $defaults = array( 'approved' => '' // '' - all | '0' - pending | '1' - approved , 'resource_id' => 1 // int or dcv , 'skip_booking_id' => '' // int or dcv ); $params = wp_parse_args( $params, $defaults ); // S a n i t i z e $params['approved'] = ( '' != $params['approved'] ) ? intval( $params['approved'] ) : ''; $params['skip_booking_id'] = ( '' != $params['skip_booking_id'] ) ? wpbc_sanitize_digit_or_csd( $params['skip_booking_id'] ) : ''; $params['resource_id'] = ( '' != $params['resource_id'] ) ? wpbc_sanitize_digit_or_csd( $params['resource_id'] ) : 1; // S Q L global $wpdb; $sql = "SELECT DISTINCT dt.booking_date, dt.approved, bk.booking_id FROM {$wpdb->prefix}bookingdates as dt INNER JOIN {$wpdb->prefix}booking as bk ON bk.booking_id = dt.booking_id WHERE ( 1 = 1 )"; // W H E R E $sql_where = ''; $sql_where .= ( '' != $params['approved'] ) ? " AND ( dt.approved = {$params['approved']} ) " : ''; // Approved (1) or Pending (0) or All // int $sql_where .= " AND dt.booking_date >= CURDATE() "; // Only actual bookings $sql_where .= " AND bk.trash != 1 "; // Not in Trash // int $sql_where .= " AND bk.booking_type IN ( {$params['resource_id']} ) "; // For specific calendar (booking resource) // int $sql_where .= ( '' != $params['skip_booking_id'] ) ? " AND dt.booking_id NOT IN ( {$params['skip_booking_id']} ) " : '' ; // Skip some bookings ? Usually, during booking edit. // O R D E R $sql_order = " ORDER BY dt.booking_date"; // Order by booking dates & times /** * Array( [0] => stdClass Object ( [booking_date] => 2022-12-27 00:00:00, [approved] => 0, [booking_id] => 187 ) [1] => stdClass Object ( [booking_date] => 2022-12-28 00:00:00, [approved] => 1, [booking_id] => 26 ) ... */ $result_arr = $wpdb->get_results( $sql . $sql_where . $sql_order ); // P A R S E $prior_check_out_date = false; $dates_arr = array(); foreach ( $result_arr as $sql_date ) { $blocked_days_range = array( $sql_date->booking_date ); $resource_id = explode( ',', $params['resource_id'] ); $resource_id = $resource_id[0]; if ( ( ! class_exists( 'wpdev_bk_biz_l' ) ) || ( ( class_exists( 'wpdev_bk_biz_l' ) ) && ( ! wpbc_is_this_parent_resource( $resource_id ) ) ) ){ //FixIn: 9.1.2.7 list( $blocked_days_range, $prior_check_out_date ) = apply_filters( 'wpbc_get_extended_block_dates_filter', array( $blocked_days_range, $prior_check_out_date ) ); } foreach ( $blocked_days_range as $date_ymd_his ) { $sql_date->booking_date = $date_ymd_his; $date_as_int = strtotime( $sql_date->booking_date ); $date_key = date( 'Y-m-d', $date_as_int ); $date_seconds = $date_as_int - strtotime( $date_key ); // Transform '2022-09-01' to 9-1-2022 $date_key__for_calendar = date( 'n-j-Y', $date_as_int ); // j - Day of the month without leading zeros 1 to 31 ; n - Number of month, without leading zeros 1 to 12 if ( empty( $dates_arr[ $date_key__for_calendar ] ) ) { $dates_arr[ $date_key__for_calendar ] = array(); } /** * Important info about [ 'sec_' . $date_seconds ] * * We need to have 'sec_0' instead of simple 0 * * for having JavaScript Objects (object property 'sec_o') instead of Array (index 0), after sending Ajax response and JSON decode! */ $dates_arr[ $date_key__for_calendar ][ 'sec_' . $date_seconds] = $sql_date; } } return $dates_arr; } /** * Get season availability based on booking resource and seasons from Booking > Resources > Availability page * * @param array $params array( 'resource_id' => 1 // int or dcv , 'from' => 'NOW' // any value that is possible to use in strtotime() , 'count' => 365 // int ) * * @return array Array ( [2023-01-09] => 1 [2023-01-10] => 1 [2023-01-11] => 1 [2023-01-12] => 1 [2023-01-13] => 1 [2023-01-14] => [2023-01-15] => [2023-01-16] => 1 [2023-01-17] => 1 [2023-01-18] => 1 [2023-01-19] => 1 ... */ function wpbc__sql__get_season_availability( $params ){ //FixIn: 9.5.4.4 $max_days_count = 365; $max_monthes_in_calendar = get_bk_option( 'booking_max_monthes_in_calendar' ); if ( strpos( $max_monthes_in_calendar, 'm' ) !== false ) { $max_days_count = intval( str_replace( 'm', '', $max_monthes_in_calendar ) ) * 31 + 5; //FixIn: 9.6.1.1 } else { $max_days_count = intval( str_replace( 'y', '', $max_monthes_in_calendar ) ) * 365 + 15; //FixIn: 9.6.1.1 } $defaults = array( 'resource_id' => 1 // int or dcv , 'from' => 'NOW' // any value that is possible to use in strtotime() , 'count' => $max_days_count // int ); $params = wp_parse_args( $params, $defaults ); // S a n i t i z e $params['resource_id'] = ( '' != $params['resource_id'] ) ? wpbc_sanitize_digit_or_csd( $params['resource_id'] ) : 1; $is_all_days_available = true; $season_filters_id_arr = array(); if ( ( class_exists( 'wpdev_bk_biz_m' ) ) && ( function_exists( 'wpbc_get_resource_meta' ) ) ) { // BM and higher //FixIn: 9.9.0.13 // S Q L $availability_res = wpbc_get_resource_meta( $params['resource_id'], 'availability' ); if ( ! empty( $availability_res ) ) { /** * Array ( [general] => On, [filter] => Array ( [1] => On, ... * [2] => Off * ... * [8] => Off * [9] => On * ) * ) */ $availability = maybe_unserialize( $availability_res[0]->value ); $is_all_days_available = ( 'On' === $availability['general'] ) ? true : false; $season_filter = $availability['filter']; // Get ID of only activated Seasons if ( is_array( $season_filter ) ) { foreach ( $season_filter as $key => $value ) { if ( $value == 'On' ) { $season_filters_id_arr[] = intval( $key ); // Sanitize booking_filter_id for future SQL } } } } } $days_availability = array(); for( $i = 0; $i < $params['count']; $i++) { $date_y_m_d = date( 'Y-m-d', strtotime( '+' . $i . 'days', strtotime( $params['from'] ) ) ); $days_availability[ $date_y_m_d ] = $is_all_days_available; $date_arr = explode( '-', $date_y_m_d ); foreach ( $season_filters_id_arr as $filter_id ) { $day = intval( $date_arr[2] ); $month = intval( $date_arr[1] ); $year = intval( $date_arr[0] ); if ( wpbc_is_day_inside_of_filter( $day, $month, $year, $filter_id ) ){ $days_availability[ $date_y_m_d ] = ! $days_availability[ $date_y_m_d ]; break; } } } return $days_availability; } wpbc-emails.php 0000666 00000067652 15165312011 0007477 0 ustar 00 <?php /** * @version 1.1 * @package Booking Calendar * @category Send Emails * @author wpdevelop * * @web-site https://wpbookingcalendar.com/ * @email info@wpbookingcalendar.com * * @modified 15.09.2015 */ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly //////////////////////////////////////////////////////////////////////////////// // Emails //////////////////////////////////////////////////////////////////////////////// /** * Check email and format it * * @param string $emails * @return string */ function wpbc_validate_emails( $emails ) { $emails = str_replace(';', ',', $emails); if ( !is_array( $emails ) ) $emails = explode( ',', $emails ); $emails_list = array(); foreach ( (array) $emails as $recipient ) { // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>" $recipient_name = ''; if( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) { if ( count( $matches ) == 3 ) { $recipient_name = $matches[1]; $recipient = $matches[2]; } } else { // Check about correct format of email if( preg_match( '/([\w\.\-_]+)?\w+@[\w\-_]+(\.\w+){1,}/im', $recipient, $matches ) ) { //FixIn: 8.7.7.2 $recipient = $matches[0]; } } $recipient_name = str_replace('"', '', $recipient_name); $recipient_name = trim( wp_specialchars_decode( esc_html( stripslashes( $recipient_name ) ), ENT_QUOTES ) ); $emails_list[] = ( empty( $recipient_name ) ? '' : $recipient_name . ' ' ) . '<' . sanitize_email( $recipient ) . '>'; } $emails_list = implode( ',', $emails_list ); return $emails_list; } /** * Convert email address to the correct format like "Jony Smith" <smith@server.com> - tp prevent "WordPress" title in email. * @param string $wpbc_mail - just simple email * @param type $booking_form_show - array with name and secondname of the person - title * @return string - formated email. */ function wpbc_email_prepand_person_name( $wpbc_mail, $booking_form_show = array() ) { //FixIn:5.4.4 $wpbc_email_title = ((isset($booking_form_show['firstname']))?$booking_form_show['firstname'].' ':'') .((isset($booking_form_show['name']))?$booking_form_show['name'].' ':'') .((isset($booking_form_show['lastname']))?$booking_form_show['lastname'].' ':'') .((isset($booking_form_show['secondname']))?$booking_form_show['secondname'].' ':''); $wpbc_email_title = ( ( empty($wpbc_email_title ) ) ? __('Booking system' ,'booking') : substr( $wpbc_email_title, 0 , -1 ) ); $wpbc_email_title = str_replace('"', '', $wpbc_email_title); $wpbc_email_title = trim( wp_specialchars_decode( esc_html( stripslashes( $wpbc_email_title ) ), ENT_QUOTES ) ); $wpbc_mail = wpbc_validate_emails( $wpbc_email_title . ' <' . $wpbc_mail . '> ' ); return $wpbc_mail; } // Old, Replaced in api-emails class wpbc_email_return_path { function __construct() { add_action( 'phpmailer_init', array( $this, 'fix' ) ); } function fix( $phpmailer ) { $phpmailer->Sender = $phpmailer->From; } } function wpbc_wp_mail( $mail_recipient, $mail_subject, $mail_body, $mail_headers ){ $wpbc_email_return_path = new wpbc_email_return_path(); // $mail_recipient = str_replace( '"', '', $mail_recipient ); //FixIn:5.4.3 if ( ! wpbc_is_this_demo() ) //FixIn:6.1.1.19 @wp_mail($mail_recipient, $mail_subject, $mail_body, $mail_headers); unset( $wpbc_email_return_path ); } function wpbc_check_for_several_emails_in_form( $mail_recipients, $formdata, $bktype ) { //FixIn: 6.0.1.9 $possible_other_emails = explode('~',$formdata); $possible_other_emails = array_map("explode", array_fill(0,count($possible_other_emails),'^'), $possible_other_emails); $other_emails = array(); foreach ( $possible_other_emails as $possible_emails ) { if ( ( $possible_emails[0] == 'email' ) //&& ( $possible_emails[1] != 'email' . $bktype ) && ( ! empty($possible_emails[2]) ) ) $other_emails[]= trim( $possible_emails[2] ); //FixIn: 8.2.1.6 } $other_emails = array_unique( $other_emails ); //FixIn: 8.2.1.6 if ( count( $other_emails ) > 1 ) { $other_emails = implode(',',$other_emails); $mail_recipients = $other_emails; } return $mail_recipients; } // N E W ///////////////////////////////////////////////////////////////////// /** * Parse email and get Parts of Email - Name and Email * * @param string $email * @return array [email] => beta@wpbookingcalendar.com [title] => Booking system [original] => "Booking system" [original_to_show] => "Booking system" <beta@wpbookingcalendar.com> */ function wpbc_get_email_parts( $email ) { $email_to_parse = html_entity_decode( $email ); // Convert " to " etc... $pure_name = ''; $pure_email = ''; if( preg_match( '/(.*)<(.+)>/', $email_to_parse, $matches ) ) { if ( count( $matches ) == 3 ) { $pure_name = $matches[1]; $pure_email = $matches[2]; } } else { // Check about correct format of email if( preg_match( '/([\w\.\-_]+)?\w+@[\w-_]+(\.\w+){1,}/im', $email_to_parse, $matches ) ) { $pure_email = $matches[0]; } } $pure_name = trim( wp_specialchars_decode( esc_html( stripslashes( $pure_name ) ), ENT_QUOTES ) , ' "'); $return_email = array( 'email' => sanitize_email( $pure_email ) , 'title' => $pure_name , 'original' => $email_to_parse , 'original_to_show' => htmlentities( $email_to_parse ) // Convert " to " etc... ); return $return_email; } function wpbc_tooltip_help__fix_quote( $field_val ) { $field_val = str_replace( '"', "'", $field_val ); return $field_val; } // Get Emails Help Shortcodes for Settings pages function wpbc_get_email_help_shortcodes( $skip_shortcodes = array() , $email_example = '') { $icn = '<a href="https://wpbookingcalendar.com/faq/email-shortcodes/#available_shortcodes" class="tooltip_top wpbc-bi-question-circle wpbc_help_tooltip_icon_left" data-original-title="%2$s"></a> %1$s'; $fields = array(); if ( class_exists('wpdev_bk_personal') ) { $fields[] = sprintf(__('You can use (in subject and content of email template) any shortcodes, which you used in the booking form. Use the shortcodes in the same way as you used them in the content form at Settings Fields page.' ,'booking')); $fields[] = '<hr/>'; } $fields[] = '<strong>' . __('You can use following shortcodes in content of this template' ,'booking') . '</strong>'; // [content] if ( class_exists( 'wpdev_bk_personal' ) ) { $fields[] = sprintf( $icn, '<code>[content]</code>', wpbc_tooltip_help__fix_quote( sprintf( __( '%s - inserting data info about the booking, which you configured in the content form at Settings Fields page', 'booking' ), '[content]' ) ) ); } else { $fields[] = sprintf( $icn, '<code>[content]</code>', wpbc_tooltip_help__fix_quote( sprintf( __( '%s - inserting data info about the booking', 'booking' ), '[content]' ) ) ); } // [dates] $fields[] = sprintf( $icn, '<code>[dates]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting the dates of booking' ,'booking'), '[dates]' ) ) ); $fields[] = sprintf( $icn, '<code>[only_dates]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting only booking dates without times' ,'booking'), '[only_dates]' ) ) ); // [check_in_date] if ( ! in_array( 'check_in_date', $skip_shortcodes ) ) { $fields[] = sprintf( $icn, '<code>[check_in_date]</code>', wpbc_tooltip_help__fix_quote( sprintf( __( '%s - inserting check-in date (first day of reservation),', 'booking' ), '[check_in_date]' ) ) ); $fields[] = sprintf( $icn, '<code>[check_in_only_date]</code>', wpbc_tooltip_help__fix_quote( sprintf( __( '%s - inserting check-in date (only date without time) (first day of reservation),', 'booking' ), '[check_in_only_date]' ) ) ); //FixIn: 8.7.2.5 } // [check_out_date] [check_out_plus1day] if ( ! in_array( 'check_out_date', $skip_shortcodes ) ) { $fields[] = sprintf( $icn, '<code>[check_out_date]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting check-out date (last day of reservation),' ,'booking'), '[check_out_date]' ) ) ); $fields[] = sprintf( $icn, '<code>[check_out_only_date]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting check-out date (only date without time) (last day of reservation),' ,'booking'), '[check_out_only_date]' ) ) ); //FixIn: 8.7.2.5 $fields[] = sprintf( $icn, '<code>[check_out_plus1day]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting check-out date (last day of reservation),' ,'booking'), '[check_out_plus1day]') . ' + 1 ' . __('day', 'booking') ) ); } // [dates_count] if ( ! in_array( 'dates_count', $skip_shortcodes ) ) $fields[] = sprintf( $icn, '<code>[dates_count]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting the number of booking dates ' ,'booking'), '[dates_count]' ) ) ); $fields[] = '<hr/>'; // [id] $fields[] = sprintf( $icn, '<code>[booking_id]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting ID of booking ' ,'booking'), '[id], [booking_id]' ) ) ); // [resource_title] [bookingtype] if ( class_exists( 'wpdev_bk_personal' ) ) { if ( ! in_array( 'bookingtype', $skip_shortcodes ) ) { $fields[] = sprintf( $icn, '<code>[resource_title]</code>', wpbc_tooltip_help__fix_quote( sprintf( __( '%s or %s - inserting the title of the booking resource ', 'booking' ), '[resource_title]', '[bookingtype]' ) ) ); } } // [cost] if ( class_exists('wpdev_bk_biz_s') ) if ( ! in_array( 'cost', $skip_shortcodes ) ) $fields[] = sprintf( $icn, '<code>[cost]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting the cost of booking ' ,'booking'), '[cost]' ) ) ); $fields[] = '<hr/>'; // [siteurl] $fields[] = sprintf( $icn, '<code>[siteurl]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting your site URL ' ,'booking'), '[siteurl]' ) ) ); if ( class_exists('wpdev_bk_personal') ) { $fields[] = sprintf( $icn, '<code>[remote_ip]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting IP address of the user who made this action ' ,'booking'), '[remote_ip]' ) ) ); $fields[] = sprintf( $icn, '<code>[user_agent]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting contents of the User-Agent: header from the current request, if there is one ' ,'booking'), '[user_agent]' ) ) ); $fields[] = sprintf( $icn, '<code>[request_url]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting address of the page (if any), where visitor make this action ' ,'booking'), '[request_url]' ) ) ); $fields[] = sprintf( $icn, '<code>[current_time]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting time of this action ' ,'booking'), '[current_time]' ) ) ); } $fields[] = sprintf( $icn, '<code>[current_date]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting date of this action ' ,'booking'), '[current_date]' ) ) ); $fields[] = sprintf( $icn, '<code>[modification_date]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting modification date of booking ' ,'booking'), '[modification_date]' ) ) ); $fields[] = sprintf( $icn, '<code>[modification_year]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting modification date of booking ' ,'booking'), '[modification_year]' ) ) ); $fields[] = sprintf( $icn, '<code>[modification_month]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting modification date of booking ' ,'booking'), '[modification_month]' ) ) ); $fields[] = sprintf( $icn, '<code>[modification_day]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting modification date of booking ' ,'booking'), '[modification_day]' ) ) ); $fields[] = sprintf( $icn, '<code>[modification_hour]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting modification date of booking ' ,'booking'), '[modification_hour]' ) ) ); $fields[] = sprintf( $icn, '<code>[modification_minutes]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting modification date of booking ' ,'booking'), '[modification_minutes]' ) ) ); $fields[] = sprintf( $icn, '<code>[modification_seconds]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting modification date of booking ' ,'booking'), '[modification_seconds]' ) ) ); //FixIn: 10.0.0.34 $fields[] = sprintf( $icn, '<code>[creation_date]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting creation date of booking ' ,'booking'), '[creation_date]' ) ) ); $fields[] = sprintf( $icn, '<code>[creation_year]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting creation date of booking ' ,'booking'), '[creation_year]' ) ) ); $fields[] = sprintf( $icn, '<code>[creation_month]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting creation date of booking ' ,'booking'), '[creation_month]' ) ) ); $fields[] = sprintf( $icn, '<code>[creation_day]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting creation date of booking ' ,'booking'), '[creation_day]' ) ) ); $fields[] = sprintf( $icn, '<code>[creation_hour]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting creation date of booking ' ,'booking'), '[creation_hour]' ) ) ); $fields[] = sprintf( $icn, '<code>[creation_minutes]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting creation date of booking ' ,'booking'), '[creation_minutes]' ) ) ); $fields[] = sprintf( $icn, '<code>[creation_seconds]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting creation date of booking ' ,'booking'), '[creation_seconds]' ) ) ); // [moderatelink] if ( ! in_array( 'moderatelink', $skip_shortcodes ) ) { $fields[] = sprintf( $icn, '<code>[moderatelink]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting moderate link of new booking ' ,'booking'), '[moderatelink]' ) ) ); //FixIn: 8.4.7.25 $fields[] = sprintf( $icn, '<code>[click2approve]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting link to approve booking in 1 mouse click ' ,'booking'), '[click2approve]' ) ) ); $fields[] = sprintf( $icn, '<code>[click2decline]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting link to set booking as pending in 1 mouse click ' ,'booking'), '[click2decline]' ) ) ); $fields[] = sprintf( $icn, '<code>[click2trash]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting link for move booking to trash in 1 mouse click ' ,'booking'), '[click2trash]' ) ) ); } //FixIn: 9.6.3.8 if ( ! in_array( 'add_to_google_cal_button', $skip_shortcodes ) ) { $fields[] = sprintf( $icn, '<code>[add_to_google_cal_button]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting link for export booking to' ,'booking'), '[add_to_google_cal_button]') . ' Google Calendar' ) ); $fields[] = sprintf( $icn, '<code>[add_to_google_cal_url]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting URL for export booking to' ,'booking'), '[add_to_google_cal_url]') . ' Google Calendar' ) ); } if ( class_exists('wpdev_bk_personal') ) { //FixIn: 8.1.3.5.1 if ( ! in_array( 'visitorbookingslisting', $skip_shortcodes ) ) $fields[] = sprintf( $icn, '<code>[visitorbookingslisting]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting link to the page where visitor can see listing of own bookings, (possible to use the %s parameter for setting different %s of this page. Example: %s )' ,'booking'), '[visitorbookingslisting]', '"url"', 'URL', '[visitorbookingslisting url="http://www.server.com/custom-page/"]' ) . ' ' . sprintf( __('Example of HTML a link usage: %s )' ,'booking'), '[visitorbookingslisting url="http://www.server.com/listing-custom-page/" type="link" title="Listing"]]' ) ) ); if ( ! in_array( 'visitorbookingediturl', $skip_shortcodes ) ) $fields[] = sprintf( $icn, '<code>[visitorbookingediturl]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting link to the page where visitor can edit the reservation, (possible to use the %s parameter for setting different %s of this page. Example: %s )' ,'booking'), '[visitorbookingediturl]', '"url"', 'URL', '[visitorbookingediturl url="http://www.server.com/custom-page/"]' ) . ' ' . sprintf( __('Example of HTML a link usage: %s )' ,'booking'), '[visitorbookingediturl url="http://www.server.com/edit-custom-page/" type="link" title="Edit Booking"]]' ) ) ); // [visitorbookingcancelurl] if ( ! in_array( 'visitorbookingcancelurl', $skip_shortcodes ) ) { $fields[] = sprintf( $icn, '<code>[visitorbookingcancelurl]</code>', wpbc_tooltip_help__fix_quote( sprintf( __( '%s - inserting link to the page where visitor can cancel the reservation, (possible to use the %s parameter for setting different %s of this page. Example: %s )', 'booking' ), '[visitorbookingcancelurl]', '"url"', 'URL', '[visitorbookingcancelurl url="http://www.server.com/custom-page/"]' ) . ' ' . sprintf( __('Example of HTML a link usage: %s )' ,'booking'), '[visitorbookingcancelurl url="http://www.server.com/cancel-custom-page/" type="link" title="Cancel Booking"]]' ) ) ); } if ( class_exists('wpdev_bk_biz_s') ) { // [visitorbookingpayurl] if ( ! in_array( 'visitorbookingpayurl', $skip_shortcodes ) ) $fields[] = sprintf( $icn, '<code>[visitorbookingpayurl]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - inserting link to payment page where visitor can pay for the reservation (possible to use the %s parameter for setting different %s of this page. Example: %s )' ,'booking'), '[visitorbookingpayurl]', '"url"', 'URL', '[visitorbookingpayurl url="http://www.server.com/custom-page/"]' ) . ' ' . sprintf( __('Example of HTML a link usage: %s )' ,'booking'), '[visitorbookingpayurl url="http://www.server.com/payment-custom-page/" type="link" title="Pay Now"]]' ) ) ); // [paymentreason] if ( ! in_array( 'paymentreason', $skip_shortcodes ) ) $fields[] = sprintf( $icn, '<code>[paymentreason]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - add the reason for booking payment, you can enter it before sending email, ' ,'booking'), '[paymentreason]' ) ) ); } } // [denyreason] if ( ! in_array( 'denyreason', $skip_shortcodes ) ) $fields[] = sprintf( $icn, '<code>[denyreason]</code>', wpbc_tooltip_help__fix_quote( sprintf( __('%s - add the reason booking was cancelled, you can enter it before sending email, ' ,'booking'), '[denyreason]' ) ) ); //$fields[] = __('HTML tags is accepted.' ,'booking'); $fields[] = '<hr/>'; // show_additional_translation_shortcode_help $fields[] = '<strong>' . sprintf(__('Configuration in several languages' ,'booking') ) . '.</strong>'; $fields[] = sprintf(__('%s - start new translation section, where %s - locale of translation' ,'booking'),'<code>[lang=LOCALE]</code>','<code>LOCALE</code>'); $fields[] = sprintf(__('Example #1: %s - start French translation section' ,'booking'),'<code>[lang=fr_FR]</code>'); $fields[] = sprintf(__('Example #2: "%s" - English and French translation of some message' ,'booking'),'<code>Thank you for your booking.[lang=fr_FR]Je vous remercie de votre reservation.</code>'); return $fields; } function wpbc_email_troubleshooting_help_notice(){ ?><div class="wpbc-settings-notice notice-warning notice-helpful-info"> <?php printf( __( 'To ensure your notifications arrive in your and your customers inboxes, we recommend connecting your email address to your domain and setting up a dedicated SMTP server. If something does not seem to be sending correctly, install the %s or check the %sEmail FAQ%s page.', 'booking' ), '<a href="https://wordpress.org/plugins/wp-mail-logging/">WP Mail Logging Plugin</a>', '<a href="https://wpbookingcalendar.com/faq/no-emails/">', '</a>' ); ?> </div><?php } /** * Check Email subject about Language sections * * @param string $subject * @param string $email_id * @return string */ function wpbc_email_api_get_subject_before( $subject, $email_id ) { $subject = wpbc_lang( $subject ); return $subject; } add_filter( 'wpbc_email_api_get_subject_before', 'wpbc_email_api_get_subject_before', 10, 2 ); // Hook fire in api-email.php /** * Check Email sections content about Language sections * * @param array $fields_values - list of params to parse: 'content', 'header_content', 'footer_content' for different languges, etc .... * @param string $email_id - Email ID * @param string $email_type - 'plain' | 'html' */ function wpbc_email_api_get_content_before( $fields_values, $email_id , $email_type ) { if ( isset( $fields_values['content'] ) ) { $fields_values['content'] = wpbc_lang( $fields_values['content'] ); if ( $email_type == 'html' ) { $fields_values['content'] = make_clickable( $fields_values['content'] ); } } if ( isset( $fields_values['header_content'] ) ) { $fields_values['header_content'] = wpbc_lang( $fields_values['header_content'] ); } if ( isset( $fields_values['footer_content'] ) ) { $fields_values['footer_content'] = wpbc_lang( $fields_values['footer_content'] ); } return $fields_values; } add_filter( 'wpbc_email_api_get_content_before', 'wpbc_email_api_get_content_before', 10, 3 ); // Hook fire in api-email.php /** * Modify email content, if needed. - In HTML mail content, make links clickable. * * @param array $email_content - content of Email * @param string $email_id - Email ID * @param string $email_type - 'plain' | 'html' */ function wpbc_email_api_get_content_after( $email_content, $email_id , $email_type ) { if ( ( $email_type == 'html' ) || ( $email_type == 'multipart' ) ) $email_content = make_clickable( $email_content ); return $email_content; } add_filter( 'wpbc_email_api_get_content_after', 'wpbc_email_api_get_content_after', 10, 3 ); // Hook fire in api-email.php /** * Check Email Headers - in New Booking Email (to admin) set Reply-To header to visitor email. * * @param string $headers * @param string $email_id - Email ID * @param array $fields_values - list of params to parse: 'content', 'header_content', 'footer_content' for different languges, etc .... * @param array $replace_array - list of relpaced shortcodes * @return string */ function wpbc_email_api_get_headers_after( $mail_headers, $email_id , $fields_values , $replace_array, $additional_params = array() ) { /* // Default in api-emails.php: // $mail_headers = 'From: ' . $this->get_from__name() . ' <' . $this->get_from__email_address() . '> ' . "\r\n" ; // $mail_headers .= 'Content-Type: ' . $this->get_content_type() . "\r\n" ; // // $mail_headers = "From: $mail_sender\n"; // preg_match('/<(.*)>/', $mail_sender, $simple_email_matches ); // $reply_to_email = ( count( $simple_email_matches ) > 1 ) ? $simple_email_matches[1] : $mail_sender; // $mail_headers .= 'Reply-To: ' . $reply_to_email . "\n"; // $mail_headers .= 'X-Sender: ' . $reply_to_email . "\n"; // $mail_headers .= 'Return-Path: ' . $reply_to_email . "\n"; */ //debuge($mail_headers, $email_id , $fields_values , $replace_array); if ( ( $email_id == 'new_admin' ) // Only for email: "New Booking to Admin" || ( isset( $additional_params['reply'] ) ) ){ //FixIn: 9.7.3.17 if ( ( ! empty( $replace_array['email'] ) ) && ( ! empty( $fields_values['enable_replyto'] ) ) && ( 'On' == $fields_values['enable_replyto'] ) ){ // Get email from the booking form. $reply_to_email = sanitize_email( $replace_array['email'] ); if ( ! empty( $reply_to_email ) ) { $mail_headers .= 'Reply-To: ' . $reply_to_email . "\r\n"; } // $mail_headers .= 'X-Sender: ' . $reply_to_email . "\r\n" ; // $mail_headers .= 'Return-Path: ' . $reply_to_email . "\r\n" ; } } return $mail_headers; } add_filter( 'wpbc_email_api_get_headers_after', 'wpbc_email_api_get_headers_after', 10, 5 ); // Hook fire in api-email.php /** * Check if we can send Email - block sending in live demos * * @param bool $is_send_email * @param string $email_id * @param array $fields_values - list of params to parse: 'content', 'header_content', 'footer_content' for different languges, etc .... * @return bool */ function wpbc_email_api_is_allow_send( $is_send_email, $email_id, $fields_values ) { //debuge($fields_values); if ( wpbc_is_this_demo() ) $is_send_email = false; return $is_send_email; } add_filter( 'wpbc_email_api_is_allow_send', 'wpbc_email_api_is_allow_send', 10, 3 ); // Hook fire in api-email.php /** * Show warning about not sending emails, and reason about this. * * @param object $wp_error_object - WP Error object * @param string $error_description - Description */ function wpbc_email_sending_error( $wp_error_object, $error_description = '' ) { if ( ( defined( 'WPBC_AJAX_ERROR_CATCH' ) ) && ( WPBC_AJAX_ERROR_CATCH ) ) { return false; } //FixIn: 9.2.1.10 if ( empty( $error_description ) ) { // $error_description = __( 'Unknown exception', 'booking' ) . '.'; // Overwrite to show error, if no description ??? } if ( ! empty( $error_description ) ) { $error_description = '' . __('Error', 'booking') . '! ' . __('Email was not sent. An error occurred.', 'booking') . ' ' . $error_description; // Admin side if ( function_exists( 'wpbc_show_message' ) ) { wpbc_show_message ( $error_description , 15 , 'error'); } // Front-end ?> <script type="text/javascript"> if (typeof( wpbc_front_end__show_message__warning_under_element ) == 'function') { wpbc_front_end__show_message__warning_under_element( '.booking_form' , '<?php echo esc_js( $error_description ) ; ?>' ); } </script> <?php } else { // Error that have no description. Its can be Empty Object like this: WP_Error Object( 'errors' => array(), 'error_data' => array() ), or NOT // debuge( $wp_error_object ); } } add_action('wpbc_email_sending_error', 'wpbc_email_sending_error', 10, 2); wpbc-debug.php 0000666 00000023420 15165312011 0007274 0 ustar 00 <?php /** * @version 1.1 * @package Any * @category Dubug info showing * @author wpdevelop * * @web-site https://wpbookingcalendar.com/ * @email info@wpbookingcalendar.com * * @modified 09.09.2015 */ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // D e b u g f u n c t i o n s /////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* * Show values of the arguments list */ if (!function_exists ('debuge')) { function debuge() { $numargs = func_num_args(); $var = func_get_args(); $makeexit = is_bool( $var[count($var)-1] ) ? $var[ count($var) - 1 ]:false; echo "<div><pre class='prettyprint linenums'>"; //FixIn: 8.8.3.11 foreach ( $var as $ind => $item ) { echo "\n<strong> [$ind] " . gettype( $item ) . "</strong>\n"; if ( 'string' === gettype( $item ) ) { echo( htmlentities( $item ) ); } else { print_r( $item ); } } echo "</pre></div>"; echo '<script type="text/javascript"> jQuery(".ajax_respond_insert, .ajax_respond").show(); </script>'; if ( $makeexit ) { echo '<div style="font-size:18px;float:right;">' . get_num_queries(). '/' . timer_stop(0, 3) . 'qps</div>'; exit; } } } /* * Show debug info for Visitors. This function used for showing debug info for visitors at the website. * if first parameter is 'clear', second parameter it's how many mesages (DIV elements) to show in exist view. Negative value, calculate DIV elements from bottom. * Example: show_debug( 'clear', -3 ); - show 3 last DIV elements. */ if (!function_exists ('show_debug')) { //FixIn: 8.8.3.18 function show_debug() { //TODO: 2023-09-19 13:42 Need to remake it relative to new 'create_booking.php' functionality. We can not simply echo here !!!! if ( is_admin() && ( defined( 'DOING_AJAX' ) ) && ( DOING_AJAX ) ) { }else { return; } //$numargs = func_num_args(); $var = func_get_args(); if ( 'clear' === $var[0] ) { echo '<script type="text/javascript"> jQuery(".ajax_respond_insert div").hide(); jQuery(".ajax_respond_insert div").slice( '.$var[1].' ).show(); </script>'; } $milliseconds = round(microtime(true) * 1000); echo "<div class='wpbc_debug wpbc_debug_{$milliseconds}'><pre class='prettyprint linenums'>"; foreach ( $var as $ind => $item ) { echo "\n"; if ( 'string' === gettype( $item ) ) { echo( htmlentities( $item ) ); } else { print_r( $item ); } } echo "</pre></div>"; echo '<script type="text/javascript"> jQuery(".ajax_respond_insert, .ajax_respond").show(); </script>'; echo '<script type="text/javascript"> if ( jQuery(".ajax_respond_insert").length == 0 ) {jQuery(".wpbc_debug_'.$milliseconds.'").hide()}; </script>'; } } /* * Show Speed of the execution and number of queries. */ if (!function_exists ('debuge_speed')) { function debuge_speed() { echo '<div style="font-size:18px;float:right;">' . get_num_queries(). '/' . timer_stop(0, 3) . 'qps</div>'; } } /** * Show error info */ if (!function_exists ('debuge_error')) { function debuge_error( $msg , $file_name='', $line_num=''){ echo get_debuge_error( $msg , $file_name , $line_num ); } } if (!function_exists ('get_debuge_error')) { function get_debuge_error( $msg , $file_name='', $line_num=''){ return wpbc_get_debuge_error( $msg , $file_name, $line_num ); } } function wpbc_get_debuge_error( $msg , $file_name='', $line_num=''){ $ver_num = ( ! defined('WPDEV_BK_VERSION') ) ? '' : '|V:' . WPDEV_BK_VERSION ; $last_db_error = ''; global $EZSQL_ERROR; if ( ( ! empty( $EZSQL_ERROR ) ) && ( is_array( $EZSQL_ERROR ) ) && ( isset( $EZSQL_ERROR[ ( count( $EZSQL_ERROR ) - 1 ) ] ) ) ){ $last_db_error2 = $EZSQL_ERROR[ (count($EZSQL_ERROR)-1)]; if ( (isset($last_db_error2['query'])) && (isset($last_db_error2['error_str'])) ) { $str = str_replace( array( '"', "'" ), '', $last_db_error2['error_str'] ); $query = str_replace( array( '"', "'" ), '', $last_db_error2['query'] ); $str = htmlspecialchars( $str, ENT_QUOTES ); $query = htmlspecialchars( $query , ENT_QUOTES ); $last_db_error = $str ; $last_db_error .= '::<span style="color:#300;">'.$query.'</span>'; } } return $msg . '<br /><span style="font-size:11px;"> [' . 'F:' . str_replace( dirname( $file_name ) , '' , $file_name ) . '| L:' . $line_num . $ver_num . '| DB:' . $last_db_error . '] </span>' ; } // Usage: if ( function_exists ('wpbc_check_post_key_max_number')) { wpbc_check_post_key_max_number(); } if ( ! function_exists ('wpbc_check_post_key_max_number')) { function wpbc_check_post_key_max_number() { /* $post_max_totalname_length = intval( ( ini_get( 'suhosin.post.max_totalname_length' ) ) ? ini_get( 'suhosin.post.max_totalname_length' ) : '9999999' ); $request_max_totalname_length = intval( ( ini_get( 'suhosin.request.max_totalname_length' ) ) ? ini_get( 'suhosin.request.max_totalname_length' ) : '9999999' ); $post_max_name_length = intval( ( ini_get( 'suhosin.post.max_name_length' ) ) ? ini_get( 'suhosin.post.max_name_length' ) : '9999999' ); $request_max_varname_length = intval( ( ini_get( 'suhosin.request.max_varname_length' ) ) ? ini_get( 'suhosin.request.max_varname_length' ) : '9999999' ); */ $php_ini_vars = array( 'suhosin.post.max_totalname_length' , 'suhosin.request.max_totalname_length' , 'suhosin.post.max_name_length' , 'suhosin.request.max_varname_length' ); foreach ( $_POST as $key_name => $post_value ) { $key_length = strlen( $key_name ); foreach ( $php_ini_vars as $php_ini_var ) { $php_ini_var_length = intval( ( ini_get( $php_ini_var ) ) ? ini_get( $php_ini_var ) : '9999999' ); if ( $key_length > $php_ini_var_length ) { wpbc_show_message_in_settings( 'Your php.ini configuration limited to ' . '<strong> '. $php_ini_var . ' = ' . $php_ini_var_length . '</strong>.' . ' ' . 'Plugin require at least ' . ( intval( $key_length ) + 1 ). ', ' . 'for saving option: ' . '<strong>'. $key_name . '</strong>' , 'error' , __('Error' ,'booking') . '.' ); } } } } } /** * Show System debug log for Beta features. * * @param mixed $show_debug_info * * Example of usage: * $is_show_debug_info = ( ( get_bk_option( 'booking_is_show_system_debug_log' ) == 'On' ) ? true : false ); if ( $is_show_debug_info ) add_action( 'wpbc_show_debug', 'wpbc_start_showing_debug', 10, 1 ); ... //- do_action( 'wpbc_show_debug', array( 'after import' , $ics_array) ); ... if ( $is_show_debug_info ) remove_action( 'wpbc_show_debug', 'wpbc_start_showing_debug', 10 ); */ function wpbc_start_showing_debug( $show_debug_info ) { //FixIn: 7.2.1.15 // Make first item as header - bold if ( is_array( $show_debug_info ) ) $show_debug_info[0] = '<strong>' . $show_debug_info[0] . '</strong>'; // Get evrything in human redable view $show_debug_info = print_r ( $show_debug_info, true ); // Remove unnecesary new lines $show_debug_info = preg_replace( '/Array[\n\r\s]*\(/', 'array(', $show_debug_info ); $show_debug_info = preg_replace( '/\)\n/', ")", $show_debug_info ); // Show echo "<div><pre class='prettyprint linenums'>"; echo( $show_debug_info ); echo "</pre></div>"; echo '<hr/>'; // For system debug log in Ajax request show it ?><script type="text/javascript"> jQuery( '.wpbc_system_info_log' ).show(); </script><?php } /** * Show Top Notice in Admin panel by writing in-line JS * * @param string $message * @param string $message_type - (default notice) notice | error | warning | info | success * @param int $seconds_to_show - (default 3000) - delay in microseconds * @param bool $is_append - (default true) - append notice instead of replacing */ function wpbc_admin_show_top_notice( $message, $message_type = 'info', $seconds_to_show = 3000, $is_append = true ) { if ( ! is_admin() ) return; // Show Notice in Admin header ?><script type="text/javascript"> wpbc_admin_show_message( '<?php echo html_entity_decode( esc_js( $message ), ENT_QUOTES ); ?>' , '<?php echo $message_type; ?>' , <?php echo intval( $seconds_to_show ); ?> , <?php echo ( $is_append ? 'true' : 'false' ); ?> ); </script><?php } add_action( 'wpbc_admin_show_top_notice', 'wpbc_admin_show_top_notice', 10, 3 ); .mad-root 0000666 00000000000 15165312011 0006254 0 ustar 00 wpbc-include.php 0000666 00000035432 15165312011 0007637 0 ustar 00 <?php /** * @version 1.0 * @package Booking Calendar * @subpackage Files Loading * @category Bookings * * @author wpdevelop * @link https://wpbookingcalendar.com/ * @email info@wpbookingcalendar.com * * @modified 29.09.2015 */ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly //////////////////////////////////////////////////////////////////////////////// // L O A D F I L E S //////////////////////////////////////////////////////////////////////////////// require_once( WPBC_PLUGIN_DIR . '/core/wpbc-debug.php' ); // Debug = Package: WPBC = require_once( WPBC_PLUGIN_DIR . '/core/wpbc-core.php' ); // Core require_once( WPBC_PLUGIN_DIR . '/core/any/class-css-js.php' ); // Abstract. Loading CSS & JS files = Package: Any = require_once( WPBC_PLUGIN_DIR . '/core/any/class-admin-settings-api.php' ); // Abstract. Settings API. require_once( WPBC_PLUGIN_DIR . '/core/any/class-admin-page-structure.php' ); // Abstract. Page Structure in Admin Panel require_once( WPBC_PLUGIN_DIR . '/core/any/class-admin-menu.php' ); // CLASS. Menus of plugin require_once( WPBC_PLUGIN_DIR . '/core/any/admin-bs-ui.php' ); // Functions. Toolbar BS UI Elements if( is_admin() ) { require_once WPBC_PLUGIN_DIR . '/core/class/wpbc-class-dismiss.php'; // Class - Dismiss require_once WPBC_PLUGIN_DIR . '/core/class/wpbc-class-notices.php'; // Class - Showing different messages and alerts. Including some predefined static messages. require_once WPBC_PLUGIN_DIR . '/core/class/wpbc-class-welcome.php'; // Class - Welcome Page - info about new version. } // Functions require_once( WPBC_PLUGIN_DIR . '/includes/_functions/nonce_func.php' ); // Nonce functions - front-end excluding require_once( WPBC_PLUGIN_DIR . '/includes/_functions/regex_str.php' ); // String and Regex functions for shortcodes require_once( WPBC_PLUGIN_DIR . '/includes/_functions/parse_booking_data.php' ); // Booking form data parsing and replacement require_once( WPBC_PLUGIN_DIR . '/core/wpbc_functions.php' ); // Functions require_once( WPBC_PLUGIN_DIR . '/core/wpbc_functions_dates.php' ); // Function Dates New in 9.8 require_once( WPBC_PLUGIN_DIR . '/core/form_parser.php' ); // Parser for booking form New in 9.8 require_once( WPBC_PLUGIN_DIR . '/core/custom_html_shortcodes.php' ); // Integrate Custom booking form HTML shortcodes require_once( WPBC_PLUGIN_DIR . '/core/wpbc-dates.php' ); // Dates require_once( WPBC_PLUGIN_DIR . '/core/wpbc_welcome.php' ); // Welcome Panel Functions // New Engine //FixIn: 9.8.0.4 require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/wpbc_cache.php' ); // Caching different requests to DB //FixIn: 9.8.0.4 require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/booking_date_class.php' ); // Functions for booking dates require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/dates_times_support.php' ); // Functions for booking DATES & TIME require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/resource_support.php' ); // Functions for resources support require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/capacity.php' ); // Get Dates from DB - Capacity, Availability and more ... require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/get_times_fields.php' ); // Get Times Fields options from Booking Form require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/aggregate.php' ); // Aggregate functions for 'aggregate' parameter in shortcode require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/where_to_save.php' ); // Check where to save booking require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/create_booking.php' ); // Functions to create new bookings require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/confirmation.php' ); // Confirmation section - get data require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/confirmation_page.php' ); // Confirmation Page require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/calendar_load.php' ); // Scripts to load calendar require_once( WPBC_PLUGIN_DIR . '/includes/_capacity/captcha_simple_text.php' ); // Simple text captcha checking require_once( WPBC_PLUGIN_DIR . '/core/wpbc-translation.php' ); // Translation, must be loaded after '/core/wpbc-core.php', because there defined add_bk_filter(), etc... //FixIn: 8.9.4.12 if ( file_exists( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations.php' ) ){ require_once( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations.php' ); // All Translation Terms //FixIn: 8.7.3.6 if ( file_exists( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations1.php' ) ){ require_once( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations1.php' ); } if ( file_exists( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations2.php' ) ){ require_once( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations2.php' ); } if ( file_exists( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations3.php' ) ){ require_once( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations3.php' ); } if ( file_exists( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations4.php' ) ){ require_once( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations4.php' ); } if ( file_exists( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations5.php' ) ){ require_once( WPBC_PLUGIN_DIR . '/core/lang/wpbc_all_translations5.php' ); } } require_once( WPBC_PLUGIN_DIR . '/includes/publish/wpbc-create-pages.php' ); // Create pages for different purposes //FixIn: 9.6.2.10 require_once( WPBC_PLUGIN_DIR . '/includes/publish/wpbc-publish-shortcode.php' ); // Publish Booking Calendar shortcodes into the Pages //FixIn: 9.8.15.5 require_once( WPBC_PLUGIN_DIR . '/core/wpbc-emails.php' ); // Emails // JS & CSS require_once( WPBC_PLUGIN_DIR . '/core/wpbc-css.php' ); // Load CSS require_once( WPBC_PLUGIN_DIR . '/core/wpbc-js.php' ); // Load JavaScript require_once( WPBC_PLUGIN_DIR . '/core/wpbc-js-vars.php' ); // Define JavaScript Vars // Admin UI require_once( WPBC_PLUGIN_DIR . '/core/admin/wpbc-toolbars.php' ); // Toolbar - BS UI Elements require_once( WPBC_PLUGIN_DIR . '/core/admin/wpbc-sql.php' ); // Data Engine for Booking Listing / Calendar Overview pages //FixIn: 9.6.3.5 //FixIn: 8.6.1.13 require_once( WPBC_PLUGIN_DIR . '/core/timeline/flex-timeline.php' ); // New. Flex. Timeline require_once( WPBC_PLUGIN_DIR . '/core/admin/wpbc-dashboard.php' ); // Dashboard Widget //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Admin Pages //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //FixIn: 9.2.1 //FixIn: 9.6.3.5 require_once( WPBC_PLUGIN_DIR . '/includes/_request/wpbc_request.php' ); //FixIn: 9.3.1.2 // Class for sanitizing $_REQUEST parameters and saving or getting it from DB require_once( WPBC_PLUGIN_DIR . '/includes/_booking_hash/booking_hash.php' ); //FixIn: 9.2.3.3 require_once( WPBC_PLUGIN_DIR . '/includes/_news/wpbc_news.php' ); // Booking Listing require_once( WPBC_PLUGIN_DIR . '/includes/_toolbar_ui/toolbar_ui.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/_listing_css_js/listing_ui.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/_pagination/pagination.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/print/bookings_print.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/page-bookings/bookings__sql.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/page-bookings/bookings__actions.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/page-bookings/bookings__listing.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/page-bookings/bookings__page.php' ); // Booking > Availability page require_once( WPBC_PLUGIN_DIR . '/includes/page-availability/availability__activation.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/page-availability/availability__toolbar_ui.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/page-availability/availability__class.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/page-availability/availability__resource.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/page-availability/availability__page.php' ); // Booking > Customize page //FixIn: 9.8.0.1 if ( WPBC_customize_plugin ) { //FixIn: 9.8.0.2 require_once( WPBC_PLUGIN_DIR . '/includes/page-customize/customize__templates.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/page-customize/customize__ajax_request.php' ); require_once( WPBC_PLUGIN_DIR . '/includes/page-customize/customize__page.php' ); } // Booking > Setup page //FixIn: 10.2.0.1 if ( WPBC_setup_plugin ) { //FixIn: 9.8.0.2 require_once( WPBC_PLUGIN_DIR . '/includes/page-setup/setup__page.php' ); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //FixIn: 9.6.3.5 require_once( WPBC_PLUGIN_DIR . '/core/admin/page-timeline.php' ); // Timeline require_once( WPBC_PLUGIN_DIR . '/core/admin/page-new.php' ); // Add New Booking page require_once( WPBC_PLUGIN_DIR . '/core/admin/wpbc-settings-functions.php' ); // Support functions for Booking > Settings General page require_once( WPBC_PLUGIN_DIR . '/core/admin/page-settings.php' ); // Settings page require_once( WPBC_PLUGIN_DIR . '/core/admin/api-settings.php' ); // Settings API require_once( WPBC_PLUGIN_DIR . '/core/admin/wpbc-gutenberg.php' ); // Settings page //////////////////////////////////////////////////////////////////////////////// require_once( WPBC_PLUGIN_DIR . '/includes/page-form-simple/preview_form.php' ); // Preview - Booking Form require_once( WPBC_PLUGIN_DIR . '/includes/page-form-simple/templates_form_simple.php' ); // Templates - Simple Booking Form if ( file_exists( WPBC_PLUGIN_DIR.'/inc/_ps/personal.php' ) ){ require_once WPBC_PLUGIN_DIR . '/inc/_ps/personal.php'; } else { require_once( WPBC_PLUGIN_DIR . '/includes/page-resource-free/page-resource-free.php' ); // Resource page for Free version require_once( WPBC_PLUGIN_DIR . '/core/admin/page-up.php' ); // Up //FixIn: 8.0.1.6 require_once( WPBC_PLUGIN_DIR . '/includes/page-form-simple/page-form-simple.php' ); // Booking Form Simple require_once( WPBC_PLUGIN_DIR . '/core/admin/page-email-new-admin.php' ); // Email - New admin. require_once( WPBC_PLUGIN_DIR . '/core/admin/page-email-new-visitor.php' ); // Email - New visitor require_once( WPBC_PLUGIN_DIR . '/core/admin/page-email-deny.php' ); // Email - Deny - set pending require_once( WPBC_PLUGIN_DIR . '/core/admin/page-email-approved.php' ); // Email - Approved require_once( WPBC_PLUGIN_DIR . '/core/admin/page-email-trash.php' ); // Email - Trash require_once( WPBC_PLUGIN_DIR . '/core/admin/page-email-deleted.php' ); // Email - Deleted / completely erase require_once( WPBC_PLUGIN_DIR . '/core/admin/page-ics-general.php' ); // General ICS Help Settings page //FixIn: 8.1.1.10 require_once( WPBC_PLUGIN_DIR . '/core/admin/page-ics-import.php' ); // Import ICS Help Settings page //FixIn: 8.0 require_once( WPBC_PLUGIN_DIR . '/core/admin/page-ics-export.php' ); // Export ICS Feeds Settings page //FixIn: 8.0 require_once( WPBC_PLUGIN_DIR . '/core/admin/page-import-gcal.php' ); // Import from Google Calendar Settings page } require_once( WPBC_PLUGIN_DIR . '/includes/_feedback/feedback.php'); //FixIn: 9.2.3.6 // Old Working require_once WPBC_PLUGIN_DIR . '/core/lib/wpdev-booking-widget.php'; // W i d g e t s require_once WPBC_PLUGIN_DIR . '/js/captcha/captcha.php'; // C A P T C H A require_once WPBC_PLUGIN_DIR . '/core/lib/wpbc-calendar-legend.php'; // Calendar Legend //FixIn: 9.4.3.6 require_once WPBC_PLUGIN_DIR . '/core/lib/wpdev-booking-class.php'; // C L A S S B o o k i n g require_once WPBC_PLUGIN_DIR . '/core/lib/wpbc-booking-new.php'; // N e w require_once WPBC_PLUGIN_DIR . '/core/lib/wpbc-cron.php'; // CRON @since: 5.2.0 if( is_admin() ) { //FixIn: 9.9.0.15 require_once WPBC_PLUGIN_DIR . '/includes/ui_modal__shortcodes/shortcode_tpl_js_loader.php'; // Templates JS Loader require_once WPBC_PLUGIN_DIR . '/includes/ui_modal__shortcodes/sh_tpl_booking.php'; // Booking - Shortcode Config Content require_once WPBC_PLUGIN_DIR . '/includes/ui_modal__shortcodes/sh_tpl_booking_calendar.php'; require_once WPBC_PLUGIN_DIR . '/includes/ui_modal__shortcodes/sh_tpl_booking_select.php'; require_once WPBC_PLUGIN_DIR . '/includes/ui_modal__shortcodes/sh_tpl_booking_timeline.php'; require_once WPBC_PLUGIN_DIR . '/includes/ui_modal__shortcodes/sh_tpl_booking_form.php'; require_once WPBC_PLUGIN_DIR . '/includes/ui_modal__shortcodes/sh_tpl_booking_search.php'; require_once WPBC_PLUGIN_DIR . '/includes/ui_modal__shortcodes/sh_tpl_booking_other.php'; require_once WPBC_PLUGIN_DIR . '/includes/ui_modal__shortcodes/sh_tpl_booking_import.php'; require_once WPBC_PLUGIN_DIR . '/includes/ui_modal__shortcodes/sh_tpl_booking_listing.php'; require_once WPBC_PLUGIN_DIR . '/includes/ui_modal__shortcodes/tiny-button-popup.php'; // PopUp for shortcodes and Buttons in Edit toolbar } require_once WPBC_PLUGIN_DIR . '/core/sync/wpbc-gcal-class.php'; //DONE: in 7.0 // Google Calendar Feeds Import @since: 5.2.0 - v.3.0 API support @since: 5.4.0 require_once WPBC_PLUGIN_DIR . '/core/sync/wpbc-gcal.php'; //DONE: in 7.0 // Sync Google Calendar Events with WPBC @since: 5.2.0 - v.3.0 API support @since: 5.4.0 require_once( WPBC_PLUGIN_DIR . '/core/any/activation.php' ); require_once( WPBC_PLUGIN_DIR . '/core/wpbc-activation.php' ); require_once( WPBC_PLUGIN_DIR . '/core/wpbc-dev-api.php' ); // API for Booking Calendar integrations //FixIn: 8.0 make_bk_action( 'wpbc_loaded_php_files' ); wpbc_functions.php 0000666 00000460347 15165312011 0010315 0 ustar 00 <?php /** * @version 1.0 * @package Booking Calendar * @subpackage Support Functions * @category Functions * * @author wpdevelop * @link https://wpbookingcalendar.com/ * @email info@wpbookingcalendar.com * * @modified 29.09.2015 */ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly // <editor-fold defaultstate="collapsed" desc=" == Booking details | replace / fields functions == " > /** * Get booking resource title (translated/localized) * * @param $resource_id * * @return string */ function wpbc_get_resource_title( $resource_id = 1 ) { $resource_title = ''; if ( function_exists( 'wpbc_db__get_resource_title' ) ) { $resource_title = wpbc_db__get_resource_title( $resource_id ); if (! empty($resource_title)) { $resource_title = wpbc_lang( $resource_title ); } } return $resource_title; } /** * Check, if exist booking for this hash. If existed, get Email of this booking * * @param string $booking_hash - booking hash. * @param string $booking_data_key - booking field key - default 'email'. * * @return bool - booking data field */ function wpbc_get__booking_data_field__by_booking_hash( $booking_hash, $booking_data_key = 'email' ){ //FixIn: 8.1.3.5 $return_val = false; // $booking_hash = '0d55671fd055fd64423294f89d6b58e6'; // debugging if ( ! empty( $booking_hash ) ) { $my_booking_id_type = wpbc_hash__get_booking_id__resource_id( $booking_hash ); if ( ! empty( $my_booking_id_type ) ) { list( $booking_id, $resource_id ) = $my_booking_id_type; $booking_data = wpbc_db_get_booking_details( $booking_id ); if ( ! empty( $booking_data ) ) { $booking_details = wpbc_get_booking_different_params_arr( $booking_id, $booking_data->form, $resource_id ); if ( isset( $booking_details[ $booking_data_key ] ) ) { $return_val = $booking_details[ $booking_data_key ]; } } } } return $return_val; } /** * Get booking details object from DB * * @param $booking_id - int * * @return mixed - booking details or false if not found * Example: * stdClass Object * ( * [booking_id] => 26 * [trash] => 0 * [sync_gid] => * [is_new] => 0 * [status] => * [sort_date] => 2018-02-27 00:00:00 * [modification_date] => 2018-02-18 12:49:30 * [form] => text^selected_short_dates_hint3^02/27/2018 - 03/02/2018~text^days_number_hint3^4~text^cost_hint3^40.250 $~text^name3^Victoria~text^secondname3^vica~email^email3^vica@wpbookingcalendar.com~text^phone3^test booking ~selectbox-one^visitors3^1 * [hash] => 0d55671fd055fd64423294f89d6b58e6 * [booking_type] => 3 * [remark] => * [cost] => 40.25 * [pay_status] => 151895097121.16 * [pay_request] => 0 * ) */ function wpbc_db_get_booking_details( $booking_id ){ //FixIn: 8.1.3.5 global $wpdb; $slct_sql = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}booking WHERE booking_id = %d LIMIT 0,1", $booking_id ); $sql_results = $wpdb->get_row( $slct_sql ); if ( ! empty( $sql_results ) ) { return $sql_results; } else { return false; } } /** * Get booking D a t e s from DB - array of objects * * @param $booking_id - int * * @return mixed - booking dates array or false if not found * */ function wpbc_db_get_booking_dates( $booking_id ){ global $wpdb; $sql = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}bookingdates as dt WHERE dt.booking_id = %d ", $booking_id ); if ( class_exists( 'wpdev_bk_biz_l' ) ) { $sql .= " ORDER BY booking_id, type_id, booking_date "; } else { $sql .= " ORDER BY booking_id, booking_date "; } $sql_results = $wpdb->get_results( $sql ); if ( ! empty( $sql_results ) ) { return $sql_results; } else { return false; } } /** * Get booking modification date from DB * @param $booking_id * * @return string */ function wpbc_db_get_booking_modification_date( $booking_id ){ //FixIn: 8.0.1.7 global $wpdb; $modification_date = ' ' . $wpdb->get_var( $wpdb->prepare( "SELECT modification_date FROM {$wpdb->prefix}booking WHERE booking_id = %d " , $booking_id ) ); return $modification_date; } /** * Get user IP * * @return mixed|string */ function wpbc_get_user_ip() { if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) { $userIP = $_SERVER['HTTP_CLIENT_IP']; } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { $userIP = $_SERVER['HTTP_X_FORWARDED_FOR']; } elseif ( isset( $_SERVER['HTTP_X_FORWARDED'] ) ) { $userIP = $_SERVER['HTTP_X_FORWARDED']; } elseif ( isset( $_SERVER['HTTP_FORWARDED_FOR'] ) ) { $userIP = $_SERVER['HTTP_FORWARDED_FOR']; } elseif ( isset( $_SERVER['HTTP_FORWARDED'] ) ) { $userIP = $_SERVER['HTTP_FORWARDED']; } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) { $userIP = $_SERVER['REMOTE_ADDR']; } else { $userIP = ""; } return $userIP; } /** * Get IP of Server * * @return string */ function wpbc_get_server_ip() { //FixIn: 9.8.14.3 $ip_address = ''; if ( array_key_exists( 'SERVER_ADDR', $_SERVER ) ) { $ip_address = $_SERVER['SERVER_ADDR']; } else if ( array_key_exists( 'LOCAL_ADDR', $_SERVER ) ) { $ip_address = $_SERVER['LOCAL_ADDR']; } return $ip_address; } /** * Get different Booking Fields as array for replace * * @param int $booking_id - ID of booking // 999 * @param string $formdata - booking form data content // selectbox-one^rangetime4^10:00 - 12:00~text^name4^Jo~text^secondname4^Smith~email^email4^smith@wpbookingcalendar.com~... * @param int $booking_resource_id - booking resource type // 4 * * @return array * * Example: * [ * [booking_id] => 26 * [id] => 26 * [days_input_format] => 01.03.2018,02.03.2018,27.02.2018,28.02.2018 * [days_only_sql] => 2018-02-27,2018-02-28,2018-03-01,2018-03-02 * [dates_sql] => 2018-02-27 00:00:00, 2018-02-28 00:00:00, 2018-03-01 00:00:00, 2018-03-02 00:00:00 * [check_in_date_sql] => 2018-02-27 00:00:00 * [check_out_date_sql] => 2018-03-02 00:00:00 * [dates] => 02/27/2018 - 03/02/2018 * [check_in_date] => 02/27/2018 * [check_out_date] => 03/02/2018 * [check_out_plus1day] => 03/03/2018 * [dates_count] => 4 * [days_count] => 4 * [nights_count] => 3 * [check_in_date_hint] => 02/27/2018 * [check_out_date_hint] => 03/02/2018 * [start_time_hint] => 00:00 * [end_time_hint] => 00:00 * [selected_dates_hint] => 02/27/2018, 02/28/2018, 03/01/2018, 03/02/2018 * [selected_timedates_hint] => 02/27/2018, 02/28/2018, 03/01/2018, 03/02/2018 * [selected_short_dates_hint] => 02/27/2018 - 03/02/2018 * [selected_short_timedates_hint] => 02/27/2018 - 03/02/2018 * [days_number_hint] => 4 * [nights_number_hint] => 3 * [siteurl] => http://beta * [resource_title] => Apartment A * [bookingtype] => Apartment A * [remote_ip] => 127.0.0.1 * [user_agent] => Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 * [request_url] => http://beta/wp-admin/post.php?post=1473&action=edit * [current_date] => 02/18/2018 * [current_time] => 14:11 * [cost_hint] => 40.250 $ * [name] => Victoria * [secondname] => vica * [email] => vica@wpbookingcalendar.com * [phone] => test booking * [visitors] => 1 * [booking_resource_id] => 3 * [resource_id] => 3 * [type_id] => 3 * [type] => 3 * [resource] => 3 * [content] => '........' * [moderatelink] => http://beta/wp-admin/admin.php?page=wpbc&view_mode=vm_listing&tab=actions&wh_booking_id=26 * [visitorbookingediturl] => http://beta/?booking_hash=0d55671fd055fd64423294f89d6b58e6 * [visitorbookingcancelurl] => http://beta/?booking_hash=0d55671fd055fd64423294f89d6b58e6&booking_cancel=1 * [visitorbookingpayurl] => http://beta/?booking_hash=0d55671fd055fd64423294f89d6b58e6&booking_pay=1 * [bookinghash] => 0d55671fd055fd64423294f89d6b58e6 * [db_cost] => 40.25 * [db_cost_hint] => 40.250 $ * [modification_date] => 2018-02-18 12:49:30 * [modification_year] => 2018 * [modification_month] => 02 * [modification_day] => 18 * [modification_hour] => 12 * [modification_minutes] => 49 * [modification_seconds] => 30 * ] */ function wpbc_get_booking_different_params_arr( $booking_id, $formdata, $booking_resource_id = 1 ) { $replace = array(); // Resources /////////////////////////////////////////////////////////////// $bk_title = wpbc_get_resource_title( $booking_resource_id ); //////////////////////////////////////////////////////////////////////////// // Dates Dif. Formats //////////////////////////////////////////////////////////////////////////// // -> '2023-10-09 12:00:01, 2023-10-09 20:00:02' $sql_dates_format = wpbc_db__get_sql_dates__in_booking__as_str( $booking_id ); // 2016-08-03 16:00:01, 2016-08-03 18:00:02 $sql_dates_only = explode(',',$sql_dates_format); $sql_days_only_array = array(); $days_as_in_form_array = array(); foreach ( $sql_dates_only as $sql_day_only ) { $sql_days_only_array[] = trim( substr($sql_day_only, 0, 11 ) ); $days_as_in_form_array[] = date_i18n( "d.m.Y", strtotime( trim( substr($sql_day_only, 0, 11 ) ) ) ); } $sql_days_only_array = array_unique( $sql_days_only_array ); sort( $sql_days_only_array ); $sql_days_only = implode( ',', $sql_days_only_array ); $days_as_in_form_array = array_unique( $days_as_in_form_array ); sort( $days_as_in_form_array ); $days_as_in_form = implode( ',', $days_as_in_form_array ); $sql_days_only_with_full_times = array(); foreach ( $sql_days_only_array as $sql_day ) { $sql_days_only_with_full_times[] = $sql_day . ' 00:00:00'; } $sql_days_only_with_full_times = implode(',', $sql_days_only_with_full_times ); if ( get_bk_option( 'booking_date_view_type' ) == 'short' ) { $formated_booking_dates = wpbc_get_dates_short_format( $sql_dates_format ); } else { $formated_booking_dates = wpbc_get_dates_comma_string_localized( $sql_dates_format ); } $sql_dates_format_check_in_out = explode(',', $sql_dates_format ); $my_check_in_date = wpbc_get_dates_comma_string_localized( $sql_dates_format_check_in_out[0] ); $my_check_out_date = wpbc_get_dates_comma_string_localized( $sql_dates_format_check_in_out[ count( $sql_dates_format_check_in_out ) - 1 ] ); $my_check_out_plus1day = wpbc_datetime_localized( date( 'Y-m-d H:i:s' , strtotime( $sql_dates_format_check_in_out[ count( $sql_dates_format_check_in_out ) - 1 ] . " +1 day" ) ) ); $date_format = get_bk_option( 'booking_date_format'); $check_in_date_hint = wpbc_date_localized( $sql_days_only_array[0] ); $check_out_date_hint = wpbc_date_localized( $sql_days_only_array[ ( count( $sql_days_only_array ) - 1 ) ] ); //FixIn: 9.7.3.16 $cancel_date_hint = wpbc_datetime_localized( date( 'Y-m-d H:i:s' , strtotime( '-14 days', strtotime( $sql_days_only_array[0] ) ) ) ); //FixIn: 10.0.0.31 $pre_checkin_date_hint = wpbc_datetime_localized( date( 'Y-m-d H:i:s' , strtotime( '-' . intval( get_bk_option( 'booking_number_for_pre_checkin_date_hint' ) ) . ' days', strtotime( $sql_days_only_array[0] ) ) ) ); // Booking Times ------------------------------------------------------------------------------------------- $start_end_time = wpbc_get_times_in_form( $formdata, $booking_resource_id ); // false || if ( $start_end_time !== false ) { $start_time = $start_end_time[0]; // array('00','00','01'); $end_time = $start_end_time[1]; // array('00','00','01'); } else { $start_time = array('00','00','00'); $end_time = array('00','00','00'); } //TODO: continue here with replacing date_i18n to wpbc_loc_ ... $start_time_hint = wpbc_time_localized( implode( ':', $start_time ) ); $end_time_hint = wpbc_time_localized( implode( ':', $end_time ) ); //FixIn: 9.5.1.3 $check_in_date_sql = wpbc_date_localized( $sql_days_only_array[0], 'Y-m-d' ); $check_out_date_sql = wpbc_date_localized( $sql_days_only_array[ ( count( $sql_days_only_array ) - 1 ) ], 'Y-m-d' ); $start_time_sql = wpbc_time_localized( implode( ':', $start_time ), 'H:i' ); $end_time_sql = wpbc_time_localized( implode( ':', $end_time ) , 'H:i' ); //////////////////////////////////////////////////////////////////////////// // Other /////////////////////////////////////////////////////////////////// $replace[ 'booking_id' ] = $booking_id; $replace[ 'id' ] = $replace[ 'booking_id' ]; $replace[ 'days_input_format' ] = $days_as_in_form; // 15.11.2023,16.11.2023,17.11.2023 $replace[ 'days_only_sql' ] = $sql_days_only; // 2023-11-15,2023-11-16,2023-11-17 $replace[ 'dates_sql' ] = $sql_dates_format; // 2016-07-28 16:00:01, 2016-07-28 18:00:02 $replace[ 'check_in_date_sql' ] = $sql_dates_format_check_in_out[0]; // 2016-07-28 16:00:01 $replace[ 'check_out_date_sql' ] = $sql_dates_format_check_in_out[ count( $sql_dates_format_check_in_out ) - 1 ]; // 2016-07-28 18:00:02 $replace[ 'dates' ] = $formated_booking_dates; // July 28, 2016 16:00 - July 28, 2016 18:00 $replace[ 'check_in_date' ] = $my_check_in_date; // July 28, 2016 16:00 $replace[ 'check_out_date' ] = $my_check_out_date; // July 28, 2016 18:00 $replace[ 'check_out_plus1day'] = $my_check_out_plus1day; // July 29, 2016 18:00 $replace[ 'dates_count' ] = count( $sql_days_only_array ); // 1 $replace[ 'days_count' ] = count( $sql_days_only_array ); // 1 $replace[ 'nights_count' ] = ( $replace[ 'days_count' ] > 1 ) ? ( $replace[ 'days_count' ] - 1 ) : $replace[ 'days_count' ]; // 1 //FixIn: 9.7.3.16 $replace[ 'cancel_date_hint' ] = $cancel_date_hint; // 11/11/2013 //FixIn: 10.0.0.31 $replace[ 'pre_checkin_date_hint' ] = $pre_checkin_date_hint; // 11/11/2013 $replace[ 'check_in_date_hint' ] = $check_in_date_hint; // 11/25/2013 $replace[ 'check_out_date_hint' ] = $check_out_date_hint; // 11/27/2013 $replace[ 'start_time_hint' ] = $start_time_hint; // 10:00 $replace[ 'end_time_hint' ] = $end_time_hint; // 12:00 //FixIn: 9.5.1.3 $replace['check_in_date_hint_sql'] = $check_in_date_sql; // 2023-03-04 $replace['check_out_date_hint_sql'] = $check_out_date_sql; // 2023-03-12 $replace['start_time_hint_sql'] = $start_time_sql; // 10:00 $replace['end_time_hint_sql'] = $end_time_sql; // 12:00 $replace['selected_dates_hint'] = wpbc_get_dates_comma_string_localized( $sql_days_only_with_full_times ); // 11/25/2013, 11/26/2013, 11/27/2013 $replace['selected_timedates_hint'] = wpbc_get_dates_comma_string_localized( $sql_dates_format ); // 11/25/2013 10:00, 11/26/2013, 11/27/2013 12:00 $replace['selected_short_dates_hint'] = wpbc_get_dates_short_format( $sql_days_only_with_full_times ); // 11/25/2013 - 11/27/2013 $replace['selected_short_timedates_hint'] = wpbc_get_dates_short_format( $sql_dates_format ); // 11/25/2013 10:00 - 11/27/2013 12:00 $replace[ 'days_number_hint' ] = $replace[ 'days_count' ]; // 3 $replace[ 'nights_number_hint' ] = $replace[ 'nights_count' ]; // 2 $replace[ 'siteurl' ] = htmlspecialchars_decode( '<a href="' . home_url() . '">' . home_url() . '</a>' ); $replace[ 'resource_title'] = wpbc_lang( $bk_title ); $replace[ 'bookingtype' ] = $replace[ 'resource_title']; $replace[ 'remote_ip' ] = wpbc_get_user_ip(); // The IP address from which the user is viewing the current page. $replace[ 'user_agent' ] = (isset($_SERVER['HTTP_USER_AGENT'])) ? $_SERVER['HTTP_USER_AGENT'] : ''; // Contents of the User-Agent: header from the current request, if there is one. $replace[ 'request_url' ] = (isset($_SERVER['HTTP_REFERER'])) ? $_SERVER['HTTP_REFERER'] : ''; // The address of the page (if any) where action was occured. Because we are sending it in Ajax request, we need to use the REFERER HTTP $replace[ 'current_date' ] = date_i18n( get_bk_option( 'booking_date_format' ) ); $replace[ 'current_time' ] = date_i18n( get_bk_option( 'booking_time_format' ) ); // Form Fields ///////////////////////////////////////////////////////////// $booking_form_show_array = wpbc__legacy__get_form_content_arr( $formdata, $booking_resource_id, '', $replace ); // We use here $replace array, becaise in "Content of booking filds data" form can be shortcodes from above definition foreach ( $booking_form_show_array['_all_fields_'] as $shortcode_name => $shortcode_value ) { if ( ! isset( $replace[ $shortcode_name ] ) ) $replace[ $shortcode_name ] = $shortcode_value; } $replace[ 'content' ] = $booking_form_show_array['content']; // Links /////////////////////////////////////////////////////////////////// $replace[ 'moderatelink' ] = htmlspecialchars_decode( // '<a href="' . esc_url( wpbc_get_bookings_url() . '&view_mode=vm_listing&tab=actions&wh_booking_id=' . $booking_id ) // . '">' . __('here', 'booking') . '</a>' ); $replace[ 'visitorbookingediturl' ] = apply_bk_filter( 'wpdev_booking_set_booking_edit_link_at_email', '[visitorbookingediturl]', $booking_id ); $replace[ 'visitorbookingslisting' ] = apply_bk_filter( 'wpdev_booking_set_booking_edit_link_at_email', '[visitorbookingslisting]', $booking_id ); //FixIn: 8.1.3.5.1 $replace[ 'visitorbookingcancelurl' ] = apply_bk_filter( 'wpdev_booking_set_booking_edit_link_at_email', '[visitorbookingcancelurl]', $booking_id ); $replace[ 'visitorbookingpayurl' ] = apply_bk_filter( 'wpdev_booking_set_booking_edit_link_at_email', '[visitorbookingpayurl]', $booking_id ); $replace[ 'bookinghash' ] = apply_bk_filter( 'wpdev_booking_set_booking_edit_link_at_email', '[bookinghash]', $booking_id ); // Cost //////////////////////////////////////////////////////////////////// $replace[ 'db_cost' ] = apply_bk_filter( 'get_booking_cost_from_db', '', $booking_id ); $replace[ 'db_cost_hint' ] = wpbc_get_cost_with_currency_for_user( $replace[ 'db_cost' ], $booking_resource_id ); //////////////////////////////////////////////////////////////////////////// //FixIn: 8.0.1.7 $modification_date = wpbc_db_get_booking_modification_date( $booking_id ); // This date $values in GMT date/Time format. So we need to switch to WordPress locale with TIME sum of actual GMT date/time value + shift of timezone from WordPress. $is_add_wp_timezone = true; $modification_date = wpbc_datetime_localized( trim( $modification_date ), 'Y-m-d H:i:s', $is_add_wp_timezone ); $replace['modification_date'] = ' ' . $modification_date; $modification_date = explode( ' ', $modification_date ); list( $replace['modification_year'], $replace['modification_month'], $replace['modification_day'] ) = explode( '-', $modification_date[0] ); list( $replace['modification_hour'], $replace['modification_minutes'], $replace['modification_seconds'] ) = explode( ':', $modification_date[1] ); //FixIn: 10.0.0.34 if ( ! empty( $replace['creation_date'] ) ) { // This date $values in GMT date/Time format. So we need to switch to WordPress locale with TIME sum of actual GMT date/time value + shift of timezone from WordPress. $creation_date = wpbc_datetime_localized( trim( $replace['creation_date'] ), 'Y-m-d H:i:s', $is_add_wp_timezone ); $replace['creation_date'] = ' ' . $creation_date; $creation_date = explode( ' ', $creation_date ); list( $replace['creation_year'], $replace['creation_month'], $replace['creation_day'] ) = explode( '-', $creation_date[0] ); list( $replace['creation_hour'], $replace['creation_minutes'], $replace['creation_seconds'] ) = explode( ':', $creation_date[1] ); } return $replace; } /** * Get additional parameters to the replace array for specific booking * * @param $replace * @param $booking_id * @param $bktype * @param $formdata * * @return mixed */ function wpbc_replace_params_for_booking_func( $replace, $booking_id, $bktype, $formdata ){ /* $modification_date = wpbc_db_get_booking_modification_date( $booking_id ); // This date $values in GMT date/Time format. So we need to switch to WordPress locale with TIME sum of actual GMT date/time value + shift of timezone from WordPress. $is_add_wp_timezone = true; $modification_date = wpbc_datetime_localized( trim( $modification_date ), 'Y-m-d H:i:s', $is_add_wp_timezone ); $replace['modification_date'] = ' ' . $modification_date; $modification_date = explode( ' ', $modification_date ); list( $replace['modification_year'], $replace['modification_month'], $replace['modification_day'] ) = explode( '-', $modification_date[0] ); list( $replace['modification_hour'], $replace['modification_minutes'], $replace['modification_seconds'] ) = explode( ':', $modification_date[1] ); */ //FixIn: 8.4.2.11 if ( isset( $replace['rangetime'] ) ) { $replace['rangetime'] = wpbc_time_slot_in_format( $replace['rangetime'] ); } if ( isset( $replace['starttime'] ) ) { $replace['starttime'] = wpbc_time_in_format( $replace['starttime'] ); } if ( isset( $replace['endtime'] ) ) { $replace['endtime'] = wpbc_time_in_format( $replace['endtime'] ); } //FixIn: 8.2.1.25 $booking_data = wpbc_db_get_booking_details( $booking_id ); if ( ! empty( $booking_data ) ) { foreach ( $booking_data as $booking_key => $booking_data ) { if ( ! isset( $replace[ $booking_key ] ) ) { $replace[ $booking_key ] = $booking_data; } } } // Set dates and times in correct format --------------------------------------------------------------- $is_add_wp_timezone = true; if ( ! empty( $replace['modification_date'] ) ) { // This date $values in GMT date/Time format. So we need to switch to WordPress locale with TIME sum of actual GMT date/time value + shift of timezone from WordPress. $modification_date = wpbc_datetime_localized( trim( $replace['modification_date'] ), 'Y-m-d H:i:s', $is_add_wp_timezone ); $replace['modification_date'] = ' ' . $modification_date; $modification_date = explode( ' ', $modification_date ); list( $replace['modification_year'], $replace['modification_month'], $replace['modification_day'] ) = explode( '-', $modification_date[0] ); list( $replace['modification_hour'], $replace['modification_minutes'], $replace['modification_seconds'] ) = explode( ':', $modification_date[1] ); } //FixIn: 10.0.0.34 if ( ! empty( $replace['creation_date'] ) ) { // This date $values in GMT date/Time format. So we need to switch to WordPress locale with TIME sum of actual GMT date/time value + shift of timezone from WordPress. $creation_date = wpbc_datetime_localized( trim( $replace['creation_date'] ), 'Y-m-d H:i:s', $is_add_wp_timezone ); $replace['creation_date'] = ' ' . $creation_date; $creation_date = explode( ' ', $creation_date ); list( $replace['creation_year'], $replace['creation_month'], $replace['creation_day'] ) = explode( '-', $creation_date[0] ); list( $replace['creation_hour'], $replace['creation_minutes'], $replace['creation_seconds'] ) = explode( ':', $creation_date[1] ); } return $replace; } add_filter( 'wpbc_replace_params_for_booking', 'wpbc_replace_params_for_booking_func', 10, 4 ); /** * Replace shortcodes in string * * @param string $subject - string to manipulate * @param array $replace_array - array with values to replace // array( [booking_id] => 9, [id] => 9, [dates] => July 3, 2016 14:00 - July 4, 2016 16:00, .... ) * @param mixed $replace_unknown_shortcodes - replace unknown params, if false, then no replace unknown params * @return string */ function wpbc_replace_booking_shortcodes( $subject, $replace_array , $replace_unknown_shortcodes = ' ' ) { $defaults = array( 'ip' => wpbc_get_user_ip() , 'blogname' => wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) , 'siteurl' => get_site_url() ); $replace = wp_parse_args( $replace_array, $defaults ); foreach ( $replace as $replace_shortcode => $replace_value ) { if ( is_null( $replace_value ) ) { $replace_value = ''; }; $subject = str_replace( array( '[' . $replace_shortcode . ']' , '{' . $replace_shortcode . '}' ) , $replace_value , $subject ); } // Remove all shortcodes, which is not replaced early. if ( $replace_unknown_shortcodes !== false ) $subject = preg_replace( '/[\s]{0,}[\[\{]{1}[a-zA-Z0-9.,-_]{0,}[\]\}]{1}[\s]{0,}/', $replace_unknown_shortcodes, $subject ); return $subject; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Get html preview of shortcode for Edit pages == " > /** * Get html preview of shortcode for Edit pages in Elementor and at Block editors * * @param $shortcode_type * @param $attr * * @return string */ function wpbc_get_preview_for_shortcode( $shortcode_type, $attr ) { //FixIn: 9.9.0.39 return '<div style="border:2px dashed #ccc;text-align: center; padding:10px;display:flex;flex-flow:column wrap;justify-content: center;align-content: center;"> <div>WP Booking Calendar Shortcode</div> <code>['.$shortcode_type.' ...]</code> <div style="font-size:0.8em;">This is not a real preview. Publish the page to see it in action.</div> </div>'; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Get version Nums or Types == " > /** * Check if this demo website * * @return bool */ function wpbc_is_this_demo() { // return !true; if ( ! class_exists( 'wpdev_bk_personal' ) ) { return false; // If this is Booking Calendar Free version, then it's not the demo. } //FixIn: 7.2.1.17 if ( ( ( isset( $_SERVER['SCRIPT_FILENAME'] ) ) && ( strpos( $_SERVER['SCRIPT_FILENAME'], 'wpbookingcalendar.com' ) !== false ) ) || ( ( isset( $_SERVER['HTTP_HOST'] ) ) && ( strpos( $_SERVER['HTTP_HOST'], 'wpbookingcalendar.com' ) !== false ) ) ) { return true; } else { return false; } } /** * Check if this Beta Demo website * * @return bool */ function wpbc_is_this_beta() { $is_beta = ( $_SERVER['HTTP_HOST'] === 'beta' ); return $is_beta; } /** Get Warning Text for Demo websites */ function wpbc_get_warning_text_in_demo_mode() { // return '<div class="wpbc-error-message wpbc_demo_test_version_warning"><strong>Warning!</strong> Demo test version does not allow changes to these items.</div>'; //Old Style return '<div class="wpbc-settings-notice notice-warning"><strong>Warning!</strong> Demo test version does not allow changes to these items.</div>'; } function wpbc_get_version_type__and_mu(){ $version = 'free'; if ( class_exists( 'wpdev_bk_personal' ) ) $version = 'personal'; if ( class_exists( 'wpdev_bk_biz_s' ) ) $version = 'biz_s'; if ( class_exists( 'wpdev_bk_biz_m' ) ) $version = 'biz_m'; if ( class_exists( 'wpdev_bk_biz_l' ) ) $version = 'biz_l'; if ( class_exists('wpdev_bk_multiuser') ) $version = 'multiuser'; return $version; } function wpbc_get_plugin_version_type(){ $version = 'free'; if ( class_exists( 'wpdev_bk_personal' ) ) $version = 'personal'; if ( class_exists( 'wpdev_bk_biz_s' ) ) $version = 'biz_s'; if ( class_exists( 'wpdev_bk_biz_m' ) ) $version = 'biz_m'; if ( class_exists( 'wpdev_bk_biz_l' ) ) $version = 'biz_l'; return $version; } /** * Check if user accidentially update Booking Calendar Paid version to Free * * @return bool */ function wpbc_is_updated_paid_to_free() { if ( ( wpbc_is_table_exists('bookingtypes') ) && ( ! class_exists('wpdev_bk_personal') ) ) return true; else return false; } function wpbc_get_ver_sufix() { if( strpos( strtolower(WPDEV_BK_VERSION) , 'multisite') !== false ) { $v_type = '-multi'; } else if( strpos( strtolower(WPDEV_BK_VERSION) , 'develop') !== false ) { $v_type = '-dev'; } else { $v_type = ''; } $v = ''; if (class_exists('wpdev_bk_personal')) $v = 'ps'. $v_type; if (class_exists('wpdev_bk_biz_s')) $v = 'bs'. $v_type; if (class_exists('wpdev_bk_biz_m')) $v = 'bm'. $v_type; if (class_exists('wpdev_bk_biz_l')) $v = 'bl'. $v_type; if (class_exists('wpdev_bk_multiuser')) $v = ''; return $v ; } function wpbc_up_link() { if ( ! wpbc_is_this_demo() ) $v = wpbc_get_ver_sufix(); else $v = ''; return 'https://wpbookingcalendar.com/' . ( ( empty($v) ) ? '' : 'upgrade-' . $v . '/' ) ; } /** * Check if "Booking Manager" installed/activated and return version number * * @return string - 0 if not installed, otherwise version num */ function wpbc_get_wpbm_version() { if ( ! defined( 'WPBM_VERSION_NUM' ) ) { return 0; } else { return WPBM_VERSION_NUM; } } /** * Get header info from this file, just for compatibility with WordPress 2.8 and older versions * * @param $file = WPBC_FILE * @param $default_headers = array( 'Name' => 'Plugin Name', 'PluginURI' => 'Plugin URI', 'Version' => 'Version', 'Description' => 'Description', 'Author' => 'Author', 'AuthorURI' => 'Author URI', 'TextDomain' => 'Text Domain', 'DomainPath' => 'Domain Path' ) * @param $context = 'plugin' * * @return array * * Example: * $plugin_data = wpbc_file__read_header_info( WPBC_FILE , array( 'Name' => 'Plugin Name', 'PluginURI' => 'Plugin URI', 'Version' => 'Version', 'Description' => 'Description', 'Author' => 'Author', 'AuthorURI' => 'Author URI', 'TextDomain' => 'Text Domain', 'DomainPath' => 'Domain Path' ) , 'plugin' ); */ function wpbc_file__read_header_info( $file, $default_headers, $context = '' ) { $fp = fopen( $file, 'r' ); // We don't need to write to the file, so just open for reading. $file_data = fread( $fp, 8192 ); // Pull only the first 8kiB of the file in. fclose( $fp ); // PHP will close file handle, but we are good citizens. if ( $context != '' ) { $extra_headers = array(); //apply_filters( "extra_$context".'_headers', array() ); $extra_headers = array_flip( $extra_headers ); foreach ( $extra_headers as $key => $value ) { $extra_headers[ $key ] = $key; } $all_headers = array_merge( $extra_headers, $default_headers ); } else { $all_headers = $default_headers; } foreach ( $all_headers as $field => $regex ) { preg_match( '/' . preg_quote( $regex, '/' ) . ':(.*)$/mi', $file_data, ${$field}); if ( !empty( ${$field} ) ) ${$field} = trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', ${$field}[1] )); else ${$field} = ''; } $file_data = compact( array_keys( $all_headers ) ); return $file_data; } /** * Check if we need BLUR this section -- add CSS Class for specific versions * * Example: $is_blured = wpbc_is_blured( array( 'free', 'ps' ) ); // true in Free and Personal versions. * * @param $versions_arr * * @return void */ function wpbc_is_blured( $versions_arr ){ $ver = wpbc_get_version_type__and_mu(); $is_blured = false; switch ( $ver ) { case 'free': $is_blured = ( ( in_array( $ver, $versions_arr ) ) || ( in_array( 'f', $versions_arr ) ) ) ? true : $is_blured; break; case 'personal': $is_blured = ( ( in_array( $ver, $versions_arr ) ) || ( in_array( 'ps', $versions_arr ) ) ) ? true : $is_blured; break; case 'biz_s': $is_blured = ( ( in_array( $ver, $versions_arr ) ) || ( in_array( 'bs', $versions_arr ) ) ) ? true : $is_blured; break; case 'biz_m': $is_blured = ( ( in_array( $ver, $versions_arr ) ) || ( in_array( 'bm', $versions_arr ) ) ) ? true : $is_blured; break; case 'biz_l': $is_blured = ( ( in_array( $ver, $versions_arr ) ) || ( in_array( 'bl', $versions_arr ) ) ) ? true : $is_blured; break; case 'multiuser': $is_blured = ( ( in_array( $ver, $versions_arr ) ) || ( in_array( 'mu', $versions_arr ) ) ) ? true : $is_blured; break; default: // Default } return $is_blured; } /** * Echo Blur CSS Class for specific versions * * Example: wpbc_echo_blur(array('free','ps')); // Echo blur in Free and Personal versions. * * @param $versions_arr * * @return void */ function wpbc_echo_blur( $versions_arr ){ $is_blured = wpbc_is_blured( $versions_arr ); if ( $is_blured ) { echo 'wpbc_blur'; } } /** * Show Upgrade Widget * * @param $id * @param $params * * @return string if upgrade panel hided than returned '' * * * Example: * wpbc_get_up_notice('booking_weekdays_conditions', array( * 'feature_link' => array( 'title' => 'feature', 'relative_url' => 'overview/#capacity' ), * 'upgrade_link' => array( 'title' => 'Upgrade to Pro', 'relative_url' => 'features/#bk_news_section' ), * 'versions' => 'Business Large, MultiUser versions', * 'css' => 'transform: translate(0) translateY(120px);' * )); */ function wpbc_get__upgrade_notice__html_content( $id, $params ){ $defaults = array( 'feature_link' => array( 'title' => 'feature', 'relative_url' => 'overview/#capacity' ), 'upgrade_link' => array( 'title' => 'Upgrade to Pro', 'relative_url' => 'features/#bk_news_section' ), 'versions' => 'Business Large, MultiUser versions', 'css' => 'transform: translate(0) translateY(120px);', 'dismiss_css_class' => '', 'html_dismiss_btn' => '' ); $params = wp_parse_args( $params, $defaults ); ob_start(); ?><div id="upgrade_notice_<?php echo esc_attr( $id ); ?>" class="wpbc_widget_content wpbc_upgrade_widget <?php echo esc_attr( str_replace( array('.','#'), '', $params['dismiss_css_class'] ) ); ?>" style="<?php echo esc_attr( $params['css'] ); ?>"> <div class="ui_container ui_container_toolbar ui_container_small wpbc_upgrade_widget_container"> <div class="ui_group ui_group__upgrade"> <div class="wpbc_upgrade_note wpbc_upgrade_theme_green"> <div> <?php printf( 'This %s is available in the %s. %s' , '<a target="_blank" href="https://wpbookingcalendar.com/' . $params['feature_link']['relative_url'] . '">' . $params['feature_link']['title'] . '</a>' , '<strong>' . $params['versions'] . '</strong>' , '<a target="_blank" href="https://wpbookingcalendar.com/' . $params['upgrade_link']['relative_url'] . '">' . $params['upgrade_link']['title'] . '</a>' ); ?> </div> <?php // Dismiss button echo $params['html_dismiss_btn']; ?> </div> </div> </div> </div><?php $html = ob_get_clean(); return $html ; } /** * Get for showing Upgrade Widget Content and CSS class if needed to blur real content. If * * @param $params = array( * 'id' => $id . '_' . 'weekdays_conditions', * 'dismiss_css_class' => '.wpbc_dismiss_weekdays_conditions', * 'blured_in_versions' => array( 'free', 'ps', 'bs', 'mu' ), * 'feature_link' => array( 'title' => 'feature', 'relative_url' => 'overview/#advanced-days-selection' ), * 'upgrade_link' => array( 'title' => 'Upgrade to Pro', 'relative_url' => 'features/#bk_news_section' ), * 'versions' => 'Business Medium / Large, MultiUser versions', * 'css' => 'transform: translate(0) translateY(120px);' * ) * * @return array( * 'content' => $upgrade_panel_html, * 'maybe_blur_css_class' => $blur_css_class * ) * * Example of usage: * * $upgrade_content_arr = wpbc_get_upgrade_widget( array( * 'id' => $id . '_' . 'weekdays_conditions', * 'dismiss_css_class' => '.wpbc_dismiss_weekdays_conditions', * 'blured_in_versions' => array( 'free', 'ps', 'bs', 'mu' ), * 'feature_link' => array( 'title' => 'feature', 'relative_url' => 'overview/#advanced-days-selection' ), * 'upgrade_link' => array( 'title' => 'Upgrade to Pro', 'relative_url' => 'features/#bk_news_section' ), * 'versions' => 'Business Medium / Large, MultiUser versions', * 'css' => 'transform: translate(0) translateY(120px);' * ) ); * * echo $upgrade_content_arr['content']; * * // ... In real content ... * <div class=" wpbc_dismiss_weekdays_conditions <?php echo esc_attr( $upgrade_content_arr['maybe_blur_css_class'] ); ?>"> * ... * </div> */ function wpbc_get_upgrade_widget( $params ) { $defaults = array( 'id' => 'wpbc_random_' . round( microtime( true ) * 1000 ), //$id . '_' . 'weekdays_conditions', 'blured_in_versions' => array( 'free', 'ps', 'bs', 'bm', 'bl', 'mu' ), 'feature_link' => array( 'title' => 'feature', 'relative_url' => 'overview/#capacity' ), 'upgrade_link' => array( 'title' => 'Upgrade to Pro', 'relative_url' => 'features/#bk_news_section' ), 'versions' => 'Business Large, MultiUser versions', 'css' => 'transform: translate(0) translateY(120px);', 'dismiss_css_class' => '' //'.wpbc_random_' . round( microtime( true ) * 1000 ), //'.'.$id . '_' . 'weekdays_conditions' ); $params = wp_parse_args( $params, $defaults ); $up_id = $params['id']; $is_blured = wpbc_is_blured( $params['blured_in_versions'] ); $upgrade_panel_html = ''; $blur_css_class = ''; if ( $is_blured ) { // --------------------------------------------------------------------------------------------------------- // Is dismissed ? // --------------------------------------------------------------------------------------------------------- ob_start(); $is_upgrade_panel_visible = wpbc_is_dismissed( $up_id , array( 'title' => '<span aria-hidden="true" style="font-size: 28px;">×</span>', 'hint' => __( 'Dismiss', 'booking' ), 'class' => 'wpbc_panel_get_started_dismiss', 'css' => '', 'dismiss_css_class' => $params['dismiss_css_class'] ) ); $html_dismiss_btn = ob_get_clean(); // --------------------------------------------------------------------------------------------------------- // Upgrade Widget // --------------------------------------------------------------------------------------------------------- if ( $is_upgrade_panel_visible ) { $upgrade_panel_html = wpbc_get__upgrade_notice__html_content( $up_id, array( 'feature_link' => $params['feature_link'], 'upgrade_link' => $params['upgrade_link'], 'versions' => $params['versions'], 'css' => $params['css'], 'dismiss_css_class' => $params['dismiss_css_class'], 'html_dismiss_btn'=> $html_dismiss_btn ) ); $blur_css_class = 'wpbc_blur'; } else { ob_start(); ?><script type="text/javascript"> jQuery(document).ready(function(){ setTimeout(function(){ jQuery( '<?php echo esc_attr( $params['dismiss_css_class'] ); ?>' ).hide() ; }, 100); }); </script><?php $upgrade_panel_html = ob_get_clean(); } } $upgrade_content_arr = array( 'content' => $upgrade_panel_html, 'maybe_blur_css_class' => $blur_css_class ); return $upgrade_content_arr; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Calendar functions == " > /** * Get maximum visible days in calendar * * @return int */ function wpbc_get_max_visible_days_in_calendar(){ // Number of months to scroll $max_visible_days_in_calendar = get_bk_option( 'booking_max_monthes_in_calendar'); if ( false !== strpos( $max_visible_days_in_calendar, 'm' ) ) { //$max_visible_days_in_calendar = intval( str_replace( 'm', '', $max_visible_days_in_calendar ) ) * 31 ; //FixIn: 9.6.1.1 //FixIn: 10.0.0.26 $diff_days = strtotime( '+' . intval( str_replace( 'm', '', $max_visible_days_in_calendar ) ) . ' months', strtotime( 'now' ) ) - strtotime( 'now' ); $max_visible_days_in_calendar = round( $diff_days / ( 60 * 60 * 24 ) ) + 1; } else { $max_visible_days_in_calendar = intval( str_replace( 'y', '', $max_visible_days_in_calendar ) ) * 365 + 15; //FixIn: 9.6.1.1 } return $max_visible_days_in_calendar; } /** * Parse {calendar ....} option parameter in Calendar | Booking form shortcode * * @param $bk_options * * @return array|false */ function wpbc_parse_calendar_options( $bk_options ) { if ( empty( $bk_options ) ) { return false; } /* $matches structure: * Array ( [0] => Array ( [0] => {calendar months="6" months_num_in_row="2" width="341px" cell_height="48px"}, [1] => calendar [2] => months="6" months_num_in_row="2" width="341px" cell_height="48px" ) [1] => Array ( [0] => {select-day condition="weekday" for="5" value="3"}, [1] => select-day [2] => condition="weekday" for="5" value="3" ) ..... ) */ $pattern_to_search='%\s*{([^\s]+)\s*([^}]+)\s*}\s*[,]?\s*%'; preg_match_all( $pattern_to_search, $bk_options, $matches, PREG_SET_ORDER ); foreach ( $matches as $value ) { if ( $value[1] == 'calendar' ) { $paramas = $value[2]; $paramas = trim( $paramas ); $paramas = explode( ' ', $paramas ); $options = array(); foreach ( $paramas as $vv ) { if ( ! empty( $vv ) ) { $vv = trim( $vv ); $vv = explode( '=', $vv ); if ( ( isset( $vv[0] ) ) && ( isset( $vv[1] ) ) ) { $options[ $vv[0] ] = trim( $vv[1] ); } } } if ( count( $options ) == 0 ) { return false; } else { return $options; } } } return false; // We are not have the "calendar" options in the shortcode } /** * Parse {calendar ....} option parameter in Calendar | Booking form shortcode * * @param $bk_options * * @return array|false */ function wpbc_parse_calendar_options__aggregate_param( $bk_options ) { //FixIn: 9.8.15.10 if ( empty( $bk_options ) ) { return false; } /* $matches structure: $matches = [ 0 = [ 0 = "{aggregate type=bookings_only}" 1 = "aggregate" 2 = "type=bookings_only" ] ] */ $pattern_to_search='%\s*{([^\s]+)\s*([^}]+)\s*}\s*[,]?\s*%'; preg_match_all( $pattern_to_search, $bk_options, $matches, PREG_SET_ORDER ); foreach ( $matches as $value ) { if ( $value[1] == 'aggregate' ) { $paramas = $value[2]; $paramas = trim( $paramas ); $paramas = explode( ' ', $paramas ); $options = array(); foreach ( $paramas as $vv ) { if ( ! empty( $vv ) ) { $vv = trim( $vv ); $vv = explode( '=', $vv ); if ( ( isset( $vv[0] ) ) && ( isset( $vv[1] ) ) ) { $options[ $vv[0] ] = trim( $vv[1] ); } } } if ( count( $options ) == 0 ) { return false; } else { return $options; } } } return false; // We are not have the "calendar" options in the shortcode } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Files functions == " > // Get array of images - icons inside of this directory function wpbc_dir_list ($directories) { // create an array to hold directory list $results = array(); if (is_string($directories)) $directories = array($directories); foreach ($directories as $dir) { if ( is_dir( $dir ) ) { $directory = $dir; } else { $directory = WPBC_PLUGIN_DIR . $dir; } if ( file_exists( $directory ) ) { //FixIn: 5.4.5 // create a handler for the directory $handler = @opendir($directory); if ($handler !== false) { // keep going until all files in directory have been read while ($file = readdir($handler)) { // if $file isn't this directory or its parent, // add it to the results array if ($file != '.' && $file != '..' && ( strpos($file, '.css' ) !== false ) ) $results[] = array($file, /* WPBC_PLUGIN_URL .*/ $dir . $file, ucfirst(strtolower( str_replace('.css', '', $file))) ); } // tidy up: close the handler closedir($handler); } } } // done! return $results; } /** * Check if such file exist or not. * * @param string $path - relative path to file (relative to plugin folder). * @return boolean true | false */ function wpbc_is_file_exist( $path ) { if ( file_exists( trailingslashit( WPBC_PLUGIN_DIR ) . ltrim( $path, '/\\' ) ) ) // check if this file exist return true; else return false; } /** * Count the number of bytes of a given string. * Input string is expected to be ASCII or UTF-8 encoded. * Warning: the function doesn't return the number of chars * in the string, but the number of bytes. * See http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 * for information on UTF-8. * * @param string $str The string to compute number of bytes * * @return The length in bytes of the given string. */ function wpbc_get_bytes_from_str( $str ) { // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT // Number of characters in string $strlen_var = strlen( $str ); $d = 0; // string bytes counter // Iterate over every character in the string, escaping with a slash or encoding to UTF-8 where necessary for ( $c = 0; $c < $strlen_var; ++ $c ) { $ord_var_c = ord( $str[$c] ); //FixIn: 2.0.17.1 switch ( true ) { case(($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): // characters U-00000000 - U-0000007F (same as ASCII) $d ++; break; case(($ord_var_c & 0xE0) == 0xC0): // characters U-00000080 - U-000007FF, mask 110XXXXX $d += 2; break; case(($ord_var_c & 0xF0) == 0xE0): // characters U-00000800 - U-0000FFFF, mask 1110XXXX $d += 3; break; case(($ord_var_c & 0xF8) == 0xF0): // characters U-00010000 - U-001FFFFF, mask 11110XXX $d += 4; break; case(($ord_var_c & 0xFC) == 0xF8): // characters U-00200000 - U-03FFFFFF, mask 111110XX $d += 5; break; case(($ord_var_c & 0xFE) == 0xFC): // characters U-04000000 - U-7FFFFFFF, mask 1111110X $d += 6; break; default: $d ++; } } return $d; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == URL functions == " > /** * Get absolute URL to relative plugin path. * Possibly to load minified version of file, if its exist * @param string $path - path * @return string */ function wpbc_plugin_url( $path ) { return trailingslashit( WPBC_PLUGIN_URL ) . ltrim( $path, '/\\' ); } // Set URL from absolute to relative (starting from /) function wpbc_set_relative_url( $url ){ $url = esc_url_raw($url); $url_path = parse_url($url, PHP_URL_PATH); $url_path = ( empty($url_path) ? $url : $url_path ); $url = trim($url_path, '/'); return '/' . $url; } /** * Get Relative URL * * @param $maybe_absolute_link * * @return string */ function wpbc_make_link_relative( $maybe_absolute_link ) { if ( $maybe_absolute_link == get_option( 'siteurl' ) ) { $maybe_absolute_link = '/'; } $maybe_absolute_link = '/' . trim( wp_make_link_relative( $maybe_absolute_link ), '/' ); return $maybe_absolute_link; } /** * Get Absolute URL (check for languages) * * @param $maybe_relative_link * * @return string */ function wpbc_make_link_absolute( $maybe_relative_link ){ if ( ( $maybe_relative_link != home_url() ) && ( strpos( $maybe_relative_link, 'http' ) !== 0 ) ) { $maybe_relative_link = wpbc_lang( $maybe_relative_link ); //FixIn: 8.4.5.1 $maybe_relative_link = home_url() . '/' . trim( wp_make_link_relative( $maybe_relative_link ), '/' ); //FixIn: 7.0.1.20 } return esc_js( $maybe_relative_link ); } /** * Redirect browser to a specific page * * @param string $url - URL of page to redirect */ function wpbc_redirect( $url ) { $url = wpbc_make_link_absolute( $url ); $url = html_entity_decode( esc_url( $url ) ); echo '<script type="text/javascript">'; echo 'window.location.href="'.$url.'";'; echo '</script>'; echo '<noscript>'; echo '<meta http-equiv="refresh" content="0;url='.$url.'" />'; echo '</noscript>'; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Get Admin Menu URLs == " > /** * Check if we edit or create a new page in WordPress ? * @return bool */ function wpbc_is_on_edit_page() { //FixIn: 9.9.0.39 if ( ( ! empty( $GLOBALS['pagenow'] ) ) && ( is_admin() ) ) { if ( ( 'post.php' === $GLOBALS['pagenow'] ) // Edit - Post / Page || ( 'post-new.php' === $GLOBALS['pagenow'] ) // Add New - Post / Page || ( ( 'admin-ajax.php' === $GLOBALS['pagenow'] ) && ( ! empty( $_REQUEST['action'] ) ) && ( 'elementor_ajax' === $_REQUEST['action'] ) ) // Elementor Edit page - Ajax ) { return true; } } return false; } /** * Get URL to specific Admin Menu page * * @param string $menu_type - { booking | add | resources | settings } * @param boolean $is_absolute_url - Absolute or relative url { default: true } * @param boolean $is_old - { default: true } * @return string - URL to menu */ function wpbc_get_menu_url( $menu_type, $is_absolute_url = true, $is_old = true) { $is_old = false; switch ( $menu_type) { case 'booking': // Bookings case 'bookings': case 'booking-listing': case 'bookings-listing': case 'listing': case 'overview': case 'calendar-overview': case 'timeline': $link = 'wpbc'; break; case 'add': // Add New Booking case 'add-bookings': case 'add-booking': case 'new': case 'new-bookings': case 'new-booking': $link = 'wpbc-new'; break; case 'availability': $link = 'wpbc-availability'; break; case 'price': $link = 'wpbc-prices'; break; case 'resources': // Resources case 'booking-resources': $link = 'wpbc-resources'; break; case 'settings': // Settings case 'options': $link = 'wpbc-settings'; break; default: // Bookings $link = 'wpbc'; break; } if ( $is_absolute_url ) { $link = admin_url( 'admin.php' ) . '?page=' . $link ; } return $link; } // ----------------------------------------------------------------------------------------------------------------- /** * Get URL of Booking Listing or Calendar Overview page * * @param boolean $is_absolute_url - Absolute or relative url { default: true } * @param boolean $is_old - { default: true } * @return string - URL to menu */ function wpbc_get_bookings_url( $is_absolute_url = true, $is_old = true ) { return wpbc_get_menu_url( 'booking', $is_absolute_url, $is_old ); } /** * Get URL of Booking > Availability page * * @param boolean $is_absolute_url - Absolute or relative url { default: true } * @param boolean $is_old - { default: true } * @return string - URL to menu */ function wpbc_get_availability_url( $is_absolute_url = true, $is_old = true ) { return wpbc_get_menu_url( 'availability', $is_absolute_url, $is_old ); } /** * Get URL of Booking > Availability page * * @param boolean $is_absolute_url - Absolute or relative url { default: true } * @param boolean $is_old - { default: true } * @return string - URL to menu */ function wpbc_get_price_url( $is_absolute_url = true, $is_old = true ) { return wpbc_get_menu_url( 'price', $is_absolute_url, $is_old ); } /** * Get URL of Booking > Add booking page * * @param boolean $is_absolute_url - Absolute or relative url { default: true } * @param boolean $is_old - { default: true } * @return string - URL to menu */ function wpbc_get_new_booking_url( $is_absolute_url = true, $is_old = true ) { return wpbc_get_menu_url( 'add', $is_absolute_url, $is_old ); } /** * Get URL of Booking > Resources page * * @param boolean $is_absolute_url - Absolute or relative url { default: true } * @param boolean $is_old - { default: true } * @return string - URL to menu */ function wpbc_get_resources_url( $is_absolute_url = true, $is_old = true ) { return wpbc_get_menu_url( 'resources', $is_absolute_url, $is_old ); } /** * Get URL of Booking > Settings page * * @param boolean $is_absolute_url - Absolute or relative url { default: true } * @param boolean $is_old - { default: true } * @return string - URL to menu */ function wpbc_get_settings_url( $is_absolute_url = true, $is_old = true ) { return wpbc_get_menu_url( 'settings', $is_absolute_url, $is_old ); } // ----------------------------------------------------------------------------------------------------------------- /** * Check if this Booking Listing or Calendar Overview page * @param string $server_param - 'REQUEST_URI' | 'HTTP_REFERER' Default: 'REQUEST_URI' * @return boolean true | false */ function wpbc_is_bookings_page( $server_param = 'REQUEST_URI' ) { // Old if ( ( is_admin() ) && ( strpos($_SERVER[ $server_param ],'wpdev-booking.phpwpdev-booking') !== false ) && ( strpos($_SERVER[ $server_param ],'wpdev-booking.phpwpdev-booking-reservation') === false ) ) { return true; } // New if ( ( is_admin() ) && ( strpos($_SERVER[ $server_param ],'page=wpbc') !== false ) && ( strpos($_SERVER[ $server_param ],'page=wpbc-') === false ) ) { return true; } return false; } /** * Check if this Booking > Add booking page * @param string $server_param - 'REQUEST_URI' | 'HTTP_REFERER' Default: 'REQUEST_URI' * @return boolean true | false */ function wpbc_is_new_booking_page( $server_param = 'REQUEST_URI' ) { // Old if ( ( is_admin() ) && ( strpos($_SERVER[ $server_param ],'wpdev-booking.phpwpdev-booking-reservation') !== false ) ) { return true; } // New if ( ( is_admin() ) && ( strpos($_SERVER[ $server_param ],'page=wpbc-new') !== false ) ) { return true; } return false; } /** * Check if this WP Booking Calendar > Settings > Booking Form page * @param string $server_param - 'REQUEST_URI' | 'HTTP_REFERER' Default: 'REQUEST_URI' * @return boolean true | false */ function wpbc_is_settings_form_page( $server_param = 'REQUEST_URI' ) { if ( ( is_admin() ) && ( strpos($_SERVER[ $server_param ],'page=wpbc-settings') !== false ) && ( ( strpos($_SERVER[ $server_param ],'&tab=form') !== false ) || ( ( class_exists( 'wpdev_bk_multiuser' ) ) && ( ! empty( $_REQUEST['tab'] ) ) && ( 'form' === $_REQUEST['tab'] ) ) // Regular user overwrite settings ) ) { return true; } return false; } /** * Check if this Booking > Availability page * @param string $server_param - 'REQUEST_URI' | 'HTTP_REFERER' Default: 'REQUEST_URI' * @return boolean true | false */ function wpbc_is_availability_page( $server_param = 'REQUEST_URI' ) { // New if ( ( is_admin() ) && ( strpos($_SERVER[ $server_param ],'page=wpbc-availability') !== false ) ) { return true; } return false; } /** * Check if this Booking > Customize page * @param string $server_param - 'REQUEST_URI' | 'HTTP_REFERER' Default: 'REQUEST_URI' * @return boolean true | false */ function wpbc_is_customize_plugin_page( $server_param = 'REQUEST_URI' ) { //FixIn: 9.8.0.1 // New if ( ( is_admin() ) && ( strpos($_SERVER[ $server_param ],'page=wpbc-customize_plugin') !== false ) ) { return true; } return false; } // ----------------------------------------------------------------------------------------------------------------- /** * Check if this Booking > Resources page * @param string $server_param - 'REQUEST_URI' | 'HTTP_REFERER' Default: 'REQUEST_URI' * @return boolean true | false */ function wpbc_is_resources_page( $server_param = 'REQUEST_URI' ) { // Old if ( ( is_admin() ) && ( strpos($_SERVER[ $server_param ],'wpdev-booking.phpwpdev-booking-resources') !== false ) ) { return true; } // New if ( ( is_admin() ) && ( strpos($_SERVER[ $server_param ],'page=wpbc-resources') !== false ) ) { return true; } return false; } /** * Check if this Booking > Settings page * @param string $server_param - 'REQUEST_URI' | 'HTTP_REFERER' Default: 'REQUEST_URI' * @return boolean true | false */ function wpbc_is_settings_page( $server_param = 'REQUEST_URI' ) { // Old if ( ( is_admin() ) && ( strpos($_SERVER[ $server_param ],'wpdev-booking.phpwpdev-booking-option') !== false ) ) { return true; } // New if ( ( is_admin() ) && ( strpos($_SERVER[ $server_param ],'page=wpbc-settings') !== false ) ) { return true; } return false; } // ----------------------------------------------------------------------------------------------------------------- /** * Transform the REQESTS parameters (GET and POST) into URL * * @param $page_param * @param array $exclude_params * @param $only_these_parameters * @return */ function wpbc_get_params_in_url( $page_param , $exclude_params = array(), $only_these_parameters = false, $is_escape_url = true, $only_get = false ){ //FixIn: 8.0.1.101 //Fix: $is_escape_url = false $exclude_params[] = 'page'; $exclude_params[] = 'post_type'; if ( isset( $_GET['page'] ) ) $page_param = $_GET['page']; $get_paramaters = array( 'page' => $page_param ); if ( $only_get ) $check_params = $_GET; else $check_params = $_REQUEST; //debuge($check_params); foreach ( $check_params as $prm_key => $prm_value ) { // Skip parameters arrays, like $_GET['rvaluation_to'] = Array ( [0] => 6, [1] => 14, [2] => 14 ) if ( ( is_string( $prm_value ) ) || ( is_numeric( $prm_value ) ) ) { if ( strlen( $prm_value ) > 1000 ) { // Check about TOOO long parameters, if it exist then reset it. $prm_value = ''; } if ( ! in_array( $prm_key, $exclude_params ) ) if ( ( $only_these_parameters === false ) || ( in_array( $prm_key, $only_these_parameters ) ) ) $get_paramaters[ $prm_key ] = $prm_value; } } //debuge($exclude_params); $url = admin_url( add_query_arg( $get_paramaters , 'admin.php' ) ); if ( $is_escape_url ) $url = esc_url_raw( $url ); //FixIn: 8.1.1.7 // $url = esc_url( $url ); return $url; /* // Old variant: if ( isset( $_GET['page'] ) ) $page_param = $_GET['page']; $url_start = 'admin.php?page=' . $page_param . '&'; $exclude_params[] = 'page'; foreach ( $_REQUEST as $prm_key => $prm_value ) { if ( !in_array( $prm_key, $exclude_params ) ) if ( ($only_these_parameters === false) || ( in_array( $prm_key, $only_these_parameters ) ) ) $url_start .= $prm_key . '=' . $prm_value . '&'; } $url_start = substr( $url_start, 0, -1 ); return $url_start; */ } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Can I Load JavaScript Files == " > function wpbc_can_i_load_on__edit_new_post_page() { if ( ! current_user_can( 'edit_posts' ) && ! current_user_can( 'edit_pages' ) ) { return false; } $pages_where_load = array( 'post-new.php', 'page-new.php', 'post.php', 'page.php', 'widgets.php', 'customize.php' ); if ( in_array( basename( $_SERVER['PHP_SELF'] ), $pages_where_load ) ) { return true; } return false; } function wpbc_can_i_load_on__searchable_resources_page() { if ( ( isset( $_REQUEST['page'] ) ) && ( $_REQUEST['page'] == 'wpbc-resources' ) && ( isset( $_REQUEST['tab'] ) ) && ( $_REQUEST['tab'] == 'searchable_resources' ) ) { return true; } return false; } function wpbc_can_i_load_on__resources_page() { if ( ( isset( $_REQUEST['page'] ) ) && ( $_REQUEST['page'] == 'wpbc-resources' ) ) { return true; } return false; } /** * Can I load scripts on this page for 'shortcode_config' * * @return bool */ function wpbc_can_i_load_on_this_page__shortcode_config() { if ( wpbc_can_i_load_on__edit_new_post_page() || ( wpbc_can_i_load_on__resources_page() && ( ! wpbc_can_i_load_on__searchable_resources_page() ) ) ){ return true; } else { return false; } } /** * Can I load scripts on this page? Loaded only at ../admin.php?page=wpbc-resources&tab=searchable_resources * * @return bool */ function wpbc_can_i_load_on_this_page__modal_search_options() { if ( wpbc_can_i_load_on__searchable_resources_page() ){ return true; } else { return false; } } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Admin Top Bar == " > function wpbc_add__booking_menu__in__admin_top_bar(){ global $wp_admin_bar; $current_user = wpbc_get_current_user(); $curr_user_role = get_bk_option( 'booking_user_role_booking' ); $level = 10; if ($curr_user_role == 'administrator') $level = 10; else if ($curr_user_role == 'editor') $level = 7; else if ($curr_user_role == 'author') $level = 2; else if ($curr_user_role == 'contributor') $level = 1; else if ($curr_user_role == 'subscriber') $level = 0; $is_super_admin = apply_bk_filter('multiuser_is_user_can_be_here', false, 'only_super_admin'); if ( ( ($current_user->user_level < $level) && (! $is_super_admin) ) || !is_admin_bar_showing() ) return; $update_count = wpbc_db_get_number_new_bookings(); $title = 'Booking Calendar';//__('Booking Calendar' ,'booking'); //FixIn: 9.1.3.3 $update_title = ''// '<img src="'.WPBC_PLUGIN_URL .'/assets/img/icon-16x16.png" style="height: 16px;vertical-align: sub;" /> ' . $title; $is_user_activated = apply_bk_filter('multiuser_is_current_user_active', true ); //FixIn: 6.0.1.17 if ( ( $update_count > 0) && ( $is_user_activated ) ) { $update_count_title = " <span class='booking-count bk-update-count' style='background: #f0f0f1;color: #2c3338;display: inline;padding: 2px 5px;font-weight: 600;border-radius: 10px;'>" . number_format_i18n($update_count) . "</span>" ; //id='booking-count' $update_title .= $update_count_title; } $link_bookings = wpbc_get_bookings_url(); $link_res = wpbc_get_resources_url(); $link_settings = wpbc_get_settings_url(); //FixIn: 9.8.15.9 $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc', 'title' => $update_title , 'href' => wpbc_get_bookings_url() ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_bookings', 'title' => __( 'Bookings', 'booking' ), 'href' => wpbc_get_bookings_url(), 'parent' => 'bar_wpbc', ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_booking_listing', 'title' => __( 'Booking Listing', 'booking' ), 'href' => wpbc_get_bookings_url() . '&view_mode=vm_listing', 'parent' => 'bar_wpbc_bookings', ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_calendar_overview', 'title' => __( 'Calendar Overview', 'booking' ), 'href' => wpbc_get_bookings_url() . '&view_mode=vm_calendar', 'parent' => 'bar_wpbc_bookings', ) ); $curr_user_role_settings = get_bk_option( 'booking_user_role_settings' ); $level = 10; if ($curr_user_role_settings == 'administrator') $level = 10; else if ($curr_user_role_settings == 'editor') $level = 7; else if ($curr_user_role_settings == 'author') $level = 2; else if ($curr_user_role_settings == 'contributor') $level = 1; else if ($curr_user_role_settings == 'subscriber') $level = 0; if ( ( ($current_user->user_level < $level) && (! $is_super_admin) ) || !is_admin_bar_showing() ) return; // Booking > Add booking page $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_new', 'title' => __( 'Add booking', 'booking' ), 'href' => wpbc_get_new_booking_url(), 'parent' => 'bar_wpbc', ) ); // Booking > Availability page $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_availability', 'title' => __( 'Availability', 'booking' ), 'href' => wpbc_get_availability_url(), 'parent' => 'bar_wpbc', ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_days_availability', 'title' => __( 'Days Availability', 'booking' ), 'href' => wpbc_get_availability_url() , 'parent' => 'bar_wpbc_availability' ) ); if ( class_exists( 'wpdev_bk_biz_m' ) ){ $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_seasons_availability', 'title' => __( 'Season Availability', 'booking' ), 'href' => wpbc_get_availability_url() . '&tab=season_availability', 'parent' => 'bar_wpbc_availability' ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_seasons_filters', 'title' => __( 'Seasons', 'booking' ), 'href' => wpbc_get_availability_url() . '&tab=filter', 'parent' => 'bar_wpbc_availability' ) ); } if ( wpbc_is_mu_user_can_be_here( 'only_super_admin' ) ) $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_general_availability', 'title' => __( 'General Availability', 'booking' ), 'href' => wpbc_get_settings_url() . '&scroll_to_section=wpbc_general_settings_availability_tab', 'parent' => 'bar_wpbc_availability' ) ); // Booking > Prices page if ( class_exists( 'wpdev_bk_biz_m' ) ){ $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_prices', 'title' => __( 'Prices', 'booking' ), 'href' => wpbc_get_price_url(), 'parent' => 'bar_wpbc', ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_daily_costs', 'title' => __('Daily Costs','booking'), 'href' => wpbc_get_price_url() . '&tab=cost', 'parent' => 'bar_wpbc_prices', ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_cost_advanced', 'title' => __('Form Options Costs','booking'), 'href' => wpbc_get_price_url() . '&tab=cost_advanced', 'parent' => 'bar_wpbc_prices', ) ); if ( class_exists( 'wpdev_bk_biz_l' ) ){ $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_coupons', 'title' => __('Discount Coupons','booking'), 'href' => wpbc_get_price_url() . '&tab=coupons', 'parent' => 'bar_wpbc_prices', ) ); } $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_seasons_costs', 'title' => __( 'Seasons', 'booking' ), 'href' => wpbc_get_price_url() . '&tab=filter', 'parent' => 'bar_wpbc_prices' ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_costs_payment_gateways', 'title' => __( 'Payment Setup', 'booking' ), 'href' => wpbc_get_settings_url() . '&tab=payment', 'parent' => 'bar_wpbc_prices' ) ); } //Booking > Resources page $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_resources', 'title' => ( ( class_exists( 'wpdev_bk_personal' ) ) ? __( 'Resources', 'booking' ) : __( 'Resource', 'booking' ) ), 'href' => $link_res, 'parent' => 'bar_wpbc', ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_resources_general', 'title' => ( ( class_exists( 'wpdev_bk_personal' ) ) ? __( 'Resources', 'booking' ) : __( 'Resource', 'booking' ) ), 'href' => $link_res, 'parent' => 'bar_wpbc_resources', ) ); if ( class_exists( 'wpdev_bk_biz_l' ) ) $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_resources_searchable', 'title' => __( 'Search Resources Setup', 'booking' ),//__( 'Searchable Resources', 'booking' ),//__( 'Search Availability', 'booking' ), 'href' => $link_res . '&tab=searchable_resources', 'parent' => 'bar_wpbc_resources' ) ); // if ($is_super_admin) // if ( class_exists( 'wpdev_bk_biz_l' ) ) // $wp_admin_bar->add_menu( // array( // 'id' => 'bar_wpbc_resources_search', // 'title' => __( 'Search Form / Results', 'booking' ), // 'href' => $link_settings . '&tab=search', // 'parent' => 'bar_wpbc_resources' // ) // ); //Booking > Settings General page $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_settings', 'title' => __( 'Settings', 'booking' ), 'href' => wpbc_get_settings_url(), 'parent' => 'bar_wpbc', ) ); if ($is_super_admin) $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_settings_general', 'title' => __( 'General', 'booking' ), 'href' => $link_settings, 'parent' => 'bar_wpbc_settings' ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_settings_form', 'title' => __( 'Booking Form', 'booking' ), 'href' => $link_settings . '&tab=form', 'parent' => 'bar_wpbc_settings' ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_settings_email', 'title' => __( 'Emails', 'booking' ), 'href' => $link_settings . '&tab=email', 'parent' => 'bar_wpbc_settings' ) ); $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_settings_sync', 'title' => __( 'Sync', 'booking' ), //FixIn: 8.0 'href' => $link_settings . '&tab=sync', 'parent' => 'bar_wpbc_settings' ) ); if ( class_exists( 'wpdev_bk_biz_s' ) ) $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_settings_payment', 'title' => __( 'Payment Setup', 'booking' ), 'href' => $link_settings . '&tab=payment', 'parent' => 'bar_wpbc_settings' ) ); if ( ( class_exists( 'wpdev_bk_biz_l' ) ) && ( wpbc_is_mu_user_can_be_here( 'only_super_admin' ) ) ) $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_settings_search', 'title' => __( 'Search Form / Results', 'booking' ), 'href' => $link_settings . '&tab=search', 'parent' => 'bar_wpbc_settings' ) ); if ( class_exists( 'wpdev_bk_multiuser' ) ) if ($is_super_admin) $wp_admin_bar->add_menu( array( 'id' => 'bar_wpbc_settings_users', 'title' => __( 'Users', 'booking' ), 'href' => $link_settings . '&tab=users', 'parent' => 'bar_wpbc_settings' ) ); } add_action( 'admin_bar_menu', 'wpbc_add__booking_menu__in__admin_top_bar', 70 ); // Add - TOP Bar - in admin menu // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Footer == " > function wpbc_show_booking_footer(){ $wpdev_copyright_adminpanel = get_bk_option( 'booking_wpdev_copyright_adminpanel' ); // check $message = ''; if ( ( 'Off' !== $wpdev_copyright_adminpanel ) && ( ! wpbc_is_this_demo() ) ) { /* $message .= '<a target="_blank" href="https://wpbookingcalendar.com/">Booking Calendar</a> ' . __('version' ,'booking') . ' ' . WP_BK_VERSION_NUM ; $message .= ' | '. sprintf(__('Add your %s on %swordpress.org%s, if you enjoyed by this plugin.' ,'booking'), '<a target="_blank" href="http://goo.gl/tcrrpK" >★★★★★</a>', '<a target="_blank" href="http://goo.gl/tcrrpK" >', '</a>' ); */ $message .= sprintf( __( 'If you like %s please leave us a %s rating. A huge thank you in advance!', 'booking' ) , '<strong>Booking Calendar</strong> ' . WP_BK_VERSION_NUM , '<a href="https://wordpress.org/support/plugin/booking/reviews/#new-post" target="_blank" title="' . esc_attr__( 'Thanks :)', 'booking' ) . '">' . '★★★★★' . '</a>' ); } if ( ! empty( $message ) ) { echo '<div id="wpbc-footer" style="position:absolute;bottom:40px;text-align:left;width:95%;font-size:0.9em;text-shadow:0 1px 0 #fff;margin:0;color:#888;">' . $message . '</div>'; ?><script type="text/javascript"> jQuery( document ).ready( function (){ jQuery( '#wpfooter' ).append( jQuery( '#wpbc-footer' ) ); } ); </script><?php } } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == P a g i n a t i o n o f T a b l e L i s t i n g == " > /** * Show P a g i n a t i o n * * @param int $summ_number_of_items - total number of items * @param int $active_page_num - number of activated page * @param int $num_items_per_page - number of items per page * @param array $only_these_parameters - array of keys to exclude from links * @param string $url_sufix - usefule for anchor to HTML section with specific ID, Example: '#my_section' */ function wpbc_show_pagination( $summ_number_of_items, $active_page_num, $num_items_per_page , $only_these_parameters = false, $url_sufix = '' ) { if ( empty( $num_items_per_page ) ) { $num_items_per_page = '10'; } $pages_number = ceil( $summ_number_of_items / $num_items_per_page ); if ( $pages_number < 2 ) return; //Fix: 5.1.4 - Just in case we are having tooo much resources, then we need to show all resources - and its empty string if ( ( isset($_REQUEST['wh_booking_type'] ) ) && ( strlen($_REQUEST['wh_booking_type']) > 1000 ) ) { $_REQUEST['wh_booking_type'] = ''; } // First parameter will overwriten by $_GET['page'] parameter $bk_admin_url = wpbc_get_params_in_url( wpbc_get_bookings_url( false, false ), array('page_num'), $only_these_parameters ); ?> <span class="wpdevelop wpbc-pagination"> <div class="container-fluid"> <div class="row"> <div class="col-sm-12 text-center control-group0"> <nav class="btn-toolbar"> <div class="btn-group wpbc-no-margin" style="float:none;"> <?php if ( $pages_number > 1 ) { ?> <a class="button button-secondary <?php echo ( $active_page_num == 1 ) ? ' disabled' : ''; ?>" href="<?php echo $bk_admin_url; ?>&page_num=<?php if ($active_page_num == 1) { echo $active_page_num; } else { echo ($active_page_num-1); } echo $url_sufix; ?>"> <?php _e('Prev', 'booking'); ?> </a> <?php } /** Number visible pages (links) that linked to active page, other pages skipped by "..." */ $num_closed_steps = 3; for ( $pg_num = 1; $pg_num <= $pages_number; $pg_num++ ) { if ( ! ( ( $pages_number > ( $num_closed_steps * 4) ) && ( $pg_num > $num_closed_steps ) && ( ( $pages_number - $pg_num + 1 ) > $num_closed_steps ) && ( abs( $active_page_num - $pg_num ) > $num_closed_steps ) ) ) { ?> <a class="button button-secondary <?php if ($pg_num == $active_page_num ) echo ' active'; ?>" href="<?php echo $bk_admin_url; ?>&page_num=<?php echo $pg_num; echo $url_sufix; ?>"> <?php echo $pg_num; ?> </a><?php if ( ( $pages_number > ( $num_closed_steps * 4) ) && ( ($pg_num+1) > $num_closed_steps ) && ( ( $pages_number - ( $pg_num + 1 ) ) > $num_closed_steps ) && ( abs($active_page_num - ( $pg_num + 1 ) ) > $num_closed_steps ) ) { echo ' <a class="button button-secondary disabled" href="javascript:void(0);">...</a> '; } } } if ( $pages_number > 1 ) { ?> <a class="button button-secondary <?php echo ( $active_page_num == $pages_number ) ? ' disabled' : ''; ?>" href="<?php echo $bk_admin_url; ?>&page_num=<?php if ($active_page_num == $pages_number) { echo $active_page_num; } else { echo ($active_page_num+1); } echo $url_sufix; ?>"> <?php _e('Next', 'booking'); ?> </a> <?php } ?> </div> </nav> </div> </div> </div> </span> <?php } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == DB - cheking if table, field or index exists == " > /** * Check if table exist * * @global $wpdb * @param string $tablename * @return 0|1 */ function wpbc_is_table_exists( $tablename ) { global $wpdb; if ( ( ( ! empty( $wpdb->prefix ) ) && ( strpos( $tablename, $wpdb->prefix ) === false ) ) || ( '_' == $wpdb->prefix ) //FixIn: 8.7.3.16 ) { $tablename = $wpdb->prefix . $tablename; } if ( 0 ) { $sql_check_table = $wpdb->prepare( "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE (TABLE_SCHEMA = '{$wpdb->dbname}') AND (TABLE_NAME = %s);", $tablename ); $res = $wpdb->get_results( $sql_check_table ); return count( $res ); } else { $sql_check_table = $wpdb->prepare("SHOW TABLES LIKE %s" , $tablename ); //FixIn: 5.4.3 $res = $wpdb->get_results( $sql_check_table ); return count( $res ); } } //FixIn: 10.0.0.1 /** * Check if we are in playground.wordpress.net , where used 'sqlite' DB * * @return bool */ function wpbc_is_this_wp_playground_db(){ return false; if ( ( ( function_exists( 'sqlite_open' ) ) || ( class_exists( 'SQLite3' ) ) ) && ( ! class_exists( 'wpdev_bk_personal' ) ) ){ return true; } else { return false; } } /** * Check if table exist * * @global $wpdb * @param string $tablename * @param $fieldname * @return 0|1 */ function wpbc_is_field_in_table_exists( $tablename , $fieldname) { if ( wpbc_is_this_wp_playground_db() ) { return 1; // Probably we are in playground.wordpress.net --> So then all such fields already was created //FixIn: 10.0.0.1 } global $wpdb; if ( ( ( ! empty( $wpdb->prefix ) ) && ( strpos( $tablename, $wpdb->prefix ) === false ) ) || ( '_' == $wpdb->prefix ) //FixIn: 8.7.3.16 ) { $tablename = $wpdb->prefix . $tablename; } if ( 0 ) { $sql_check_table = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='{$tablename}' AND TABLE_SCHEMA='{$wpdb->dbname}' "; $res = $wpdb->get_results( $sql_check_table ); foreach ( $res as $fld ) { if ( $fieldname === $fld->COLUMN_NAME ) { return 1; } } } else { $sql_check_table = "SHOW COLUMNS FROM {$tablename}"; $res = $wpdb->get_results( $sql_check_table ); foreach ( $res as $fld ) { if ( $fld->Field == $fieldname ) { return 1; } } } return 0; } /** * Check if index exist * * @global $wpdb * @param string $tablename * @param $fieldindex * @return 0|1 */ function wpbc_is_index_in_table_exists( $tablename , $fieldindex) { global $wpdb; if ( (! empty($wpdb->prefix) ) && ( strpos($tablename, $wpdb->prefix) === false ) ) $tablename = $wpdb->prefix . $tablename ; $sql_check_table = $wpdb->prepare("SHOW INDEX FROM {$tablename} WHERE Key_name = %s", $fieldindex ); $res = $wpdb->get_results( $sql_check_table ); if (count($res)>0) return 1; else return 0; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Security: Escaping & Sanitizing == " > /** * Sanitize $_GET, $_POST, $_REQUEST text parameters //FixIn: 10.0.0.12 * * @param $value * @param $keep_newlines bool * * @return string */ function wpbc_clean_text_value( $value , $keep_newlines = false ){ if ( $keep_newlines ) { $value_cleaned = sanitize_textarea_field( $value ); } else { $value_cleaned = sanitize_text_field( $value ); } return $value_cleaned; } // check $value for injection here function wpbc_clean_parameter( $value, $is_escape_sql = true ) { $value = preg_replace( '/<[^>]*>/', '', $value ); // clean any tags $value = str_replace( '<', ' ', $value ); $value = str_replace( '>', ' ', $value ); $value = strip_tags( $value ); //FixIn: 9.7.4.1 - escape coded html/xss // Escape any XSS injection $value = sanitize_textarea_field( $value ); $value = sanitize_textarea_field( html_entity_decode( $value ) ); // If we have field converted to 'Unicode Hex Character Code', then we make HTML decode firstly (html_entity_decode) and then make sanitizing if ( $is_escape_sql ) { $value = esc_sql( $value ); // Clean SQL injection //FixIn: 9.7.4.2 } $value = esc_textarea( $value ); //FixIn: 7.1.1.2 return $value; } /** * Check paramter if it number or comma separated list of numbers * * @global type $wpdb * @param string $value * @return string * * Exmaple: wpbc_clean_digit_or_csd( '12,a,45,9' ) => '12,0,45,9' * or wpbc_clean_digit_or_csd( '10a' ) => '10 * or wpbc_clean_digit_or_csd( array( '12,a,45,9', '10a' ) ) => array ( '12,0,45,9', '10' ) */ function wpbc_clean_digit_or_csd( $value ) { //FixIn:6.2.1.4 if ( $value === '' ) return $value; if ( is_array( $value ) ) { foreach ( $value as $key => $check_value ) { $value[ $key ] = wpbc_clean_digit_or_csd( $check_value ); } return $value; } $value = str_replace( ';', ',', $value ); $array_of_nums = explode(',', $value); $result = array(); foreach ($array_of_nums as $check_element) { $result[] = intval( $check_element ); //FixIn: 8.0.2.10 } $result = implode(',', $result ); return $result; } /** * Cehck about Valid date, like 2016-07-20 or digit * * @param string $value * @return string or int */ function wpbc_clean_digit_or_date( $value ) { //FixIn:6.2.1.4 if ( $value === '' ) return $value; if ( preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $value ) ) { return $value; // Date is valid in format: 2016-07-20 } else { return intval( $value ); } } /** * Check about Valid dat in format '2024-05-08' otherwise return '' * * @param string $value * * @return string date or '' if date was not valie */ function wpbc_clean_date( $value ) { if ( ( ! empty( $value ) ) && ( preg_match( "/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $value ) ) ){ return $value; // Date is valid in format: 2024-05-08 } return ''; // Date not Valid } /** * Escape any XSS injection from values in booking form * * @param array $structured_booking_data_arr [...] * * @return array [...] */ function wpbc_escape_any_xss_in_arr( $structured_booking_data_arr ) { foreach ( $structured_booking_data_arr as $field_name => $field_value ) { if ( is_array( $field_value ) ) { $structured_booking_data_arr[ $field_name ] = wpbc_escape_any_xss_in_arr( $field_value ); } else { $is_escape_sql = false; // Do not replace % $field_value_cleaned = wpbc_escape_any_xss_in_string( $field_value, $is_escape_sql ); $structured_booking_data_arr[ $field_name ] = $field_value_cleaned; } } return $structured_booking_data_arr; } /** * Escape any XSS injection from string values * * @param string $field_value * * @return string */ function wpbc_escape_any_xss_in_string( $field_value, $is_escape_sql = true ) { $field_value_cleaned = wpbc_clean_parameter( $field_value, $is_escape_sql ); $field_value_cleaned = str_replace( '%', '%', $field_value_cleaned ); // clean % in form, because can be problems with SQL prepare function return $field_value_cleaned; } function wpbc_esc_like( $value_trimmed ) { global $wpdb; if ( method_exists( $wpdb ,'esc_like' ) ) return $wpdb->esc_like( $value_trimmed ); // Its require minimum WP 4.0.0 else return addcslashes( $value_trimmed, '_%\\' ); // Direct implementation from $wpdb->esc_like( } /** * Sanitize term to Slug format (no spaces, lowercase). * urldecode - reverse munging of UTF8 characters. * * @param mixed $value * @return string */ function wpbc_get_slug_format( $value ) { return urldecode( sanitize_title( $value ) ); } /** * Clean user string for using in SQL LIKE statement - append to LIKE sql * * @param string $value - to clean * @return string - escaped * Exmaple: * $search_escaped_like_title = wpbc_clean_like_string_for_append_in_sql_for_db( $input_var ); * * $where_sql = " WHERE title LIKE ". $search_escaped_like_title ." "; */ function wpbc_clean_like_string_for_append_in_sql_for_db( $value ) { global $wpdb; $value_trimmed = trim( stripslashes( $value ) ); $wild = '%'; $like = $wild . wpbc_esc_like( $value_trimmed ) . $wild; $sql = $wpdb->prepare( "'%s'", $like ); return $sql; /* Help: * First half of escaping for LIKE special characters % and _ before preparing for MySQL. * Use this only before wpdb::prepare() or esc_sql(). Reversing the order is very bad for security. * * Example Prepared Statement: * * $wild = '%'; * $find = 'only 43% of planets'; * $like = $wild . wpbc_esc_like( $find ) . $wild; * $sql = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE '%s'", $like ); * * Example Escape Chain: * * $sql = esc_sql( wpbc_esc_like( $input ) ); */ } /** * Clean string for using in SQL LIKE requests inside single quotes: WHERE title LIKE '%". $escaped_search_title ."%' * Replaced _ to \_ % to \% \ to \\ * @param string $value - to clean * @return string - escaped * Exmaple: * $search_escaped_like_title = wpbc_clean_like_string_for_db( $input_var ); * * $where_sql = " WHERE title LIKE '%". $search_escaped_like_title ."%' "; * * Important! Use SINGLE quotes after in SQL query: LIKE '%".$data."%' */ function wpbc_clean_like_string_for_db( $value ){ global $wpdb; $value_trimmed = trim( stripslashes( $value ) ); $value_trimmed = wpbc_esc_like( $value_trimmed ); $value = trim( $wpdb->prepare( "'%s'", $value_trimmed ) , "'" ); return $value; /* Help: * First half of escaping for LIKE special characters % and _ before preparing for MySQL. * Use this only before wpdb::prepare() or esc_sql(). Reversing the order is very bad for security. * * Example Prepared Statement: * * $wild = '%'; * $find = 'only 43% of planets'; * $like = $wild . wpbc_esc_like( $find ) . $wild; * $sql = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE '%s'", $like ); * * Example Escape Chain: * * $sql = esc_sql( wpbc_esc_like( $input ) ); */ } /** * Escape string from SQL for the HTML form field * * @param string $value * @return string * * Used: esc_sql function. * * https://codex.wordpress.org/Function_Reference/esc_sql * Note: Be careful to use this function correctly. It will only escape values to be used in strings in the query. * That is, it only provides escaping for values that will be within quotes in the SQL (as in field = '{$escaped_value}'). * If your value is not going to be within quotes, your code will still be vulnerable to SQL injection. * For example, this is vulnerable, because the escaped value is not surrounded by quotes in the SQL query: * ORDER BY {$escaped_value}. As such, this function does not escape unquoted numeric values, field names, or SQL keywords. * */ function wpbc_clean_string_for_form( $value ){ global $wpdb; $value_trimmed = trim( stripslashes( $value ) ); //FixIn: 8.0.2.10 //Fix for update of WP 4.8.3 if ( method_exists( $wpdb, 'remove_placeholder_escape' ) ) $esc_sql_value = $wpdb->remove_placeholder_escape( esc_sql( $value_trimmed ) ); else $esc_sql_value = esc_sql( $value_trimmed ); //$value = trim( $wpdb->prepare( "'%s'", $esc_sql_value ) , "'" ); $esc_sql_value = trim( stripslashes( $esc_sql_value ) ); return $esc_sql_value; } /** * Escape shortcode parameters * * @param array $attr * * @return array */ function wpbc_escape_shortcode_params( $attr ) { //FixIn: 9.7.3.6.1 if ( is_array( $attr ) ) { $scaped_attr = array(); foreach ( $attr as $attr_key => $attr_val ) { $attr_key = esc_attr( $attr_key ); $attr_val = esc_attr( $attr_val ); $scaped_attr[ $attr_key ] = $attr_val; } return $scaped_attr; } if ( is_string( $attr ) ) { //FixIn: 9.7.3.6.2 $scaped_attr = esc_attr( $attr ); return $scaped_attr; } return $attr; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Number of New Bookings == " > /** * Reset Cache for getting Number of new bookings. After this operation, system will get number of new bookings from the DB. * * @return void */ function wpbc_booking_cache__new_bookings__reset(){ update_bk_option( 'booking_cache__new_bookings__saved_date', '' ); } add_action( 'wpbc_track_new_booking', 'wpbc_booking_cache__new_bookings__reset' ); add_action( 'wpbc_set_booking_pending', 'wpbc_booking_cache__new_bookings__reset' ); add_action( 'wpbc_set_booking_approved', 'wpbc_booking_cache__new_bookings__reset' ); add_action( 'wpbc_move_booking_to_trash', 'wpbc_booking_cache__new_bookings__reset' ); add_action( 'wpbc_restore_booking_from_trash', 'wpbc_booking_cache__new_bookings__reset' ); add_action( 'wpbc_delete_booking_completely', 'wpbc_booking_cache__new_bookings__reset' ); add_action( 'wpbc_set_booking_as_read', 'wpbc_booking_cache__new_bookings__reset' ); add_action( 'wpbc_set_booking_as_unread', 'wpbc_booking_cache__new_bookings__reset' ); /** * Get number of new bookings * @return false|int|mixed|null */ function wpbc_db_get_number_new_bookings(){ $new_bookings__number = get_bk_option( 'booking_cache__new_bookings__number' ); $new_bookings__saved_date = get_bk_option( 'booking_cache__new_bookings__saved_date' ); $nowdate_str_ymdhis = date( 'Y-m-d H:i:s', strtotime( 'now' ) ); if ( ! empty( $new_bookings__saved_date ) ) { $is_expired = ( strtotime( $new_bookings__saved_date ) <= strtotime( '-10 minutes', strtotime( $nowdate_str_ymdhis ) ) ); if ( ! $is_expired ) { return $new_bookings__number; } } if ( 1 ) { global $wpdb; //if ( wpbc_is_field_in_table_exists('booking','is_new') == 0 ) return 0; // do not created this field, so return 0 $trash_bookings = ' AND bk.trash != 1 '; //FixIn: 6.1.1.10 - check also below usage of {$trash_bookings} $sql_req = "SELECT bk.booking_id FROM {$wpdb->prefix}booking as bk WHERE bk.is_new = 1 {$trash_bookings} "; $sql_req = apply_bk_filter( 'get_sql_for_checking_new_bookings', $sql_req ); $sql_req = apply_bk_filter( 'get_sql_for_checking_new_bookings_multiuser', $sql_req ); $bookings = $wpdb->get_results( $sql_req ); $bookings_count = count( $bookings ); } update_bk_option( 'booking_cache__new_bookings__number', $bookings_count ); update_bk_option( 'booking_cache__new_bookings__saved_date', $nowdate_str_ymdhis ); return $bookings_count; } /** * Update 'is_new' status of bookings in Database * * @param $id_of_new_bookings inr or comma seperated ID of bookings. Example: '1' | '3,5,7,9' * @param $is_new '0' | '1' * @param $user_id 'user_id' * */ function wpbc_db_update_number_new_bookings( $id_of_new_bookings, $is_new = '0' , $user_id = 1 ){ global $wpdb; if (count($id_of_new_bookings) > 0 ) { //if (wpbc_is_field_in_table_exists('booking','is_new') == 0) return 0; // do not created this field, so return 0 $id_of_new_bookings = implode(',', $id_of_new_bookings); $id_of_new_bookings = wpbc_clean_like_string_for_db( $id_of_new_bookings ); //debuge($id_of_new_bookings); if ($id_of_new_bookings == 'all') { $update_sql = "UPDATE {$wpdb->prefix}booking AS bk SET bk.is_new = {$is_new} WHERE bk.is_new != {$is_new} "; //FixIn: 8.2.1.18 //debuge($update_sql); $update_sql = apply_bk_filter('update_sql_for_checking_new_bookings', $update_sql, 0 , $user_id ); } else $update_sql = "UPDATE {$wpdb->prefix}booking AS bk SET bk.is_new = {$is_new} WHERE bk.booking_id IN ( {$id_of_new_bookings} ) "; if ( false === $wpdb->query( $update_sql ) ) { debuge_error('Error during updating status of bookings at DB',__FILE__,__LINE__); die(); } } } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Requests: News , Version == " > define ('OBC_CHECK_URL', 'https://wpbookingcalendar.com/'); function wpdev_ajax_check_bk_news( $sub_url = '' ){ $v=array(); if (class_exists('wpdev_bk_personal')) $v[] = 'wpdev_bk_personal'; if (class_exists('wpdev_bk_biz_s')) $v[] = 'wpdev_bk_biz_s'; if (class_exists('wpdev_bk_biz_m')) $v[] = 'wpdev_bk_biz_m'; if (class_exists('wpdev_bk_biz_l')) $v[] = 'wpdev_bk_biz_l'; if (class_exists('wpdev_bk_multiuser')) $v[] = 'wpdev_bk_multiuser'; $obc_settings = array(); $ver = get_bk_option('bk_version_data'); if ( $ver !== false ) { $obc_settings = array( 'subscription_key'=>wp_json_encode($ver) ); } $params = array( 'action' => 'get_news', 'subscription_email' => isset($obc_settings['subscription_email'])?$obc_settings['subscription_email']:false, 'subscription_key' => isset($obc_settings['subscription_key'])?$obc_settings['subscription_key']:false, 'bk' => array('bk_ver'=>WPDEV_BK_VERSION, 'bk_url'=>WPBC_PLUGIN_URL,'bk_dir'=>WPBC_PLUGIN_DIR, 'bk_clss'=>$v), 'siteurl' => get_option('siteurl'), 'siteip' => wpbc_get_server_ip(), //FixIn: 9.8.14.3 'admin_email' => get_option('admin_email') ); $request = new WP_Http(); if (empty($sub_url)) $sub_url = 'info/'; $result = $request->request( OBC_CHECK_URL . $sub_url, array( 'method' => 'POST', 'timeout' => 15, 'body' => $params )); if (!is_wp_error($result) && ($result['response']['code']=='200') && (true) ) { $string = ($result['body']); //$string = str_replace( "'", ''', $string ); echo $string; echo ' <script type="text/javascript"> '; echo ' jQuery("#ajax_bk_respond").after( jQuery("#ajax_bk_respond #bk_news_loaded") );'; echo ' jQuery("#bk_news_loaded").slideUp(1).slideDown(1500);'; echo ' </script> '; } else /**/ { // Some error appear echo '<div id="bk_errror_loading">'; if (is_wp_error($result)) echo $result->get_error_message(); else echo $result['response']['message']; echo '</div>'; echo ' <script type="text/javascript"> '; echo ' document.getElementById("bk_news").style.display="none";'; echo ' jQuery("#ajax_bk_respond").after( jQuery("#ajax_bk_respond #bk_errror_loading") );'; echo ' jQuery("#bk_errror_loading").slideUp(1).slideDown(1500);'; echo ' jQuery("#bk_news_section").animate({opacity:1},3000).slideUp(1500);'; echo ' </script> '; } } /** * Check if user defined to not show up_news section. * */ function wpbc_is_show_up_news(){ //FixIn: 8.1.3.9 $wpdev_copyright_adminpanel = get_bk_option( 'booking_wpdev_copyright_adminpanel' ); // check if ( ( $wpdev_copyright_adminpanel === 'Off' ) && ( ! wpbc_is_this_demo() ) && ( class_exists('wpdev_bk_personal') ) ) { return false; } else { return true; } } function wpdev_ajax_check_bk_version(){ $v=array(); if (class_exists('wpdev_bk_personal')) $v[] = 'wpdev_bk_personal'; if (class_exists('wpdev_bk_biz_s')) $v[] = 'wpdev_bk_biz_s'; if (class_exists('wpdev_bk_biz_m')) $v[] = 'wpdev_bk_biz_m'; if (class_exists('wpdev_bk_biz_l')) $v[] = 'wpdev_bk_biz_l'; if (class_exists('wpdev_bk_multiuser')) $v[] = 'wpdev_bk_multiuser'; $obc_settings = array(); $params = array( 'action' => 'set_register', 'order_number' => isset($_POST['order_num'])?$_POST['order_num']:false, 'bk' => array('bk_ver'=>WPDEV_BK_VERSION, 'bk_url'=>WPBC_PLUGIN_URL,'bk_dir'=>WPBC_PLUGIN_DIR, 'bk_clss'=>$v), 'siteurl' => get_option('siteurl'), 'siteip' => wpbc_get_server_ip(), //FixIn: 9.8.14.3 'admin_email' => get_option('admin_email') ); update_bk_option( 'bk_version_data' , serialize($params) ); $request = new WP_Http(); $result = $request->request( OBC_CHECK_URL . 'register/', array( 'method' => 'POST', 'timeout' => 15, 'body' => $params )); if ( ! is_wp_error($result) && ( $result['response']['code']=='200' ) && ( true ) ) { $string = ($result['body']); //$string = str_replace( "'", ''', $string ); echo $string ; echo ' <script type="text/javascript"> '; echo ' jQuery("#ajax_message").append( jQuery("#ajax_respond #bk_registration_info") );'; echo ' jQuery("#ajax_message").append( "<div id=\'bk_registration_info_reload\'>If page will not reload automatically, please refresh page after 60 seconds...</div>" );'; echo ' </script> '; } else /**/ { // Some error appear echo '<div id="bk_errror_loading" class="warning_message" >'; echo '<div class="info_message">'; _e('Warning! Some error occur, during sending registration request.' ,'booking'); echo '</div>'; if (is_wp_error($result)) echo $result->get_error_message(); else echo $result['response']['message']; echo '<br /><br />'; _e('Please refresh this page and if the same error appear again contact support by email (with info about order number and website) for finishing the registrations' ,'booking'); echo ' <a href="mailto:activate@wpbookingcalendar.com">activate@wpbookingcalendar.com</a>'; echo '</strong></div>'; echo ' <script type="text/javascript"> '; echo ' jQuery( "#ajax_message" ).html( "" );'; echo ' jQuery("#ajax_message").append( jQuery("#ajax_respond #bk_errror_loading") );'; echo ' jQuery("#bk_errror_loading").slideUp(1).slideDown(1500);'; echo ' jQuery("#recheck_version").animate({opacity:1},3000).slideUp(1500);'; echo ' </script> '; } } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == User ID | Role - functions == " > /** * Check if Current User have specific Role * * @return bool Whether the current user has the given capability. */ function wpbc_is_current_user_have_this_role( $user_role ) { if ( $user_role == 'administrator' ) $user_role = 'activate_plugins'; if ( $user_role == 'editor' ) $user_role = 'publish_pages'; if ( $user_role == 'author' ) $user_role = 'publish_posts'; if ( $user_role == 'contributor' ) $user_role = 'edit_posts'; if ( $user_role == 'subscriber') $user_role = 'read'; return current_user_can( $user_role ); } /** * Get Current ID of user or get user ID of Forced log in user in Booking Calendar MultiUser version. * * @return int */ function wpbc_get_current_user_id() { //FixIn: 9.2.4.1 if ( function_exists( 'wpbc_mu__wp_get_current_user' ) ) { $user = wpbc_mu__wp_get_current_user(); $user_bk_id = $user->ID; } else { $user_bk_id = get_current_user_id(); } return $user_bk_id; } /** * Get Current User Object or get User object of Forced log in user in Booking Calendar MultiUser version. * * @return stdClass|WP_User|null */ function wpbc_get_current_user() { //FixIn: 9.2.4.1 if ( function_exists( 'wpbc_mu__wp_get_current_user' ) ) { $user = wpbc_mu__wp_get_current_user(); } else { $user = wp_get_current_user(); } return $user; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Messages for Admin panel == " > /** * Show "Saved Changes" message at the top of settings page. * */ function wpbc_show_changes_saved_message() { wpbc_show_message ( __('Changes saved.','booking'), 5 ); } /** * Show Message at Top of Admin Pages * * @param type $message - mesage to show * @param type $time_to_show - number of seconds to show, if 0 or skiped, then unlimited time. * @param type $message_type - Default: updated { updated | error | notice } */ function wpbc_show_message ( $message, $time_to_show , $message_type = 'updated') { // Generate unique HTML ID for the message $inner_message_id = intval( time() * rand(10, 100) ); // Get formated HTML message $notice = wpbc_get_formated_message( $message, $message_type, $inner_message_id ); // Get the time of message showing $time_to_show = intval( $time_to_show ) * 1000; // Show this Message ?> <script type="text/javascript"> if ( jQuery('.wpbc_admin_message').length ) { jQuery('.wpbc_admin_message').append( '<?php echo $notice; ?>' ); <?php if ( $time_to_show > 0 ) { ?> jQuery('#wpbc_inner_message_<?php echo $inner_message_id; ?>').animate({opacity: 1},<?php echo $time_to_show; ?>).fadeOut( 2000 ); <?php } ?> } </script> <?php } /** * Escape and prepare message to show it * * @param type $message - message * @param type $message_type - Default: updated { updated | error | notice } * @param string $inner_message_id - ID of message DIV, can be skipped * @return string */ function wpbc_get_formated_message ( $message, $message_type = 'updated', $inner_message_id = '') { // Recheck for any "lang" shortcodes for replacing to correct language $message = wpbc_lang( $message ); // Escape any JavaScript from message $notice = html_entity_decode( esc_js( $message ) ,ENT_QUOTES) ; $notice .= '<a class="close tooltip_left" rel="tooltip" title="'. esc_js(__("Hide",'booking')). '" data-dismiss="alert" href="javascript:void(0)" onclick="javascript:jQuery(this).parent().hide();">×</a>'; if (! empty( $inner_message_id )) $inner_message_id = 'id="wpbc_inner_message_'. $inner_message_id .'"'; $notice = '<div '.$inner_message_id.' class="wpbc_inner_message '. $message_type . '">' . $notice . '</div>'; return $notice; } /** * Show system info in settings page * * @param string $message ... * @param string $message_type 'info' | 'warning' | 'error' * @param string $title __('Important!' ,'booking') | __('Note' ,'booking') * * Exmaple: wpbc_show_message_in_settings( __( 'Nothing Found', 'booking' ), 'warning', __('Important!' ,'booking') ); */ function wpbc_show_message_in_settings( $message, $message_type = 'info', $title = '' , $is_echo = true ) { $message_content = ''; $message_content .= '<div class="clear"></div>'; $message_content .= '<div class="wpbc-settings-notice notice-' . $message_type . '" style="text-align:left;">'; if ( ! empty( $title ) ) $message_content .= '<strong>' . esc_js( $title ) . '</strong> '; $message_content .= html_entity_decode( esc_js( $message ) ,ENT_QUOTES) ; $message_content .= '</div>'; $message_content .= '<div class="clear"></div>'; if ( $is_echo ) echo $message_content; else return $message_content; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Meta Boxes - Open/Close == " > /** * Meta box section open tag * * @param string $metabox_id HTML ID of section * @param string $title Title in section * @param array $params [ 'is_section_visible_after_load' => true, // Default true - is this section Visible, after page loading or hidden - useful for Booking > Settings General page LEFT column sections 'is_show_minimize' => true // Default true - is show minimize button at top right section ] * * @return void */ function wpbc_open_meta_box_section( $metabox_id, $title, $params = array() ) { $defaults = array( 'is_section_visible_after_load' => true, 'is_show_minimize' => true, 'dismiss_button' => '', 'css_class' => 'postbox' ); $params = wp_parse_args( $params, $defaults ); $my_close_open_win_id = $metabox_id . '_metabox'; ?> <div class='meta-box'> <div id="<?php echo $my_close_open_win_id; ?>" class="<?php echo esc_attr( $params['css_class'] ); ?> <?php if ( $params['is_show_minimize'] ) { if ( '1' == get_user_option( 'booking_win_' . $my_close_open_win_id ) ) { echo 'closed'; } } ?>" style="<?php if ( ! $params['is_section_visible_after_load'] ) { echo 'display:none'; } ?>" ><div class="postbox-header" style="display: flex;flex-flow: row nowrap;border-bottom: 1px solid #ccd0d4;"> <h3 class='hndle' style="flex: 1 1 auto;border: none;"> <span><?php echo wp_kses_post( $title ); ?></span> <?php echo $params['dismiss_button']; ?> </h3> <?php if ( $params['is_show_minimize'] ) { ?> <div title="<?php _e('Click to toggle','booking'); ?>" class="handlediv" onclick="javascript:wpbc_verify_window_opening(<?php echo wpbc_get_current_user_id(); ?>, '<?php echo $my_close_open_win_id; ?>');" ><br/></div> <?php } ?> </div> <div class="inside"> <?php } function wpbc_close_meta_box_section() { ?> </div> </div> </div> <?php } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Is Dismissed == " > /** * Only check if Dismised or visible this section * * @param $element_html_id * * @return bool */ function wpbc_is_dismissed_panel_visible( $element_html_id ) { return ( '1' != get_user_option( 'booking_win_' . $element_html_id ) ); } /** * Show dismiss close button for specific HTML section * * @param string $element_html_id - ID of HTML selection to dismiss * @param array $params - array( 'title' => 'Dismiss', 'is_apply_in_demo' => false ) ) * * @return bool * * Examples: * $is_dismissed = wpbc_is_dismissed( 'wpbc_dashboard_section_video_f' ); * * $is_dismissed = wpbc_is_dismissed( 'html_id', array( 'is_apply_in_demo' => $is_apply_in_demo ) ); * * $is_panel_visible = wpbc_is_dismissed( 'wpbc-panel-get-started', array( * 'title' => '<i class="menu_icon icon-1x wpbc_icn_close"></i> ', * 'hint' => __( 'Dismiss', 'booking' ), * 'class' => 'wpbc_panel_get_started_dismiss', * 'css' => '' * )); */ function wpbc_is_dismissed( $element_html_id, $params = array() ){ //FixIn: 8.1.3.10 $defaults = array( 'title' => '×' , 'hint' => __( 'Dismiss' ,'booking') , 'is_apply_in_demo' => ! wpbc_is_this_demo() , 'class' => '' // CSS class of close X element , 'css' => '' // Style class of close X element , 'dismiss_css_class' => '' //'.'.$id . '_' . 'weekdays_conditions' ); $params = wp_parse_args( $params, $defaults ); $params['css'] = 'text-decoration: none;font-weight: 600;float:right;' . $params['css']; // Append CSS instead of replace it if ( ( class_exists( 'WPBC_Dismiss' )) && ( $params[ 'is_apply_in_demo' ] ) ) { global $wpbc_Dismiss; $is_panel_visible = $wpbc_Dismiss->render( array( 'id' => $element_html_id, 'title' => $params['title'], 'hint' => $params['hint'], 'class' => $params['class'], 'css' => $params['css'], 'dismiss_css_class' => $params['dismiss_css_class'] ) ); } else { $is_panel_visible = false; } return $is_panel_visible; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Inline JavaScript to Footer page == " > /** * Queue JavaScript for later output at footer * * @param string $code */ function wpbc_enqueue_js( $code ) { global $wpbc_queued_js; if ( empty( $wpbc_queued_js ) ) { $wpbc_queued_js = ''; } $wpbc_queued_js .= "\n" . $code . "\n"; } /** * Output any queued javascript code in the footer. */ function wpbc_print_js() { global $wpbc_queued_js; if ( ! empty( $wpbc_queued_js ) ) { $wpbc_queued_js = wp_check_invalid_utf8( $wpbc_queued_js ); $wpbc_queued_js = wp_specialchars_decode( $wpbc_queued_js , ENT_COMPAT); // Converts double quotes '"' => '"' $wpbc_queued_js = preg_replace( '/&#(x)?0*(?(1)27|39);?/i', "'", $wpbc_queued_js ); $wpbc_queued_js = str_replace( "\r", '', $wpbc_queued_js ); echo "<!-- WPBC JavaScript -->\n<script type=\"text/javascript\">\njQuery(function($) {" . $wpbc_queued_js . "});\n</script>\n<!-- End WPBC JavaScript -->\n"; $wpbc_queued_js = ''; unset( $wpbc_queued_js ); } } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Support functions for MU version == " > /** * Set active User Environment in MultiUser version, depend from owner of booking resource * * @param int $previous_active_user (default=-1) - blank parameter * @param int $bktype - booking resource ID for checking * @return int - ID of Previous Active User * * Usage: $previous_active_user = apply_bk_filter('wpbc_mu_set_environment_for_owner_of_resource', -1, $bktype ); */ function wpbc_mu_set_environment_for_owner_of_resource( $previous_active_user = -1, $bktype = 1 ) { if ( class_exists( 'wpdev_bk_multiuser' ) ) { // Get the owner of this booking resource $user_bk_id = apply_bk_filter( 'get_user_of_this_bk_resource', false, $bktype ); $user = wpbc_get_current_user(); // Get possible other active user settings $previous_active_user = apply_bk_filter( 'get_client_side_active_params_of_user' ); // Set active user of that specific booking resource make_bk_action( 'check_multiuser_params_for_client_side_by_user_id', $user_bk_id ); } return $previous_active_user; } add_bk_filter('wpbc_mu_set_environment_for_owner_of_resource', 'wpbc_mu_set_environment_for_owner_of_resource'); /** * Set environment for this user in MU version * * @param int $previous_active_user - ID of user * Usage: make_bk_action('wpbc_mu_set_environment_for_user', $previous_active_user ); */ function wpbc_mu_set_environment_for_user( $previous_active_user ) { if ( $previous_active_user !== -1 ) { // Reactivate the previous active user make_bk_action('check_multiuser_params_for_client_side_by_user_id', $previous_active_user ); } } add_bk_action('wpbc_mu_set_environment_for_user', 'wpbc_mu_set_environment_for_user'); /** * Check if we have simulated user login in Booking Calendar MultiUser version * return user ID of regular simulated user or 0 if not simulated * @return int */ function wpbc_mu__is_simulated_login_as_user(){ if ( class_exists( 'wpdev_bk_multiuser' ) ) { $real_current_user_id = get_current_user_id(); // Is current user suer booking admin and if this user was simulated log in $is_user_super_admin = apply_bk_filter( 'is_user_super_admin', $real_current_user_id ); if ( $is_user_super_admin ) { $simulate_user_id = intval( get_option( 'booking_simulate_login_as_user' ) ); // Is user was simulated log in if ( ( ! empty( $simulate_user_id ) ) && ( $simulate_user_id > 0 ) ) { return $simulate_user_id; } } } return 0; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Currency functions (>=BS) == " > /** * Format booking cost with a currency symbol. * In MultiUser version also checking about specific currency that belong to specific WordPress user. * This checking based on belonging specific booking resource to specific user. * * @param float $cost * @param int $booking_resource_id * @return string - $cost_to_show_with_currency */ function wpbc_get_cost_with_currency_for_user( $cost, $booking_resource_id = 0 ){ if ( ( $cost === '' ) || ( ! class_exists( 'wpdev_bk_biz_s' ) ) ) { return ''; } if ( ! empty( $booking_resource_id ) ) { $previous_active_user = apply_bk_filter( 'wpbc_mu_set_environment_for_owner_of_resource', - 1, $booking_resource_id ); } // MU $cost_to_show_with_currency = wpbc_cost_show( $cost, array( 'currency' => wpbc_get_currency() ) ); if ( ! empty( $booking_resource_id ) ) { make_bk_action( 'wpbc_mu_set_environment_for_user', $previous_active_user ); } // MU return $cost_to_show_with_currency; } /** * Get currency Symbol. * In MultiUser version also checking about specific currency that belong to specific WordPress user. * This checking based on belonging specific booking resource to specific user. * * @param int $booking_resource_id - ID of specific booking resource * @return string - currency symbol */ function wpbc_get_currency_symbol_for_user( $booking_resource_id = 0 ){ if ( ! class_exists( 'wpdev_bk_biz_s' ) ) { return ''; } if ( ! empty( $booking_resource_id ) ) { $previous_active_user = apply_bk_filter( 'wpbc_mu_set_environment_for_owner_of_resource', - 1, $booking_resource_id ); } // MU $currency_symbol = wpbc_get_currency_symbol(); if ( ! empty( $booking_resource_id ) ) { make_bk_action( 'wpbc_mu_set_environment_for_user', $previous_active_user ); } // MU return $currency_symbol; } /** * Get Cost per period: DAY / NIGHT / FIXED / HOUR * In MultiUser version This checking based on belonging specific booking resource to specific user. * * @param int $booking_resource_id - ID of specific booking resource * @return string - cost per period */ function wpbc_get_cost_per_period_for_user( $booking_resource_id = 0 ){ //FixIn: 10.0.0.14 if ( ! class_exists( 'wpdev_bk_biz_s' ) ) { return ''; } if ( ! empty( $booking_resource_id ) ) { $previous_active_user = apply_bk_filter( 'wpbc_mu_set_environment_for_owner_of_resource', - 1, $booking_resource_id ); } // MU $cost_period = get_bk_option( 'booking_paypal_price_period' ); if ( ! empty( $booking_resource_id ) ) { make_bk_action( 'wpbc_mu_set_environment_for_user', $previous_active_user ); } // MU return $cost_period; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Approve | Pending functions == " > /** * Check is this booking approved -- get booking dates in DB and check status * * @param $booking_id * * @return bool */ function wpbc_is_booking_approved( $booking_id ){ //FixIn: 8.1.2.8 $is_booking_approved = false; global $wpdb; $sql = $wpdb->prepare( "SELECT DISTINCT approved FROM {$wpdb->prefix}bookingdates WHERE booking_id = %d ORDER BY booking_date", $booking_id ); $dates_result = $wpdb->get_results( $sql ); foreach ( $dates_result as $my_date ) { if ( '1' == $my_date->approved ) { $is_booking_approved = true; //FixIn: 8.3.1.2 } } return $is_booking_approved; } /** * Approve booking in DB (update booking dates in DB like approved = '1') * * @param $booking_id int | CSD e.g. 100 | '1,5,10' * * @return bool */ function wpbc_db__booking_approve( $booking_id ) { $booking_id = wpbc_clean_digit_or_csd( $booking_id ); // Check paramter if it number or comma separated list of numbers global $wpdb; $update_sql = "UPDATE {$wpdb->prefix}bookingdates SET approved = '1' WHERE booking_id IN ({$booking_id});"; if ( false === $wpdb->query( $update_sql ) ) { return false; } return true; } /** * Approve specific booking and send email about this. * * @param int $booking_id - ID of booking * @param string $email_reason */ function wpbc_auto_approve_booking( $booking_id , $email_reason = '' ) { $booking_id = wpbc_clean_digit_or_csd( $booking_id ); // Check paramter if it number or comma separated list of numbers if ( is_numeric( $booking_id ) ) { //FixIn: 8.1.2.8 if ( ! wpbc_is_booking_approved( $booking_id ) ) { do_action( 'wpbc_booking_approved', $booking_id, 1 ); //FixIn: 8.7.6.1 wpbc_send_email_approved( $booking_id, 1, $email_reason ); } } else { $booking_id_arr = explode( ',',$booking_id ); foreach ( $booking_id_arr as $bk_id ) { if ( ! wpbc_is_booking_approved( $bk_id ) ) { do_action( 'wpbc_booking_approved', $bk_id, 1 ); //FixIn: 8.7.6.1 wpbc_send_email_approved( $bk_id, 1, $email_reason ); } } } $db_result = wpbc_db__booking_approve( $booking_id ); if ( false === $db_result ){ wpbc_redirect( site_url() ); } } /** * Set as Pending specific booking and send email about this. * * @param int $booking_id - ID of booking * @param string $denyreason */ function wpbc_auto_pending_booking( $booking_id, $denyreason = '' ) { //FixIn: 8.4.7.25 global $wpdb; $booking_id = wpbc_clean_digit_or_csd( $booking_id ); // Check paramter if it number or comma separated list of numbers if ( is_numeric( $booking_id ) ) { //FixIn: 8.1.2.8 if ( wpbc_is_booking_approved( $booking_id ) ) { wpbc_send_email_deny( $booking_id, 1, $denyreason ); } } else { $booking_id_arr = explode( ',',$booking_id ); foreach ( $booking_id_arr as $bk_id ) { if ( wpbc_is_booking_approved( $bk_id ) ) { wpbc_send_email_deny( $bk_id, 1, $denyreason ); } } } $update_sql = "UPDATE {$wpdb->prefix}bookingdates SET approved = '0' WHERE booking_id IN ({$booking_id});"; if ( false === $wpdb->query( $update_sql ) ){ wpbc_redirect( site_url() ); } } /** * Cancel (move to Trash) specific booking. * * @param int $booking_id - ID of booking * @param string $email_reason - reason of cancellation */ function wpbc_auto_cancel_booking( $booking_id , $email_reason = '' ) { //FixIn: 8.4.7.25 global $wpdb; $booking_id = wpbc_clean_digit_or_csd( $booking_id ); // Check paramter if it number or comma separated list of numbers if ( empty( $email_reason ) ) { //FixIn: 8.4.7.25 // Get the reason of cancellation. $email_reason = __( 'Payment rejected', 'booking' ); $auto_cancel_pending_unpaid_bk_is_send_email = get_bk_option( 'booking_auto_cancel_pending_unpaid_bk_is_send_email' ); if ( $auto_cancel_pending_unpaid_bk_is_send_email == 'On' ) { $email_reason = get_bk_option( 'booking_auto_cancel_pending_unpaid_bk_email_reason' ); } } // Send decline emails wpbc_send_email_trash( $booking_id, 1, $email_reason ); if ( false === $wpdb->query( "UPDATE {$wpdb->prefix}booking AS bk SET bk.trash = 1 WHERE booking_id IN ({$booking_id})" ) ){ wpbc_redirect( site_url() ); } } //FixIn: 9.9.0.43 /** * Auto approve booking and send email, after successful payment process * * It resolves issue in Booking Calendar MultiUser version for sending "regular email" to visitors, if was made booking for booking resource, that belong to regular user, * and was activated this option "Receive all payments only to Super Booking Admin account" at the WP Booking Calendar > Settings General page in "Multiuser Options" section * * @param $booking_id * * @return void */ function wpbc_auto_approve_booking__after_payment( $booking_id ) { //-------------------------------------------------------------------------------------------------- //FixIn: 9.9.0.43 $is_force_again = false; if ( class_exists( 'wpdev_bk_multiuser' ) ) { list( $booking_hash, $booking_resource_id ) = wpbc_hash__get_booking_hash__resource_id( $booking_id ); $user_id = apply_bk_filter( 'get_user_of_this_bk_resource', false, $booking_resource_id ); $is_booking_resource_user_super_admin = apply_bk_filter( 'is_user_super_admin', $user_id ); if ( ( 'On' == get_bk_option( 'booking_super_admin_receive_regular_user_payments' ) ) && ( ! $is_booking_resource_user_super_admin ) ) { // Finish "Super-User" forcing make_bk_action( 'finish_force_using_this_user' ); //Reactivate data for "regular user make_bk_action( 'check_multiuser_params_for_client_side_by_user_id', $user_id ); $is_force_again = true; } } // ------------------------------------------------------------------------------------------------- wpbc_auto_approve_booking( $booking_id ); if ( $is_force_again ) { //FixIn: 9.9.0.43 make_bk_action( 'make_force_using_this_user', - 999 ); // '-999' - This ID "by default" is the ID of super booking admin user } } /** * Auto cancel booking and send email, after successful payment process * * It resolves issue in Booking Calendar MultiUser version for sending "regular email" to visitors, if was made booking for booking resource, that belong to regular user, * and was activated this option "Receive all payments only to Super Booking Admin account" at the WP Booking Calendar > Settings General page in "Multiuser Options" section * * @param $booking_id * * @return void */ function wpbc_auto_cancel_booking__after_payment( $booking_id ) { //-------------------------------------------------------------------------------------------------- //FixIn: 9.9.0.43 $is_force_again = false; if ( class_exists( 'wpdev_bk_multiuser' ) ) { list( $booking_hash, $booking_resource_id ) = wpbc_hash__get_booking_hash__resource_id( $booking_id ); $user_id = apply_bk_filter( 'get_user_of_this_bk_resource', false, $booking_resource_id ); $is_booking_resource_user_super_admin = apply_bk_filter( 'is_user_super_admin', $user_id ); if ( ( 'On' == get_bk_option( 'booking_super_admin_receive_regular_user_payments' ) ) && ( ! $is_booking_resource_user_super_admin ) ) { // Finish "Super-User" forcing make_bk_action( 'finish_force_using_this_user' ); //Reactivate data for "regular user make_bk_action( 'check_multiuser_params_for_client_side_by_user_id', $user_id ); $is_force_again = true; } } // ------------------------------------------------------------------------------------------------- wpbc_auto_cancel_booking( $booking_id ); if ( $is_force_again ) { //FixIn: 9.9.0.43 make_bk_action( 'make_force_using_this_user', - 999 ); // '-999' - This ID "by default" is the ID of super booking admin user } } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == REGION CITIES LIST - Support functions Timezone in .ICS & Google Calendar == " > /** * Get list of cities for timezone usage in format like: $city["Europe"]["Kiev"] = "Kiev"; * * @return array|array[] */ function wpbc_get_booking_region_cities_list(){ //FixIn: 8.9.4.9 $city = array( 'Africa' => array() , 'America' => array() , 'Antarctica' => array() , 'Arctic' => array() , 'Asia' => array() , 'Atlantic' => array() , 'Australia' => array() , 'Europe' => array() , 'Indian' => array() , 'Pacific' => array() ); $city["Africa"]["Abidjan"] = "Abidjan"; $city["Africa"]["Accra"] = "Accra"; $city["Africa"]["Addis_Ababa"] = "Addis Ababa"; $city["Africa"]["Algiers"] = "Algiers"; $city["Africa"]["Asmara"] = "Asmara"; $city["Africa"]["Bamako"] = "Bamako"; $city["Africa"]["Bangui"] = "Bangui"; $city["Africa"]["Banjul"] = "Banjul"; $city["Africa"]["Bissau"] = "Bissau"; $city["Africa"]["Blantyre"] = "Blantyre"; $city["Africa"]["Brazzaville"] = "Brazzaville"; $city["Africa"]["Bujumbura"] = "Bujumbura"; $city["Africa"]["Cairo"] = "Cairo"; $city["Africa"]["Casablanca"] = "Casablanca"; $city["Africa"]["Ceuta"] = "Ceuta"; $city["Africa"]["Conakry"] = "Conakry"; $city["Africa"]["Dakar"] = "Dakar"; $city["Africa"]["Dar_es_Salaam"] = "Dar es Salaam"; $city["Africa"]["Djibouti"] = "Djibouti"; $city["Africa"]["Douala"] = "Douala"; $city["Africa"]["El_Aaiun"] = "El Aaiun"; $city["Africa"]["Freetown"] = "Freetown"; $city["Africa"]["Gaborone"] = "Gaborone"; $city["Africa"]["Harare"] = "Harare"; $city["Africa"]["Johannesburg"] = "Johannesburg"; $city["Africa"]["Kampala"] = "Kampala"; $city["Africa"]["Khartoum"] = "Khartoum"; $city["Africa"]["Kigali"] = "Kigali"; $city["Africa"]["Kinshasa"] = "Kinshasa"; $city["Africa"]["Lagos"] = "Lagos"; $city["Africa"]["Libreville"] = "Libreville"; $city["Africa"]["Lome"] = "Lome"; $city["Africa"]["Luanda"] = "Luanda"; $city["Africa"]["Lubumbashi"] = "Lubumbashi"; $city["Africa"]["Lusaka"] = "Lusaka"; $city["Africa"]["Malabo"] = "Malabo"; $city["Africa"]["Maputo"] = "Maputo"; $city["Africa"]["Maseru"] = "Maseru"; $city["Africa"]["Mbabane"] = "Mbabane"; $city["Africa"]["Mogadishu"] = "Mogadishu"; $city["Africa"]["Monrovia"] = "Monrovia"; $city["Africa"]["Nairobi"] = "Nairobi"; $city["Africa"]["Ndjamena"] = "Ndjamena"; $city["Africa"]["Niamey"] = "Niamey"; $city["Africa"]["Nouakchott"] = "Nouakchott"; $city["Africa"]["Ouagadougou"] = "Ouagadougou"; $city["Africa"]["Porto-Novo"] = "Porto-Novo"; $city["Africa"]["Sao_Tome"] = "Sao Tome"; $city["Africa"]["Tripoli"] = "Tripoli"; $city["Africa"]["Tunis"] = "Tunis"; $city["Africa"]["Windhoek"] = "Windhoek"; $city["America"]["Adak"] = "Adak"; $city["America"]["Anchorage"] = "Anchorage"; $city["America"]["Anguilla"] = "Anguilla"; $city["America"]["Antigua"] = "Antigua"; $city["America"]["Araguaina"] = "Araguaina"; $city["America"]["Argentina/Buenos_Aires"] = "Argentina - Buenos Aires"; $city["America"]["Argentina/Catamarca"] = "Argentina - Catamarca"; $city["America"]["Argentina/Cordoba"] = "Argentina - Cordoba"; $city["America"]["Argentina/Jujuy"] = "Argentina - Jujuy"; $city["America"]["Argentina/La_Rioja"] = "Argentina - La Rioja"; $city["America"]["Argentina/Mendoza"] = "Argentina - Mendoza"; $city["America"]["Argentina/Rio_Gallegos"] = "Argentina - Rio Gallegos"; $city["America"]["Argentina/Salta"] = "Argentina - Salta"; $city["America"]["Argentina/San_Juan"] = "Argentina - San Juan"; $city["America"]["Argentina/San_Luis"] = "Argentina - San Luis"; $city["America"]["Argentina/Tucuman"] = "Argentina - Tucuman"; $city["America"]["Argentina/Ushuaia"] = "Argentina - Ushuaia"; $city["America"]["Aruba"] = "Aruba"; $city["America"]["Asuncion"] = "Asuncion"; $city["America"]["Atikokan"] = "Atikokan"; $city["America"]["Bahia"] = "Bahia"; $city["America"]["Barbados"] = "Barbados"; $city["America"]["Belem"] = "Belem"; $city["America"]["Belize"] = "Belize"; $city["America"]["Blanc-Sablon"] = "Blanc-Sablon"; $city["America"]["Boa_Vista"] = "Boa Vista"; $city["America"]["Bogota"] = "Bogota"; $city["America"]["Boise"] = "Boise"; $city["America"]["Cambridge_Bay"] = "Cambridge Bay"; $city["America"]["Campo_Grande"] = "Campo Grande"; $city["America"]["Cancun"] = "Cancun"; $city["America"]["Caracas"] = "Caracas"; $city["America"]["Cayenne"] = "Cayenne"; $city["America"]["Cayman"] = "Cayman"; $city["America"]["Chicago"] = "Chicago"; $city["America"]["Chihuahua"] = "Chihuahua"; $city["America"]["Costa_Rica"] = "Costa Rica"; $city["America"]["Cuiaba"] = "Cuiaba"; $city["America"]["Curacao"] = "Curacao"; $city["America"]["Danmarkshavn"] = "Danmarkshavn"; $city["America"]["Dawson"] = "Dawson"; $city["America"]["Dawson_Creek"] = "Dawson Creek"; $city["America"]["Denver"] = "Denver"; $city["America"]["Detroit"] = "Detroit"; $city["America"]["Dominica"] = "Dominica"; $city["America"]["Edmonton"] = "Edmonton"; $city["America"]["Eirunepe"] = "Eirunepe"; $city["America"]["El_Salvador"] = "El Salvador"; $city["America"]["Fortaleza"] = "Fortaleza"; $city["America"]["Glace_Bay"] = "Glace Bay"; $city["America"]["Godthab"] = "Godthab"; $city["America"]["Goose_Bay"] = "Goose Bay"; $city["America"]["Grand_Turk"] = "Grand Turk"; $city["America"]["Grenada"] = "Grenada"; $city["America"]["Guadeloupe"] = "Guadeloupe"; $city["America"]["Guatemala"] = "Guatemala"; $city["America"]["Guayaquil"] = "Guayaquil"; $city["America"]["Guyana"] = "Guyana"; $city["America"]["Halifax"] = "Halifax"; $city["America"]["Havana"] = "Havana"; $city["America"]["Hermosillo"] = "Hermosillo"; $city["America"]["Indiana/Indianapolis"] = "Indiana - Indianapolis"; $city["America"]["Indiana/Knox"] = "Indiana - Knox"; $city["America"]["Indiana/Marengo"] = "Indiana - Marengo"; $city["America"]["Indiana/Petersburg"] = "Indiana - Petersburg"; $city["America"]["Indiana/Tell_City"] = "Indiana - Tell City"; $city["America"]["Indiana/Vevay"] = "Indiana - Vevay"; $city["America"]["Indiana/Vincennes"] = "Indiana - Vincennes"; $city["America"]["Indiana/Winamac"] = "Indiana - Winamac"; $city["America"]["Inuvik"] = "Inuvik"; $city["America"]["Iqaluit"] = "Iqaluit"; $city["America"]["Jamaica"] = "Jamaica"; $city["America"]["Juneau"] = "Juneau"; $city["America"]["Kentucky/Louisville"] = "Kentucky - Louisville"; $city["America"]["Kentucky/Monticello"] = "Kentucky - Monticello"; $city["America"]["La_Paz"] = "La Paz"; $city["America"]["Lima"] = "Lima"; $city["America"]["Los_Angeles"] = "Los Angeles"; $city["America"]["Maceio"] = "Maceio"; $city["America"]["Managua"] = "Managua"; $city["America"]["Manaus"] = "Manaus"; $city["America"]["Marigot"] = "Marigot"; $city["America"]["Martinique"] = "Martinique"; $city["America"]["Mazatlan"] = "Mazatlan"; $city["America"]["Menominee"] = "Menominee"; $city["America"]["Merida"] = "Merida"; $city["America"]["Mexico_City"] = "Mexico City"; $city["America"]["Miquelon"] = "Miquelon"; $city["America"]["Moncton"] = "Moncton"; $city["America"]["Monterrey"] = "Monterrey"; $city["America"]["Montevideo"] = "Montevideo"; $city["America"]["Montreal"] = "Montreal"; $city["America"]["Montserrat"] = "Montserrat"; $city["America"]["Nassau"] = "Nassau"; $city["America"]["New_York"] = "New York"; $city["America"]["Nipigon"] = "Nipigon"; $city["America"]["Nome"] = "Nome"; $city["America"]["Noronha"] = "Noronha"; $city["America"]["North_Dakota/Center"] = "North Dakota - Center"; $city["America"]["North_Dakota/New_Salem"] = "North Dakota - New Salem"; $city["America"]["Panama"] = "Panama"; $city["America"]["Pangnirtung"] = "Pangnirtung"; $city["America"]["Paramaribo"] = "Paramaribo"; $city["America"]["Phoenix"] = "Phoenix"; $city["America"]["Port-au-Prince"] = "Port-au-Prince"; $city["America"]["Port_of_Spain"] = "Port of Spain"; $city["America"]["Porto_Velho"] = "Porto Velho"; $city["America"]["Puerto_Rico"] = "Puerto Rico"; $city["America"]["Rainy_River"] = "Rainy River"; $city["America"]["Rankin_Inlet"] = "Rankin Inlet"; $city["America"]["Recife"] = "Recife"; $city["America"]["Regina"] = "Regina"; $city["America"]["Resolute"] = "Resolute"; $city["America"]["Rio_Branco"] = "Rio Branco"; $city["America"]["Santarem"] = "Santarem"; $city["America"]["Santiago"] = "Santiago"; $city["America"]["Santo_Domingo"] = "Santo Domingo"; $city["America"]["Sao_Paulo"] = "Sao Paulo"; $city["America"]["Scoresbysund"] = "Scoresbysund"; $city["America"]["Shiprock"] = "Shiprock"; $city["America"]["St_Barthelemy"] = "St Barthelemy"; $city["America"]["St_Johns"] = "St Johns"; $city["America"]["St_Kitts"] = "St Kitts"; $city["America"]["St_Lucia"] = "St Lucia"; $city["America"]["St_Thomas"] = "St Thomas"; $city["America"]["St_Vincent"] = "St Vincent"; $city["America"]["Swift_Current"] = "Swift Current"; $city["America"]["Tegucigalpa"] = "Tegucigalpa"; $city["America"]["Thule"] = "Thule"; $city["America"]["Thunder_Bay"] = "Thunder Bay"; $city["America"]["Tijuana"] = "Tijuana"; $city["America"]["Toronto"] = "Toronto"; $city["America"]["Tortola"] = "Tortola"; $city["America"]["Vancouver"] = "Vancouver"; $city["America"]["Whitehorse"] = "Whitehorse"; $city["America"]["Winnipeg"] = "Winnipeg"; $city["America"]["Yakutat"] = "Yakutat"; $city["America"]["Yellowknife"] = "Yellowknife"; $city["Antarctica"]["Casey"] = "Casey"; $city["Antarctica"]["Davis"] = "Davis"; $city["Antarctica"]["DumontDUrville"] = "DumontDUrville"; $city["Antarctica"]["Mawson"] = "Mawson"; $city["Antarctica"]["McMurdo"] = "McMurdo"; $city["Antarctica"]["Palmer"] = "Palmer"; $city["Antarctica"]["Rothera"] = "Rothera"; $city["Antarctica"]["South_Pole"] = "South Pole"; $city["Antarctica"]["Syowa"] = "Syowa"; $city["Antarctica"]["Vostok"] = "Vostok"; $city["Arctic"]["Longyearbyen"] = "Longyearbyen"; $city["Asia"]["Aden"] = "Aden"; $city["Asia"]["Almaty"] = "Almaty"; $city["Asia"]["Amman"] = "Amman"; $city["Asia"]["Anadyr"] = "Anadyr"; $city["Asia"]["Aqtau"] = "Aqtau"; $city["Asia"]["Aqtobe"] = "Aqtobe"; $city["Asia"]["Ashgabat"] = "Ashgabat"; $city["Asia"]["Baghdad"] = "Baghdad"; $city["Asia"]["Bahrain"] = "Bahrain"; $city["Asia"]["Baku"] = "Baku"; $city["Asia"]["Bangkok"] = "Bangkok"; $city["Asia"]["Beirut"] = "Beirut"; $city["Asia"]["Bishkek"] = "Bishkek"; $city["Asia"]["Brunei"] = "Brunei"; $city["Asia"]["Choibalsan"] = "Choibalsan"; $city["Asia"]["Chongqing"] = "Chongqing"; $city["Asia"]["Colombo"] = "Colombo"; $city["Asia"]["Damascus"] = "Damascus"; $city["Asia"]["Dhaka"] = "Dhaka"; $city["Asia"]["Dili"] = "Dili"; $city["Asia"]["Dubai"] = "Dubai"; $city["Asia"]["Dushanbe"] = "Dushanbe"; $city["Asia"]["Gaza"] = "Gaza"; $city["Asia"]["Harbin"] = "Harbin"; $city["Asia"]["Ho_Chi_Minh"] = "Ho Chi Minh"; $city["Asia"]["Hong_Kong"] = "Hong Kong"; $city["Asia"]["Hovd"] = "Hovd"; $city["Asia"]["Irkutsk"] = "Irkutsk"; $city["Asia"]["Jakarta"] = "Jakarta"; $city["Asia"]["Jayapura"] = "Jayapura"; $city["Asia"]["Jerusalem"] = "Jerusalem"; $city["Asia"]["Kabul"] = "Kabul"; $city["Asia"]["Kamchatka"] = "Kamchatka"; $city["Asia"]["Karachi"] = "Karachi"; $city["Asia"]["Kashgar"] = "Kashgar"; $city["Asia"]["Kathmandu"] = "Kathmandu"; $city["Asia"]["Kolkata"] = "Kolkata"; $city["Asia"]["Krasnoyarsk"] = "Krasnoyarsk"; $city["Asia"]["Kuala_Lumpur"] = "Kuala Lumpur"; $city["Asia"]["Kuching"] = "Kuching"; $city["Asia"]["Kuwait"] = "Kuwait"; $city["Asia"]["Macau"] = "Macau"; $city["Asia"]["Magadan"] = "Magadan"; $city["Asia"]["Makassar"] = "Makassar"; $city["Asia"]["Manila"] = "Manila"; $city["Asia"]["Muscat"] = "Muscat"; $city["Asia"]["Nicosia"] = "Nicosia"; $city["Asia"]["Novosibirsk"] = "Novosibirsk"; $city["Asia"]["Omsk"] = "Omsk"; $city["Asia"]["Oral"] = "Oral"; $city["Asia"]["Phnom_Penh"] = "Phnom Penh"; $city["Asia"]["Pontianak"] = "Pontianak"; $city["Asia"]["Pyongyang"] = "Pyongyang"; $city["Asia"]["Qatar"] = "Qatar"; $city["Asia"]["Qyzylorda"] = "Qyzylorda"; $city["Asia"]["Rangoon"] = "Rangoon"; $city["Asia"]["Riyadh"] = "Riyadh"; $city["Asia"]["Sakhalin"] = "Sakhalin"; $city["Asia"]["Samarkand"] = "Samarkand"; $city["Asia"]["Seoul"] = "Seoul"; $city["Asia"]["Shanghai"] = "Shanghai"; $city["Asia"]["Singapore"] = "Singapore"; $city["Asia"]["Taipei"] = "Taipei"; $city["Asia"]["Tashkent"] = "Tashkent"; $city["Asia"]["Tbilisi"] = "Tbilisi"; $city["Asia"]["Tehran"] = "Tehran"; $city["Asia"]["Thimphu"] = "Thimphu"; $city["Asia"]["Tokyo"] = "Tokyo"; $city["Asia"]["Ulaanbaatar"] = "Ulaanbaatar"; $city["Asia"]["Urumqi"] = "Urumqi"; $city["Asia"]["Vientiane"] = "Vientiane"; $city["Asia"]["Vladivostok"] = "Vladivostok"; $city["Asia"]["Yakutsk"] = "Yakutsk"; $city["Asia"]["Yekaterinburg"] = "Yekaterinburg"; $city["Asia"]["Yerevan"] = "Yerevan"; $city["Atlantic"]["Azores"] = "Azores"; $city["Atlantic"]["Bermuda"] = "Bermuda"; $city["Atlantic"]["Canary"] = "Canary"; $city["Atlantic"]["Cape_Verde"] = "Cape Verde"; $city["Atlantic"]["Faroe"] = "Faroe"; $city["Atlantic"]["Madeira"] = "Madeira"; $city["Atlantic"]["Reykjavik"] = "Reykjavik"; $city["Atlantic"]["South_Georgia"] = "South Georgia"; $city["Atlantic"]["Stanley"] = "Stanley"; $city["Atlantic"]["St_Helena"] = "St Helena"; $city["Australia"]["Adelaide"] = "Adelaide"; $city["Australia"]["Brisbane"] = "Brisbane"; $city["Australia"]["Broken_Hill"] = "Broken Hill"; $city["Australia"]["Currie"] = "Currie"; $city["Australia"]["Darwin"] = "Darwin"; $city["Australia"]["Eucla"] = "Eucla"; $city["Australia"]["Hobart"] = "Hobart"; $city["Australia"]["Lindeman"] = "Lindeman"; $city["Australia"]["Lord_Howe"] = "Lord Howe"; $city["Australia"]["Melbourne"] = "Melbourne"; $city["Australia"]["Perth"] = "Perth"; $city["Australia"]["Sydney"] = "Sydney"; $city["Europe"]["Amsterdam"] = "Amsterdam"; $city["Europe"]["Andorra"] = "Andorra"; $city["Europe"]["Athens"] = "Athens"; $city["Europe"]["Belgrade"] = "Belgrade"; $city["Europe"]["Berlin"] = "Berlin"; $city["Europe"]["Bratislava"] = "Bratislava"; $city["Europe"]["Brussels"] = "Brussels"; $city["Europe"]["Bucharest"] = "Bucharest"; $city["Europe"]["Budapest"] = "Budapest"; $city["Europe"]["Chisinau"] = "Chisinau"; $city["Europe"]["Copenhagen"] = "Copenhagen"; $city["Europe"]["Dublin"] = "Dublin"; $city["Europe"]["Gibraltar"] = "Gibraltar"; $city["Europe"]["Guernsey"] = "Guernsey"; $city["Europe"]["Helsinki"] = "Helsinki"; $city["Europe"]["Isle_of_Man"] = "Isle of Man"; $city["Europe"]["Istanbul"] = "Istanbul"; $city["Europe"]["Jersey"] = "Jersey"; $city["Europe"]["Kaliningrad"] = "Kaliningrad"; $city["Europe"]["Kiev"] = "Kiev"; $city["Europe"]["Lisbon"] = "Lisbon"; $city["Europe"]["Ljubljana"] = "Ljubljana"; $city["Europe"]["London"] = "London"; $city["Europe"]["Luxembourg"] = "Luxembourg"; $city["Europe"]["Madrid"] = "Madrid"; $city["Europe"]["Malta"] = "Malta"; $city["Europe"]["Mariehamn"] = "Mariehamn"; $city["Europe"]["Minsk"] = "Minsk"; $city["Europe"]["Monaco"] = "Monaco"; $city["Europe"]["Moscow"] = "Moscow"; $city["Europe"]["Oslo"] = "Oslo"; $city["Europe"]["Paris"] = "Paris"; $city["Europe"]["Podgorica"] = "Podgorica"; $city["Europe"]["Prague"] = "Prague"; $city["Europe"]["Riga"] = "Riga"; $city["Europe"]["Rome"] = "Rome"; $city["Europe"]["Samara"] = "Samara"; $city["Europe"]["San_Marino"] = "San Marino"; $city["Europe"]["Sarajevo"] = "Sarajevo"; $city["Europe"]["Simferopol"] = "Simferopol"; $city["Europe"]["Skopje"] = "Skopje"; $city["Europe"]["Sofia"] = "Sofia"; $city["Europe"]["Stockholm"] = "Stockholm"; $city["Europe"]["Tallinn"] = "Tallinn"; $city["Europe"]["Tirane"] = "Tirane"; $city["Europe"]["Uzhgorod"] = "Uzhgorod"; $city["Europe"]["Vaduz"] = "Vaduz"; $city["Europe"]["Vatican"] = "Vatican"; $city["Europe"]["Vienna"] = "Vienna"; $city["Europe"]["Vilnius"] = "Vilnius"; $city["Europe"]["Volgograd"] = "Volgograd"; $city["Europe"]["Warsaw"] = "Warsaw"; $city["Europe"]["Zagreb"] = "Zagreb"; $city["Europe"]["Zaporozhye"] = "Zaporozhye"; $city["Europe"]["Zurich"] = "Zurich"; $city["Indian"]["Antananarivo"] = "Antananarivo"; $city["Indian"]["Chagos"] = "Chagos"; $city["Indian"]["Christmas"] = "Christmas"; $city["Indian"]["Cocos"] = "Cocos"; $city["Indian"]["Comoro"] = "Comoro"; $city["Indian"]["Kerguelen"] = "Kerguelen"; $city["Indian"]["Mahe"] = "Mahe"; $city["Indian"]["Maldives"] = "Maldives"; $city["Indian"]["Mauritius"] = "Mauritius"; $city["Indian"]["Mayotte"] = "Mayotte"; $city["Indian"]["Reunion"] = "Reunion"; $city["Pacific"]["Apia"] = "Apia"; $city["Pacific"]["Auckland"] = "Auckland"; $city["Pacific"]["Chatham"] = "Chatham"; $city["Pacific"]["Easter"] = "Easter"; $city["Pacific"]["Efate"] = "Efate"; $city["Pacific"]["Enderbury"] = "Enderbury"; $city["Pacific"]["Fakaofo"] = "Fakaofo"; $city["Pacific"]["Fiji"] = "Fiji"; $city["Pacific"]["Funafuti"] = "Funafuti"; $city["Pacific"]["Galapagos"] = "Galapagos"; $city["Pacific"]["Gambier"] = "Gambier"; $city["Pacific"]["Guadalcanal"] = "Guadalcanal"; $city["Pacific"]["Guam"] = "Guam"; $city["Pacific"]["Honolulu"] = "Honolulu"; $city["Pacific"]["Johnston"] = "Johnston"; $city["Pacific"]["Kiritimati"] = "Kiritimati"; $city["Pacific"]["Kosrae"] = "Kosrae"; $city["Pacific"]["Kwajalein"] = "Kwajalein"; $city["Pacific"]["Majuro"] = "Majuro"; $city["Pacific"]["Marquesas"] = "Marquesas"; $city["Pacific"]["Midway"] = "Midway"; $city["Pacific"]["Nauru"] = "Nauru"; $city["Pacific"]["Niue"] = "Niue"; $city["Pacific"]["Norfolk"] = "Norfolk"; $city["Pacific"]["Noumea"] = "Noumea"; $city["Pacific"]["Pago_Pago"] = "Pago Pago"; $city["Pacific"]["Palau"] = "Palau"; $city["Pacific"]["Pitcairn"] = "Pitcairn"; $city["Pacific"]["Ponape"] = "Ponape"; $city["Pacific"]["Port_Moresby"] = "Port Moresby"; $city["Pacific"]["Rarotonga"] = "Rarotonga"; $city["Pacific"]["Saipan"] = "Saipan"; $city["Pacific"]["Tahiti"] = "Tahiti"; $city["Pacific"]["Tarawa"] = "Tarawa"; $city["Pacific"]["Tongatapu"] = "Tongatapu"; $city["Pacific"]["Truk"] = "Truk"; $city["Pacific"]["Wake"] = "Wake"; $city["Pacific"]["Wallis"] = "Wallis"; return $city; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == Logs for Notes / remarks == " > /** * Add Log info to Notes of bookings * * @param array | int $booking_id_arr * @param string $message */ function wpbc_db__add_log_info( $booking_id_arr, $message ) { if ( get_bk_option( 'booking_log_booking_actions' ) !== 'On' ) { return; } $booking_id_arr = (array) $booking_id_arr; $is_append = true; foreach ( $booking_id_arr as $booking_id ) { $date_time = date_i18n( '[Y-m-d H:i] ' ); make_bk_action('wpdev_make_update_of_remark' , $booking_id , $date_time . $message , $is_append ); //FixIn: 9.1.2.14 } } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == CSV export support function == " > /** * Get Path from request URL. E.g. 'my-category/file.csv' from url, like https://server.com/my-category/file.csv * * It can detect internal sub folder or WordPress, like http://server.com/my-website/ * * @return false|string */ function wpbc_get_request_url_path(){ if ( function_exists( 'wp_parse_url' ) ) { $my_parsed_url = wp_parse_url( $_SERVER['REQUEST_URI'] ); } else { $my_parsed_url = @parse_url( $_SERVER['REQUEST_URI'] ); } if ( false === $my_parsed_url ) { // seriously malformed URLs, parse_url() may return FALSE. return false; } $my_parsed_url_path = trim( $my_parsed_url['path'] ); $my_parsed_url_path = trim( $my_parsed_url_path, '/' ); // Check internal sub folder of WP, like http://server.com/my-website/[ ... LINK ...] //FixIn: 2.0.5.4 if ( function_exists( 'wp_parse_url' ) ) { $wp_home_server_url = wp_parse_url( home_url() ); } else { $wp_home_server_url = @parse_url( home_url() ); } if ( ( false !== $wp_home_server_url ) && ( ! empty( $wp_home_server_url['path'] ) ) ) { // seriously malformed URLs, parse_url() may return FALSE. $server_url_sufix = trim( $wp_home_server_url[ 'path' ] ); // [path] => /my-website $server_url_sufix = trim( $server_url_sufix, '/' ); // my-website if ( ! empty( $server_url_sufix ) ) { $check_sufix = substr( $my_parsed_url_path, 0, strlen( $server_url_sufix ) ); if ( $check_sufix === $server_url_sufix ) { $my_parsed_url_path = substr( $my_parsed_url_path, strlen( $server_url_sufix ) ); $my_parsed_url_path = trim( $my_parsed_url_path, '/' ); } } } //End FixIn: 2.0.5.4 return $my_parsed_url_path; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == SKIP BLANK EMAIL == " > /** * Check if this email is NOT blank email to SKIP email sending -- from autofill fast booking creation at Booking > Add booking page. * * Currently, plugin use 'blank@wpbookingmanager.com' for all blank bookings, previously it was used 'admin@blank.com' * * @param $email_address * @param $email_content * * @return bool */ function wpbc_is_not_blank_email( $email_address, $email_content ) { // Previously: if ( ( strpos( $to, '@blank.com' ) === false ) && ( strpos( $replace['content'], 'admin@blank.com' ) === false ) ) { ->send(...) } if ( ( false === strpos( $email_address, '@blank.com' ) ) && ( false === strpos( $email_content, 'admin@blank.com' ) ) && ( false === strpos( $email_address, '@wpbookingmanager.com' ) ) && ( false === strpos( $email_content, 'blank@wpbookingmanager.com' ) ) ) { return true; } return false; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" == PHP PERFORMANCE == " > /** * Start calculation of time for execution of specific part of CODE * * @param string $name name of section * @param array $php_performance * * @return array * * Example: * * $php_performance = php_performance_START( 'emails_sending' , $php_performance ); * * ... some code here ... * * $php_performance = php_performance_END( 'emails_sending' , $php_performance ); * * echo 'Time of execution: ' . $php_performancep['emails_sending'] * */ function php_performance_START( $name, $php_performance ) { if ( empty( $php_performance ) ) { $php_performance = array(); } $php_performance[ $name ] = microtime( true ); return $php_performance; } /** * End calculation of time for execution of specific part of CODE * * @param string $name name of section * @param array $php_performance * * @return array * * Example: * * $php_performance = php_performance_START( 'emails_sending' , $php_performance ); * * ... some code here ... * * $php_performance = php_performance_END( 'emails_sending' , $php_performance ); * * echo 'Time of execution: ' . $php_performancep['emails_sending'] * */ function php_performance_END( $name, $php_performance ) { if ( empty( $php_performance ) ) { $php_performance = array(); } if ( empty( $php_performance[ $name ] ) ) { $php_performance[ $name ] = microtime( true ); } $php_performance[ $name ] = microtime( true ) - $php_performance[ $name ]; return $php_performance; } /** * Set maximum number of seconds for execution. * * @param $limit_in_seconds_int * * @return void */ function wpbc_set_limit_php( $limit_in_seconds_int = 300 ){ $is_win = ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) ); // Windows does not have support for this timeout function if ( ! $is_win ) { $max = (int) ini_get( 'max_execution_time' ); // If unlimited, or if set_time_limit is disabled, then skip. if ( ( 0 !== $max ) && ( $limit_in_seconds_int > $max ) && ( function_exists( 'set_time_limit' ) ) && ( false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) ) && ( ! ini_get( 'safe_mode' ) ) ) { @set_time_limit( $limit_in_seconds_int ); @ini_set( 'memory_limit', '512M' ); } } } // </editor-fold>